summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Staszak <jstaszak@apple.com>2011-07-06 18:22:43 +0000
committerJakub Staszak <jstaszak@apple.com>2011-07-06 18:22:43 +0000
commit9da9934e27dfb48de77b80a3e20ed2d869b52024 (patch)
tree711368e52d6596eeb3273012758fe07e87213687
parentd7c7e2ff7d3305e4bc5634902ec4b8406e9cefca (diff)
downloadllvm-9da9934e27dfb48de77b80a3e20ed2d869b52024.tar.gz
llvm-9da9934e27dfb48de77b80a3e20ed2d869b52024.tar.bz2
llvm-9da9934e27dfb48de77b80a3e20ed2d869b52024.tar.xz
Introduce "expect" intrinsic instructions.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@134516 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/InitializePasses.h1
-rw-r--r--include/llvm/Intrinsics.td5
-rw-r--r--include/llvm/LLVMContext.h3
-rw-r--r--include/llvm/LinkAllPasses.h1
-rw-r--r--include/llvm/Support/PassManagerBuilder.h1
-rw-r--r--include/llvm/Transforms/Scalar.h8
-rw-r--r--lib/CodeGen/IntrinsicLowering.cpp7
-rw-r--r--lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp7
-rw-r--r--lib/Transforms/Scalar/Scalar.cpp1
-rw-r--r--lib/Transforms/Utils/CMakeLists.txt1
-rw-r--r--lib/Transforms/Utils/LowerExpectIntrinsic.cpp163
-rw-r--r--lib/VMCore/LLVMContext.cpp4
-rw-r--r--test/CodeGen/Generic/builtin-expect.ll223
-rw-r--r--test/Transforms/LowerExpectIntrinsic/basic.ll251
-rw-r--r--test/Transforms/LowerExpectIntrinsic/dg.exp3
15 files changed, 678 insertions, 1 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index dfd924613e..0cd1222999 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -141,6 +141,7 @@ void initializeLoopUnrollPass(PassRegistry&);
void initializeLoopUnswitchPass(PassRegistry&);
void initializeLoopIdiomRecognizePass(PassRegistry&);
void initializeLowerAtomicPass(PassRegistry&);
+void initializeLowerExpectIntrinsicPass(PassRegistry&);
void initializeLowerIntrinsicsPass(PassRegistry&);
void initializeLowerInvokePass(PassRegistry&);
void initializeLowerSetJmpPass(PassRegistry&);
diff --git a/include/llvm/Intrinsics.td b/include/llvm/Intrinsics.td
index 14396244af..c854471036 100644
--- a/include/llvm/Intrinsics.td
+++ b/include/llvm/Intrinsics.td
@@ -266,6 +266,11 @@ def int_objectsize : Intrinsic<[llvm_anyint_ty], [llvm_ptr_ty, llvm_i1_ty],
[IntrNoMem]>,
GCCBuiltin<"__builtin_object_size">;
+//===------------------------- Expect Intrinsics --------------------------===//
+//
+def int_expect : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
+ LLVMMatchType<0>], [IntrNoMem]>;
+
//===-------------------- Bit Manipulation Intrinsics ---------------------===//
//
diff --git a/include/llvm/LLVMContext.h b/include/llvm/LLVMContext.h
index 3502ff73c1..65146c31aa 100644
--- a/include/llvm/LLVMContext.h
+++ b/include/llvm/LLVMContext.h
@@ -39,7 +39,8 @@ public:
// compile-time performance optimization, not a correctness optimization.
enum {
MD_dbg = 0, // "dbg"
- MD_tbaa = 1 // "tbaa"
+ MD_tbaa = 1, // "tbaa"
+ MD_prof = 2 // "prof"
};
/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h
index c2ea8efb9a..a1945486fc 100644
--- a/include/llvm/LinkAllPasses.h
+++ b/include/llvm/LinkAllPasses.h
@@ -92,6 +92,7 @@ namespace {
(void) llvm::createLoopUnswitchPass();
(void) llvm::createLoopIdiomPass();
(void) llvm::createLoopRotatePass();
+ (void) llvm::createLowerExpectIntrinsicPass();
(void) llvm::createLowerInvokePass();
(void) llvm::createLowerSetJmpPass();
(void) llvm::createLowerSwitchPass();
diff --git a/include/llvm/Support/PassManagerBuilder.h b/include/llvm/Support/PassManagerBuilder.h
index 8ac507f8bc..ccb49e7287 100644
--- a/include/llvm/Support/PassManagerBuilder.h
+++ b/include/llvm/Support/PassManagerBuilder.h
@@ -152,6 +152,7 @@ public:
FPM.add(createCFGSimplificationPass());
FPM.add(createScalarReplAggregatesPass());
FPM.add(createEarlyCSEPass());
+ FPM.add(createLowerExpectIntrinsicPass());
}
/// populateModulePassManager - This sets up the primary pass manager.
diff --git a/include/llvm/Transforms/Scalar.h b/include/llvm/Transforms/Scalar.h
index e830435751..2187d4eb76 100644
--- a/include/llvm/Transforms/Scalar.h
+++ b/include/llvm/Transforms/Scalar.h
@@ -361,6 +361,14 @@ Pass *createObjCARCOptPass();
FunctionPass *createInstructionSimplifierPass();
extern char &InstructionSimplifierID;
+
+//===----------------------------------------------------------------------===//
+//
+// LowerExpectIntriniscs - Removes llvm.expect intrinsics and creates
+// "block_weights" metadata.
+FunctionPass *createLowerExpectIntrinsicPass();
+
+
} // End llvm namespace
#endif
diff --git a/lib/CodeGen/IntrinsicLowering.cpp b/lib/CodeGen/IntrinsicLowering.cpp
index 3861ddadf6..b0a823042d 100644
--- a/lib/CodeGen/IntrinsicLowering.cpp
+++ b/lib/CodeGen/IntrinsicLowering.cpp
@@ -353,6 +353,13 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) {
report_fatal_error("Code generator does not support intrinsic function '"+
Callee->getName()+"'!");
+ case Intrinsic::expect: {
+ // Just replace __builtin_expect(exp, c) with EXP.
+ Value *V = CI->getArgOperand(0);
+ CI->replaceAllUsesWith(V);
+ break;
+ }
+
// The setjmp/longjmp intrinsics should only exist in the code if it was
// never optimized (ie, right out of the CFE), or if it has been hacked on
// by the lowerinvoke pass. In both cases, the right thing to do is to
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index ea59ca14d8..fc8f0d2996 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -4771,6 +4771,13 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
case Intrinsic::flt_rounds:
setValue(&I, DAG.getNode(ISD::FLT_ROUNDS_, dl, MVT::i32));
return 0;
+
+ case Intrinsic::expect: {
+ // Just replace __builtin_expect(exp, c) with EXP.
+ setValue(&I, getValue(I.getArgOperand(0)));
+ return 0;
+ }
+
case Intrinsic::trap: {
StringRef TrapFuncName = getTrapFunctionName();
if (TrapFuncName.empty()) {
diff --git a/lib/Transforms/Scalar/Scalar.cpp b/lib/Transforms/Scalar/Scalar.cpp
index 158d65352f..302c287d3c 100644
--- a/lib/Transforms/Scalar/Scalar.cpp
+++ b/lib/Transforms/Scalar/Scalar.cpp
@@ -48,6 +48,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) {
initializeLoopUnswitchPass(Registry);
initializeLoopIdiomRecognizePass(Registry);
initializeLowerAtomicPass(Registry);
+ initializeLowerExpectIntrinsicPass(Registry);
initializeMemCpyOptPass(Registry);
initializeObjCARCAliasAnalysisPass(Registry);
initializeObjCARCExpandPass(Registry);
diff --git a/lib/Transforms/Utils/CMakeLists.txt b/lib/Transforms/Utils/CMakeLists.txt
index 2df534f232..204c2c63e1 100644
--- a/lib/Transforms/Utils/CMakeLists.txt
+++ b/lib/Transforms/Utils/CMakeLists.txt
@@ -14,6 +14,7 @@ add_llvm_library(LLVMTransformUtils
Local.cpp
LoopSimplify.cpp
LoopUnroll.cpp
+ LowerExpectIntrinsic.cpp
LowerInvoke.cpp
LowerSwitch.cpp
Mem2Reg.cpp
diff --git a/lib/Transforms/Utils/LowerExpectIntrinsic.cpp b/lib/Transforms/Utils/LowerExpectIntrinsic.cpp
new file mode 100644
index 0000000000..fd94c84413
--- /dev/null
+++ b/lib/Transforms/Utils/LowerExpectIntrinsic.cpp
@@ -0,0 +1,163 @@
+#define DEBUG_TYPE "lower-expect-intrinsic"
+#include "llvm/Constants.h"
+#include "llvm/Function.h"
+#include "llvm/BasicBlock.h"
+#include "llvm/LLVMContext.h"
+#include "llvm/Instructions.h"
+#include "llvm/Intrinsics.h"
+#include "llvm/Metadata.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/ADT/Statistic.h"
+#include <vector>
+
+using namespace llvm;
+
+STATISTIC(IfHandled, "Number of 'expect' intrinsic intructions handled");
+
+static cl::opt<uint32_t>
+LikelyBranchWeight("likely-branch-weight", cl::Hidden, cl::init(64),
+ cl::desc("Weight of the branch likely to be taken (default = 64)"));
+static cl::opt<uint32_t>
+UnlikelyBranchWeight("unlikely-branch-weight", cl::Hidden, cl::init(4),
+ cl::desc("Weight of the branch unlikely to be taken (default = 4)"));
+
+namespace {
+
+ class LowerExpectIntrinsic : public FunctionPass {
+
+ bool HandleSwitchExpect(SwitchInst *SI);
+
+ bool HandleIfExpect(BranchInst *BI);
+
+ public:
+ static char ID;
+ LowerExpectIntrinsic() : FunctionPass(ID) {
+ initializeLowerExpectIntrinsicPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnFunction(Function &F);
+ };
+}
+
+
+bool LowerExpectIntrinsic::HandleSwitchExpect(SwitchInst *SI) {
+ CallInst *CI = dyn_cast<CallInst>(SI->getCondition());
+ if (!CI)
+ return false;
+
+ Function *Fn = CI->getCalledFunction();
+ if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
+ return false;
+
+ Value *ArgValue = CI->getArgOperand(0);
+ ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
+ if (!ExpectedValue)
+ return false;
+
+ LLVMContext &Context = CI->getContext();
+ const Type *Int32Ty = Type::getInt32Ty(Context);
+
+ unsigned caseNo = SI->findCaseValue(ExpectedValue);
+ std::vector<Value *> Vec;
+ unsigned n = SI->getNumCases();
+ Vec.resize(n + 1); // +1 for MDString
+
+ Vec[0] = MDString::get(Context, "branch_weights");
+ for (unsigned i = 0; i < n; ++i) {
+ Vec[i + 1] = ConstantInt::get(Int32Ty, i == caseNo ? LikelyBranchWeight : UnlikelyBranchWeight);
+ }
+
+ MDNode *WeightsNode = llvm::MDNode::get(Context, Vec);
+ SI->setMetadata(LLVMContext::MD_prof, WeightsNode);
+
+ SI->setCondition(ArgValue);
+ return true;
+}
+
+
+bool LowerExpectIntrinsic::HandleIfExpect(BranchInst *BI) {
+ if (BI->isUnconditional())
+ return false;
+
+ // Handle non-optimized IR code like:
+ // %expval = call i64 @llvm.expect.i64.i64(i64 %conv1, i64 1)
+ // %tobool = icmp ne i64 %expval, 0
+ // br i1 %tobool, label %if.then, label %if.end
+
+ ICmpInst *CmpI = dyn_cast<ICmpInst>(BI->getCondition());
+ if (!CmpI || CmpI->getPredicate() != CmpInst::ICMP_NE)
+ return false;
+
+ CallInst *CI = dyn_cast<CallInst>(CmpI->getOperand(0));
+ if (!CI)
+ return false;
+
+ Function *Fn = CI->getCalledFunction();
+ if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
+ return false;
+
+ Value *ArgValue = CI->getArgOperand(0);
+ ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
+ if (!ExpectedValue)
+ return false;
+
+ LLVMContext &Context = CI->getContext();
+ const Type *Int32Ty = Type::getInt32Ty(Context);
+ bool Likely = ExpectedValue->isOne();
+
+ // If expect value is equal to 1 it means that we are more likely to take
+ // branch 0, in other case more likely is branch 1.
+ Value *Ops[] = {
+ MDString::get(Context, "branch_weights"),
+ ConstantInt::get(Int32Ty, Likely ? LikelyBranchWeight : UnlikelyBranchWeight),
+ ConstantInt::get(Int32Ty, Likely ? UnlikelyBranchWeight : LikelyBranchWeight)
+ };
+
+ MDNode *WeightsNode = MDNode::get(Context, ArrayRef<Value *>(Ops, 3));
+ BI->setMetadata(LLVMContext::MD_prof, WeightsNode);
+
+ CmpI->setOperand(0, ArgValue);
+ return true;
+}
+
+
+bool LowerExpectIntrinsic::runOnFunction(Function &F) {
+ for (Function::iterator I = F.begin(), E = F.end(); I != E;) {
+ BasicBlock *BB = I++;
+
+ // Create "block_weights" metadata.
+ if (BranchInst *BI = dyn_cast<BranchInst>(BB->getTerminator())) {
+ if (HandleIfExpect(BI))
+ IfHandled++;
+ } else if (SwitchInst *SI = dyn_cast<SwitchInst>(BB->getTerminator())) {
+ if (HandleSwitchExpect(SI))
+ IfHandled++;
+ }
+
+ // remove llvm.expect intrinsics.
+ for (BasicBlock::iterator BI = BB->begin(), BE = BB->end();
+ BI != BE; ) {
+ CallInst *CI = dyn_cast<CallInst>(BI++);
+ if (!CI)
+ continue;
+
+ Function *Fn = CI->getCalledFunction();
+ if (Fn && Fn->getIntrinsicID() == Intrinsic::expect)
+ CI->eraseFromParent();
+ }
+ }
+
+ return false;
+}
+
+
+char LowerExpectIntrinsic::ID = 0;
+INITIALIZE_PASS(LowerExpectIntrinsic, "lower-expect", "Lower 'expect' "
+ "Intrinsics", false, false)
+
+FunctionPass *llvm::createLowerExpectIntrinsicPass() {
+ return new LowerExpectIntrinsic();
+}
diff --git a/lib/VMCore/LLVMContext.cpp b/lib/VMCore/LLVMContext.cpp
index 1bd497d05d..ebd1e0aa1b 100644
--- a/lib/VMCore/LLVMContext.cpp
+++ b/lib/VMCore/LLVMContext.cpp
@@ -39,6 +39,10 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) {
// Create the 'tbaa' metadata kind.
unsigned TBAAID = getMDKindID("tbaa");
assert(TBAAID == MD_tbaa && "tbaa kind id drifted"); (void)TBAAID;
+
+ // Create the 'prof' metadata kind.
+ unsigned ProfID = getMDKindID("prof");
+ assert(ProfID == MD_prof && "prof kind id drifted"); (void)ProfID;
}
LLVMContext::~LLVMContext() { delete pImpl; }
diff --git a/test/CodeGen/Generic/builtin-expect.ll b/test/CodeGen/Generic/builtin-expect.ll
new file mode 100644
index 0000000000..e8cd07badf
--- /dev/null
+++ b/test/CodeGen/Generic/builtin-expect.ll
@@ -0,0 +1,223 @@
+; RUN: llc < %s
+
+define i32 @test1(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp sgt i32 %tmp, 1
+ %conv = zext i1 %cmp to i32
+ %conv1 = sext i32 %conv to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 1)
+ %tobool = icmp ne i64 %expval, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+declare i64 @llvm.expect.i64(i64, i64) nounwind readnone
+
+declare i32 @f(...)
+
+define i32 @test2(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool = icmp ne i64 %expval, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test3(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %tobool = icmp ne i32 %tmp, 0
+ %lnot = xor i1 %tobool, true
+ %lnot.ext = zext i1 %lnot to i32
+ %conv = sext i32 %lnot.ext to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool1 = icmp ne i64 %expval, 0
+ br i1 %tobool1, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test4(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %tobool = icmp ne i32 %tmp, 0
+ %lnot = xor i1 %tobool, true
+ %lnot1 = xor i1 %lnot, true
+ %lnot.ext = zext i1 %lnot1 to i32
+ %conv = sext i32 %lnot.ext to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool2 = icmp ne i64 %expval, 0
+ br i1 %tobool2, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test5(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp slt i32 %tmp, 0
+ %conv = zext i1 %cmp to i32
+ %conv1 = sext i32 %conv to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 0)
+ %tobool = icmp ne i64 %expval, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test6(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ switch i64 %expval, label %sw.epilog [
+ i64 1, label %sw.bb
+ i64 2, label %sw.bb
+ ]
+
+sw.bb: ; preds = %entry, %entry
+ store i32 0, i32* %retval
+ br label %return
+
+sw.epilog: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %sw.epilog, %sw.bb
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test7(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ switch i64 %expval, label %sw.epilog [
+ i64 2, label %sw.bb
+ i64 3, label %sw.bb
+ ]
+
+sw.bb: ; preds = %entry, %entry
+ %tmp1 = load i32* %x.addr, align 4
+ store i32 %tmp1, i32* %retval
+ br label %return
+
+sw.epilog: ; preds = %entry
+ store i32 0, i32* %retval
+ br label %return
+
+return: ; preds = %sw.epilog, %sw.bb
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+define i32 @test8(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp sgt i32 %tmp, 1
+ %conv = zext i1 %cmp to i32
+ %expval = call i32 @llvm.expect.i32(i32 %conv, i32 1)
+ %tobool = icmp ne i32 %expval, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+declare i32 @llvm.expect.i32(i32, i32) nounwind readnone
+
diff --git a/test/Transforms/LowerExpectIntrinsic/basic.ll b/test/Transforms/LowerExpectIntrinsic/basic.ll
new file mode 100644
index 0000000000..c00127e1ed
--- /dev/null
+++ b/test/Transforms/LowerExpectIntrinsic/basic.ll
@@ -0,0 +1,251 @@
+; RUN: opt -lower-expect -strip-dead-prototypes -S -o - < %s | FileCheck %s
+
+; CHECK: @test1
+define i32 @test1(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp sgt i32 %tmp, 1
+ %conv = zext i1 %cmp to i32
+ %conv1 = sext i32 %conv to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 1)
+ %tobool = icmp ne i64 %expval, 0
+; CHECK: !prof !0
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+declare i64 @llvm.expect.i64(i64, i64) nounwind readnone
+
+declare i32 @f(...)
+
+; CHECK: @test2
+define i32 @test2(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool = icmp ne i64 %expval, 0
+; CHECK: !prof !0
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test3
+define i32 @test3(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %tobool = icmp ne i32 %tmp, 0
+ %lnot = xor i1 %tobool, true
+ %lnot.ext = zext i1 %lnot to i32
+ %conv = sext i32 %lnot.ext to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool1 = icmp ne i64 %expval, 0
+; CHECK: !prof !0
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool1, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test4
+define i32 @test4(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %tobool = icmp ne i32 %tmp, 0
+ %lnot = xor i1 %tobool, true
+ %lnot1 = xor i1 %lnot, true
+ %lnot.ext = zext i1 %lnot1 to i32
+ %conv = sext i32 %lnot.ext to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+ %tobool2 = icmp ne i64 %expval, 0
+; CHECK: !prof !0
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool2, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test5
+define i32 @test5(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp slt i32 %tmp, 0
+ %conv = zext i1 %cmp to i32
+ %conv1 = sext i32 %conv to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 0)
+ %tobool = icmp ne i64 %expval, 0
+; CHECK: !prof !1
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test6
+define i32 @test6(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+; CHECK: !prof !2
+; CHECK-NOT: @llvm.expect
+ switch i64 %expval, label %sw.epilog [
+ i64 1, label %sw.bb
+ i64 2, label %sw.bb
+ ]
+
+sw.bb: ; preds = %entry, %entry
+ store i32 0, i32* %retval
+ br label %return
+
+sw.epilog: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %sw.epilog, %sw.bb
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test7
+define i32 @test7(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %conv = sext i32 %tmp to i64
+ %expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
+; CHECK: !prof !3
+; CHECK-NOT: @llvm.expect
+ switch i64 %expval, label %sw.epilog [
+ i64 2, label %sw.bb
+ i64 3, label %sw.bb
+ ]
+
+sw.bb: ; preds = %entry, %entry
+ %tmp1 = load i32* %x.addr, align 4
+ store i32 %tmp1, i32* %retval
+ br label %return
+
+sw.epilog: ; preds = %entry
+ store i32 0, i32* %retval
+ br label %return
+
+return: ; preds = %sw.epilog, %sw.bb
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+; CHECK: @test8
+define i32 @test8(i32 %x) nounwind uwtable ssp {
+entry:
+ %retval = alloca i32, align 4
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ %tmp = load i32* %x.addr, align 4
+ %cmp = icmp sgt i32 %tmp, 1
+ %conv = zext i1 %cmp to i32
+ %expval = call i32 @llvm.expect.i32(i32 %conv, i32 1)
+ %tobool = icmp ne i32 %expval, 0
+; CHECK: !prof !0
+; CHECK-NOT: @llvm.expect
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %call = call i32 (...)* @f()
+ store i32 %call, i32* %retval
+ br label %return
+
+if.end: ; preds = %entry
+ store i32 1, i32* %retval
+ br label %return
+
+return: ; preds = %if.end, %if.then
+ %0 = load i32* %retval
+ ret i32 %0
+}
+
+declare i32 @llvm.expect.i32(i32, i32) nounwind readnone
+
+; CHECK: !0 = metadata !{metadata !"branch_weights", i32 64, i32 4}
+; CHECK: !1 = metadata !{metadata !"branch_weights", i32 4, i32 64}
+; CHECK: !2 = metadata !{metadata !"branch_weights", i32 4, i32 64, i32 4}
+; CHECK: !3 = metadata !{metadata !"branch_weights", i32 64, i32 4, i32 4}
diff --git a/test/Transforms/LowerExpectIntrinsic/dg.exp b/test/Transforms/LowerExpectIntrinsic/dg.exp
new file mode 100644
index 0000000000..f2005891a5
--- /dev/null
+++ b/test/Transforms/LowerExpectIntrinsic/dg.exp
@@ -0,0 +1,3 @@
+load_lib llvm.exp
+
+RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,cpp}]]