From 9da9934e27dfb48de77b80a3e20ed2d869b52024 Mon Sep 17 00:00:00 2001 From: Jakub Staszak Date: Wed, 6 Jul 2011 18:22:43 +0000 Subject: [PATCH] Introduce "expect" intrinsic instructions. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@134516 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/InitializePasses.h | 1 + include/llvm/Intrinsics.td | 5 + include/llvm/LLVMContext.h | 3 +- include/llvm/LinkAllPasses.h | 1 + include/llvm/Support/PassManagerBuilder.h | 1 + include/llvm/Transforms/Scalar.h | 8 + lib/CodeGen/IntrinsicLowering.cpp | 7 + lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 7 + lib/Transforms/Scalar/Scalar.cpp | 1 + lib/Transforms/Utils/CMakeLists.txt | 1 + lib/Transforms/Utils/LowerExpectIntrinsic.cpp | 163 +++++++++++++++ lib/VMCore/LLVMContext.cpp | 4 + test/CodeGen/Generic/builtin-expect.ll | 223 ++++++++++++++++++++ test/Transforms/LowerExpectIntrinsic/basic.ll | 251 +++++++++++++++++++++++ test/Transforms/LowerExpectIntrinsic/dg.exp | 3 + 15 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 lib/Transforms/Utils/LowerExpectIntrinsic.cpp create mode 100644 test/CodeGen/Generic/builtin-expect.ll create mode 100644 test/Transforms/LowerExpectIntrinsic/basic.ll create mode 100644 test/Transforms/LowerExpectIntrinsic/dg.exp diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index dfd924613ef..0cd12229996 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 14396244afb..c854471036d 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 3502ff73c19..65146c31aaa 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 c2ea8efb9a6..a1945486fc8 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 8ac507f8bcc..ccb49e7287c 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 e8304357518..2187d4eb765 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 3861ddadf65..b0a823042d4 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 ea59ca14d8e..fc8f0d29964 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 158d65352f9..302c287d3cb 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 2df534f2321..204c2c63e1a 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 00000000000..fd94c84413b --- /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 + +using namespace llvm; + +STATISTIC(IfHandled, "Number of 'expect' intrinsic intructions handled"); + +static cl::opt +LikelyBranchWeight("likely-branch-weight", cl::Hidden, cl::init(64), + cl::desc("Weight of the branch likely to be taken (default = 64)")); +static cl::opt +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(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(CI->getArgOperand(1)); + if (!ExpectedValue) + return false; + + LLVMContext &Context = CI->getContext(); + const Type *Int32Ty = Type::getInt32Ty(Context); + + unsigned caseNo = SI->findCaseValue(ExpectedValue); + std::vector 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(BI->getCondition()); + if (!CmpI || CmpI->getPredicate() != CmpInst::ICMP_NE) + return false; + + CallInst *CI = dyn_cast(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(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(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(BB->getTerminator())) { + if (HandleIfExpect(BI)) + IfHandled++; + } else if (SwitchInst *SI = dyn_cast(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(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 1bd497d05d4..ebd1e0aa1b0 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 00000000000..e8cd07badf0 --- /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 00000000000..c00127e1ed2 --- /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 00000000000..f2005891a59 --- /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}]] -- 2.11.0