From c7f992f9a3374e4b4c75f0166815350eea2734aa Mon Sep 17 00:00:00 2001 From: Michael Zolotukhin Date: Mon, 21 Apr 2014 12:01:33 +0000 Subject: [PATCH] Reapply r206732. This time without optimization of branches. llvm-svn: 206749 --- include/llvm/IR/Intrinsics.td | 13 + include/llvm/Target/TargetLowering.h | 16 ++ lib/CodeGen/CodeGenPrepare.cpp | 236 ++++++++++++++++++ lib/CodeGen/TargetLoweringBase.cpp | 1 + lib/Target/ARM64/ARM64ISelLowering.cpp | 2 + .../ARM64/SafeDivRemIntrinsics-Opts.ll | 83 ++++++ test/CodeGen/ARM64/SafeDivRemIntrinsics.ll | 152 +++++++++++ test/CodeGen/X86/SafeDivRemIntrinsics-Opts.ll | 82 ++++++ test/CodeGen/X86/SafeDivRemIntrinsics.ll | 144 +++++++++++ 9 files changed, 729 insertions(+) create mode 100644 test/CodeGen/ARM64/SafeDivRemIntrinsics-Opts.ll create mode 100644 test/CodeGen/ARM64/SafeDivRemIntrinsics.ll create mode 100644 test/CodeGen/X86/SafeDivRemIntrinsics-Opts.ll create mode 100644 test/CodeGen/X86/SafeDivRemIntrinsics.ll diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index 6a48f173935..c65b86f5b70 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -444,6 +444,19 @@ def int_umul_with_overflow : Intrinsic<[llvm_anyint_ty, llvm_i1_ty], [LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>; +def int_safe_udiv : Intrinsic<[llvm_anyint_ty, llvm_i1_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; +def int_safe_urem : Intrinsic<[llvm_anyint_ty, llvm_i1_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; +def int_safe_sdiv : Intrinsic<[llvm_anyint_ty, llvm_i1_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; +def int_safe_srem : Intrinsic<[llvm_anyint_ty, llvm_i1_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; + //===------------------------- Memory Use Markers -------------------------===// // def int_lifetime_start : Intrinsic<[], diff --git a/include/llvm/Target/TargetLowering.h b/include/llvm/Target/TargetLowering.h index 5afcd80a280..2e4956f16d0 100644 --- a/include/llvm/Target/TargetLowering.h +++ b/include/llvm/Target/TargetLowering.h @@ -218,6 +218,10 @@ public: /// Return true if pow2 div is cheaper than a chain of srl/add/sra. bool isPow2DivCheap() const { return Pow2DivIsCheap; } + /// Return true if Div never traps, returns 0 when div by 0 and return TMin, + /// when sdiv TMin by -1. + bool isDivWellDefined() const { return DivIsWellDefined; } + /// Return true if Flow Control is an expensive operation that should be /// avoided. bool isJumpExpensive() const { return JumpIsExpensive; } @@ -1026,6 +1030,13 @@ protected: /// signed divide by power of two, and let the target handle it. void setPow2DivIsCheap(bool isCheap = true) { Pow2DivIsCheap = isCheap; } + /// Tells the code-generator that it is safe to execute sdiv/udiv/srem/urem + /// even when RHS is 0. It is also safe to execute sdiv/srem when LHS is + /// SignedMinValue and RHS is -1. + void setDivIsWellDefined (bool isWellDefined = true) { + DivIsWellDefined = isWellDefined; + } + /// Add the specified register class as an available regclass for the /// specified value type. This indicates the selector can handle values of /// that class natively. @@ -1441,6 +1452,11 @@ private: /// signed divide by power of two, and let the target handle it. bool Pow2DivIsCheap; + /// Tells the code-generator that it is safe to execute sdiv/udiv/srem/urem + /// even when RHS is 0. It is also safe to execute sdiv/srem when LHS is + /// SignedMinValue and RHS is -1. + bool DivIsWellDefined; + /// Tells the code generator that it shouldn't generate extra flow control /// instructions and should attempt to combine flow control instructions via /// predication. diff --git a/lib/CodeGen/CodeGenPrepare.cpp b/lib/CodeGen/CodeGenPrepare.cpp index 1ab24162dc8..009e153e0e8 100644 --- a/lib/CodeGen/CodeGenPrepare.cpp +++ b/lib/CodeGen/CodeGenPrepare.cpp @@ -688,6 +688,242 @@ bool CodeGenPrepare::OptimizeCallInst(CallInst *CI) { } return true; } + // Lower all uses of llvm.safe.[us]{div|rem}... + if (II && + (II->getIntrinsicID() == Intrinsic::safe_sdiv || + II->getIntrinsicID() == Intrinsic::safe_udiv || + II->getIntrinsicID() == Intrinsic::safe_srem || + II->getIntrinsicID() == Intrinsic::safe_urem)) { + // Given + // result_struct = type {iN, i1} + // %R = call result_struct llvm.safe.sdiv.iN(iN %x, iN %y) + // Expand it to actual IR, which produces result to the same variable %R. + // First element of the result %R.1 is the result of division, second + // element shows whether the division was correct or not. + // If %y is 0, %R.1 is 0, %R.2 is 1. (1) + // If %x is minSignedValue and %y is -1, %R.1 is %x, %R.2 is 1. (2) + // In other cases %R.1 is (sdiv %x, %y), %R.2 is 0. (3) + // + // Similar applies to srem, udiv, and urem builtins, except that in unsigned + // variants we don't check condition (2). + + bool IsSigned; + BinaryOperator::BinaryOps Op; + switch (II->getIntrinsicID()) { + case Intrinsic::safe_sdiv: + IsSigned = true; + Op = Instruction::SDiv; + break; + case Intrinsic::safe_udiv: + IsSigned = false; + Op = Instruction::UDiv; + break; + case Intrinsic::safe_srem: + IsSigned = true; + Op = Instruction::SRem; + break; + case Intrinsic::safe_urem: + IsSigned = false; + Op = Instruction::URem; + break; + default: + llvm_unreachable("Only Div/Rem intrinsics are handled here."); + } + + Value *LHS = II->getOperand(0), *RHS = II->getOperand(1); + bool DivWellDefined = TLI && TLI->isDivWellDefined(); + + bool ResultNeeded[2] = {false, false}; + SmallVector ResultsUsers[2]; + bool BadCase = false; + for (User *U: II->users()) { + ExtractValueInst *EVI = dyn_cast(U); + if (!EVI || EVI->getNumIndices() > 1 || EVI->getIndices()[0] > 1) { + BadCase = true; + break; + } + ResultNeeded[EVI->getIndices()[0]] = true; + ResultsUsers[EVI->getIndices()[0]].push_back(U); + } + // Behave conservatively, if there is an unusual user of the results. + if (BadCase) + ResultNeeded[0] = ResultNeeded[1] = true; + + // Early exit if non of the results is ever used. + if (!ResultNeeded[0] && !ResultNeeded[1]) { + II->eraseFromParent(); + return true; + } + + // Early exit if the second result (flag) isn't used and target + // div-instruction computes exactly what we want to get as the first result + // and never traps. + if (ResultNeeded[0] && !ResultNeeded[1] && DivWellDefined) { + BinaryOperator *Div = BinaryOperator::Create(Op, LHS, RHS); + Div->insertAfter(II); + for (User *U: ResultsUsers[0]) { + Instruction *UserInst = dyn_cast(U); + assert(UserInst && "Unexpected null-instruction"); + UserInst->replaceAllUsesWith(Div); + UserInst->eraseFromParent(); + } + II->eraseFromParent(); + CurInstIterator = Div; + ModifiedDT = true; + return true; + } + + Value *MinusOne = Constant::getAllOnesValue(LHS->getType()); + Value *Zero = Constant::getNullValue(LHS->getType()); + + // Split the original BB and create other basic blocks that will be used + // for checks. + BasicBlock *StartBB = II->getParent(); + BasicBlock::iterator SplitPt = ++(BasicBlock::iterator(II)); + BasicBlock *NextBB = StartBB->splitBasicBlock(SplitPt, "div.end"); + + BasicBlock *DivByZeroBB; + DivByZeroBB = BasicBlock::Create(II->getContext(), "div.divz", + NextBB->getParent(), NextBB); + BranchInst::Create(NextBB, DivByZeroBB); + BasicBlock *DivBB = BasicBlock::Create(II->getContext(), "div.div", + NextBB->getParent(), NextBB); + BranchInst::Create(NextBB, DivBB); + + // For signed variants, check the condition (2): + // LHS == SignedMinValue, RHS == -1. + Value *CmpMinusOne; + Value *CmpMinValue; + BasicBlock *ChkDivMinBB; + BasicBlock *DivMinBB; + Value *MinValue; + if (IsSigned) { + APInt SignedMinValue = + APInt::getSignedMinValue(LHS->getType()->getPrimitiveSizeInBits()); + MinValue = Constant::getIntegerValue(LHS->getType(), SignedMinValue); + ChkDivMinBB = BasicBlock::Create(II->getContext(), "div.chkdivmin", + NextBB->getParent(), NextBB); + BranchInst::Create(NextBB, ChkDivMinBB); + DivMinBB = BasicBlock::Create(II->getContext(), "div.divmin", + NextBB->getParent(), NextBB); + BranchInst::Create(NextBB, DivMinBB); + CmpMinusOne = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, + RHS, MinusOne, "cmp.rhs.minus.one", + ChkDivMinBB->getTerminator()); + CmpMinValue = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, + LHS, MinValue, "cmp.lhs.signed.min", + ChkDivMinBB->getTerminator()); + BinaryOperator *CmpSignedOvf = BinaryOperator::Create(Instruction::And, + CmpMinusOne, + CmpMinValue); + // Here we're interested in the case when both %x is TMin and %y is -1. + // In this case the result will overflow. + // If that's not the case, we can perform usual division. These blocks + // will be inserted after DivByZero, so the division will be safe. + CmpSignedOvf->insertBefore(ChkDivMinBB->getTerminator()); + BranchInst::Create(DivMinBB, DivBB, CmpSignedOvf, + ChkDivMinBB->getTerminator()); + ChkDivMinBB->getTerminator()->eraseFromParent(); + } + + // Check the condition (1): + // RHS == 0. + Value *CmpDivZero = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, + RHS, Zero, "cmp.rhs.zero", + StartBB->getTerminator()); + + // If RHS != 0, we want to check condition (2) in signed case, or proceed + // to usual division in unsigned case. + BranchInst::Create(DivByZeroBB, IsSigned ? ChkDivMinBB : DivBB, CmpDivZero, + StartBB->getTerminator()); + StartBB->getTerminator()->eraseFromParent(); + + // At the moment we have all the control flow created. We just need to + // insert DIV and PHI (if needed) to get the result value. + Instruction *DivRes, *FlagRes; + Instruction *InsPoint = nullptr; + if (ResultNeeded[0]) { + BinaryOperator *Div = BinaryOperator::Create(Op, LHS, RHS); + if (DivWellDefined) { + // The result value is the result of DIV operation placed right at the + // original place of the intrinsic. + Div->insertAfter(II); + DivRes = Div; + } else { + // The result is a PHI-node. + Div->insertBefore(DivBB->getTerminator()); + PHINode *DivResPN = + PHINode::Create(LHS->getType(), IsSigned ? 3 : 2, "div.res.phi", + NextBB->begin()); + DivResPN->addIncoming(Div, DivBB); + DivResPN->addIncoming(Zero, DivByZeroBB); + if (IsSigned) + DivResPN->addIncoming(MinValue, DivMinBB); + DivRes = DivResPN; + InsPoint = DivResPN; + } + } + + // Prepare a value for the second result (flag) if it is needed. + if (ResultNeeded[1]) { + Type *FlagTy = II->getType()->getStructElementType(1); + PHINode *FlagResPN = + PHINode::Create(FlagTy, IsSigned ? 3 : 2, "div.flag.phi", + NextBB->begin()); + FlagResPN->addIncoming(Constant::getNullValue(FlagTy), DivBB); + FlagResPN->addIncoming(Constant::getAllOnesValue(FlagTy), DivByZeroBB); + if (IsSigned) + FlagResPN->addIncoming(Constant::getAllOnesValue(FlagTy), DivMinBB); + FlagRes = FlagResPN; + if (!InsPoint) + InsPoint = FlagRes; + } + + // If possible, propagate the results to the user. Otherwise, create alloca, + // and create a struct with the results on stack. + if (!BadCase) { + if (ResultNeeded[0]) { + for (User *U: ResultsUsers[0]) { + Instruction *UserInst = dyn_cast(U); + assert(UserInst && "Unexpected null-instruction"); + UserInst->replaceAllUsesWith(DivRes); + UserInst->eraseFromParent(); + } + } + if (ResultNeeded[1]) { + for (User *FlagU: ResultsUsers[1]) { + Instruction *FlagUInst = dyn_cast(FlagU); + FlagUInst->replaceAllUsesWith(FlagRes); + FlagUInst->eraseFromParent(); + } + } + } else { + // Create alloca, store our new values to it, and then load the final + // result from it. + Constant *Idx0 = ConstantInt::get(Type::getInt32Ty(II->getContext()), 0); + Constant *Idx1 = ConstantInt::get(Type::getInt32Ty(II->getContext()), 1); + Value *Idxs_DivRes[2] = {Idx0, Idx0}; + Value *Idxs_FlagRes[2] = {Idx0, Idx1}; + Value *NewRes = new llvm::AllocaInst(II->getType(), 0, "div.res.ptr", II); + Instruction *ResDivAddr = GetElementPtrInst::Create(NewRes, Idxs_DivRes); + Instruction *ResFlagAddr = + GetElementPtrInst::Create(NewRes, Idxs_FlagRes); + ResDivAddr->insertAfter(InsPoint); + ResFlagAddr->insertAfter(ResDivAddr); + StoreInst *StoreResDiv = new StoreInst(DivRes, ResDivAddr); + StoreInst *StoreResFlag = new StoreInst(FlagRes, ResFlagAddr); + StoreResDiv->insertAfter(ResFlagAddr); + StoreResFlag->insertAfter(StoreResDiv); + LoadInst *LoadRes = new LoadInst(NewRes, "div.res"); + LoadRes->insertAfter(StoreResFlag); + II->replaceAllUsesWith(LoadRes); + } + + II->eraseFromParent(); + CurInstIterator = StartBB->end(); + ModifiedDT = true; + return true; + } if (II && TLI) { SmallVector PtrOps; diff --git a/lib/CodeGen/TargetLoweringBase.cpp b/lib/CodeGen/TargetLoweringBase.cpp index c1a47751eb8..4ed20d02703 100644 --- a/lib/CodeGen/TargetLoweringBase.cpp +++ b/lib/CodeGen/TargetLoweringBase.cpp @@ -682,6 +682,7 @@ TargetLoweringBase::TargetLoweringBase(const TargetMachine &tm, HasMultipleConditionRegisters = false; IntDivIsCheap = false; Pow2DivIsCheap = false; + DivIsWellDefined = false; JumpIsExpensive = false; PredictableSelectIsExpensive = false; MaskAndBranchFoldingIsLegal = false; diff --git a/lib/Target/ARM64/ARM64ISelLowering.cpp b/lib/Target/ARM64/ARM64ISelLowering.cpp index 6df2122abec..7fee5646d1c 100644 --- a/lib/Target/ARM64/ARM64ISelLowering.cpp +++ b/lib/Target/ARM64/ARM64ISelLowering.cpp @@ -435,6 +435,8 @@ ARM64TargetLowering::ARM64TargetLowering(ARM64TargetMachine &TM) setMinFunctionAlignment(2); + setDivIsWellDefined(true); + RequireStrictAlign = StrictAlign; } diff --git a/test/CodeGen/ARM64/SafeDivRemIntrinsics-Opts.ll b/test/CodeGen/ARM64/SafeDivRemIntrinsics-Opts.ll new file mode 100644 index 00000000000..c5ab2935031 --- /dev/null +++ b/test/CodeGen/ARM64/SafeDivRemIntrinsics-Opts.ll @@ -0,0 +1,83 @@ +; RUN: llc < %s -march=arm64 | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +%divovf32 = type { i32, i1 } + +declare %divovf32 @llvm.safe.sdiv.i32(i32, i32) nounwind readnone +declare %divovf32 @llvm.safe.udiv.i32(i32, i32) nounwind readnone + +; CHECK-LABEL: sdiv32_results_unused +; CHECK: entry +; CHECK-NEXT: ret +define void @sdiv32_results_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret void +} + +; CHECK-LABEL: sdiv32_div_result_unused +; CHECK-NOT: sdiv{{[ ]}} +define i1 @sdiv32_div_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + %bit = extractvalue %divovf32 %divr, 1 + ret i1 %bit +} + +; CHECK-LABEL: sdiv32_flag_result_unused +; CHECK-NOT: cb +; CHECK: sdiv{{[ ]}} +define i32 @sdiv32_flag_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + %div = extractvalue %divovf32 %divr, 0 + ret i32 %div +} + +; CHECK-LABEL: sdiv32_result_returned +; CHECK: sdiv{{[ ]}} +define %divovf32 @sdiv32_result_returned(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} + +; CHECK-LABEL: udiv32_results_unused +; CHECK: entry +; CHECK-NEXT: ret +define void @udiv32_results_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret void +} + +; CHECK-LABEL: udiv32_div_result_unused +; CHECK-NOT: udiv{{[ ]}} +define i1 @udiv32_div_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + %bit = extractvalue %divovf32 %divr, 1 + ret i1 %bit +} + +; CHECK-LABEL: udiv32_flag_result_unused +; CHECK-NOT: cb +; CHECK: udiv{{[ ]}} +define i32 @udiv32_flag_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + %div = extractvalue %divovf32 %divr, 0 + ret i32 %div +} + +; CHECK-LABEL: udiv32_result_returned +; CHECK: udiv{{[ ]}} +define %divovf32 @udiv32_result_returned(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} + +!llvm.ident = !{!0} + +!0 = metadata !{metadata !"clang version 3.5.0 "} diff --git a/test/CodeGen/ARM64/SafeDivRemIntrinsics.ll b/test/CodeGen/ARM64/SafeDivRemIntrinsics.ll new file mode 100644 index 00000000000..0abbbfb99d7 --- /dev/null +++ b/test/CodeGen/ARM64/SafeDivRemIntrinsics.ll @@ -0,0 +1,152 @@ +; RUN: llc < %s -march=arm64 | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +%divovf8 = type { i8, i1 } +%divovf16 = type { i16, i1 } +%divovf32 = type { i32, i1 } +%divovf64 = type { i64, i1 } + +declare %divovf8 @llvm.safe.sdiv.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.sdiv.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.sdiv.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.sdiv.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.srem.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.srem.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.srem.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.srem.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.udiv.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.udiv.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.udiv.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.udiv.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.urem.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.urem.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.urem.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.urem.i64(i64, i64) nounwind readnone + +; CHECK-LABEL: sdiv8 +; CHECK: sdiv{{[ ]}} +define %divovf8 @sdiv8(i8 %x, i8 %y) { +entry: + %divr = call %divovf8 @llvm.safe.sdiv.i8(i8 %x, i8 %y) + ret %divovf8 %divr +} +; CHECK-LABEL: sdiv16 +; CHECK: sdiv{{[ ]}} +define %divovf16 @sdiv16(i16 %x, i16 %y) { +entry: + %divr = call %divovf16 @llvm.safe.sdiv.i16(i16 %x, i16 %y) + ret %divovf16 %divr +} +; CHECK-LABEL: sdiv32 +; CHECK: sdiv{{[ ]}} +define %divovf32 @sdiv32(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} +; CHECK-LABEL: sdiv64 +; CHECK: sdiv{{[ ]}} +define %divovf64 @sdiv64(i64 %x, i64 %y) { +entry: + %divr = call %divovf64 @llvm.safe.sdiv.i64(i64 %x, i64 %y) + ret %divovf64 %divr +} +; CHECK-LABEL: udiv8 +; CHECK: udiv{{[ ]}} +define %divovf8 @udiv8(i8 %x, i8 %y) { +entry: + %divr = call %divovf8 @llvm.safe.udiv.i8(i8 %x, i8 %y) + ret %divovf8 %divr +} +; CHECK-LABEL: udiv16 +; CHECK: udiv{{[ ]}} +define %divovf16 @udiv16(i16 %x, i16 %y) { +entry: + %divr = call %divovf16 @llvm.safe.udiv.i16(i16 %x, i16 %y) + ret %divovf16 %divr +} +; CHECK-LABEL: udiv32 +; CHECK: udiv{{[ ]}} +define %divovf32 @udiv32(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} +; CHECK-LABEL: udiv64 +; CHECK: udiv{{[ ]}} +define %divovf64 @udiv64(i64 %x, i64 %y) { +entry: + %divr = call %divovf64 @llvm.safe.udiv.i64(i64 %x, i64 %y) + ret %divovf64 %divr +} +; CHECK-LABEL: srem8 +; CHECK: sdiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf8 @srem8(i8 %x, i8 %y) { +entry: + %remr = call %divovf8 @llvm.safe.srem.i8(i8 %x, i8 %y) + ret %divovf8 %remr +} +; CHECK-LABEL: srem16 +; CHECK: sdiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf16 @srem16(i16 %x, i16 %y) { +entry: + %remr = call %divovf16 @llvm.safe.srem.i16(i16 %x, i16 %y) + ret %divovf16 %remr +} +; CHECK-LABEL: srem32 +; CHECK: sdiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf32 @srem32(i32 %x, i32 %y) { +entry: + %remr = call %divovf32 @llvm.safe.srem.i32(i32 %x, i32 %y) + ret %divovf32 %remr +} +; CHECK-LABEL: srem64 +; CHECK: sdiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf64 @srem64(i64 %x, i64 %y) { +entry: + %remr = call %divovf64 @llvm.safe.srem.i64(i64 %x, i64 %y) + ret %divovf64 %remr +} +; CHECK-LABEL: urem8 +; CHECK: udiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf8 @urem8(i8 %x, i8 %y) { +entry: + %remr = call %divovf8 @llvm.safe.urem.i8(i8 %x, i8 %y) + ret %divovf8 %remr +} +; CHECK-LABEL: urem16 +; CHECK: udiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf16 @urem16(i16 %x, i16 %y) { +entry: + %remr = call %divovf16 @llvm.safe.urem.i16(i16 %x, i16 %y) + ret %divovf16 %remr +} +; CHECK-LABEL: urem32 +; CHECK: udiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf32 @urem32(i32 %x, i32 %y) { +entry: + %remr = call %divovf32 @llvm.safe.urem.i32(i32 %x, i32 %y) + ret %divovf32 %remr +} +; CHECK-LABEL: urem64 +; CHECK: udiv{{[ ]}} +; CHECK: msub{{[ ]}} +define %divovf64 @urem64(i64 %x, i64 %y) { +entry: + %remr = call %divovf64 @llvm.safe.urem.i64(i64 %x, i64 %y) + ret %divovf64 %remr +} + +!llvm.ident = !{!0} + +!0 = metadata !{metadata !"clang version 3.5.0 "} diff --git a/test/CodeGen/X86/SafeDivRemIntrinsics-Opts.ll b/test/CodeGen/X86/SafeDivRemIntrinsics-Opts.ll new file mode 100644 index 00000000000..3b9ce43be49 --- /dev/null +++ b/test/CodeGen/X86/SafeDivRemIntrinsics-Opts.ll @@ -0,0 +1,82 @@ +; RUN: llc < %s -march=x86-64 | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +%divovf32 = type { i32, i1 } + +declare %divovf32 @llvm.safe.sdiv.i32(i32, i32) nounwind readnone +declare %divovf32 @llvm.safe.udiv.i32(i32, i32) nounwind readnone + +; CHECK-LABEL: sdiv32_results_unused +; CHECK: entry +; CHECK-NEXT: ret +define void @sdiv32_results_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret void +} + +; CHECK-LABEL: sdiv32_div_result_unused +; CHECK-NOT: idiv +define i1 @sdiv32_div_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + %bit = extractvalue %divovf32 %divr, 1 + ret i1 %bit +} + +; CHECK-LABEL: sdiv32_flag_result_unused +; CHECK: idiv +define i32 @sdiv32_flag_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + %div = extractvalue %divovf32 %divr, 0 + ret i32 %div +} + +; CHECK-LABEL: sdiv32_result_returned +; CHECK: idiv +define %divovf32 @sdiv32_result_returned(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} + +; CHECK-LABEL: udiv32_results_unused +; CHECK: entry +; CHECK-NEXT: ret +define void @udiv32_results_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret void +} + +; CHECK-LABEL: udiv32_div_result_unused +; CHECK-NOT: udiv{{[ ]}} +define i1 @udiv32_div_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + %bit = extractvalue %divovf32 %divr, 1 + ret i1 %bit +} + +; CHECK-LABEL: udiv32_flag_result_unused +; CHECK-NOT: cb +; CHECK: {{[ ]}}div +define i32 @udiv32_flag_result_unused(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + %div = extractvalue %divovf32 %divr, 0 + ret i32 %div +} + +; CHECK-LABEL: udiv32_result_returned +; CHECK: {{[ ]}}div +define %divovf32 @udiv32_result_returned(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} + +!llvm.ident = !{!0} + +!0 = metadata !{metadata !"clang version 3.5.0 "} diff --git a/test/CodeGen/X86/SafeDivRemIntrinsics.ll b/test/CodeGen/X86/SafeDivRemIntrinsics.ll new file mode 100644 index 00000000000..f71705fa7bc --- /dev/null +++ b/test/CodeGen/X86/SafeDivRemIntrinsics.ll @@ -0,0 +1,144 @@ +; RUN: llc < %s -march=x86-64 | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +%divovf8 = type { i8, i1 } +%divovf16 = type { i16, i1 } +%divovf32 = type { i32, i1 } +%divovf64 = type { i64, i1 } + +declare %divovf8 @llvm.safe.sdiv.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.sdiv.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.sdiv.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.sdiv.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.srem.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.srem.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.srem.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.srem.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.udiv.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.udiv.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.udiv.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.udiv.i64(i64, i64) nounwind readnone + +declare %divovf8 @llvm.safe.urem.i8(i8, i8) nounwind readnone +declare %divovf16 @llvm.safe.urem.i16(i16, i16) nounwind readnone +declare %divovf32 @llvm.safe.urem.i32(i32, i32) nounwind readnone +declare %divovf64 @llvm.safe.urem.i64(i64, i64) nounwind readnone + +; CHECK-LABEL: sdiv8 +; CHECK: idivb{{[ ]}} +define %divovf8 @sdiv8(i8 %x, i8 %y) { +entry: + %divr = call %divovf8 @llvm.safe.sdiv.i8(i8 %x, i8 %y) + ret %divovf8 %divr +} +; CHECK-LABEL: sdiv16 +; CHECK: idivw{{[ ]}} +define %divovf16 @sdiv16(i16 %x, i16 %y) { +entry: + %divr = call %divovf16 @llvm.safe.sdiv.i16(i16 %x, i16 %y) + ret %divovf16 %divr +} +; CHECK-LABEL: sdiv32 +; CHECK: idivl{{[ ]}} +define %divovf32 @sdiv32(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.sdiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} +; CHECK-LABEL: sdiv64 +; CHECK: idivq{{[ ]}} +define %divovf64 @sdiv64(i64 %x, i64 %y) { +entry: + %divr = call %divovf64 @llvm.safe.sdiv.i64(i64 %x, i64 %y) + ret %divovf64 %divr +} +; CHECK-LABEL: udiv8 +; CHECK: {{[ ]}}divb{{[ ]}} +define %divovf8 @udiv8(i8 %x, i8 %y) { +entry: + %divr = call %divovf8 @llvm.safe.udiv.i8(i8 %x, i8 %y) + ret %divovf8 %divr +} +; CHECK-LABEL: udiv16 +; CHECK: {{[ ]}}divw{{[ ]}} +define %divovf16 @udiv16(i16 %x, i16 %y) { +entry: + %divr = call %divovf16 @llvm.safe.udiv.i16(i16 %x, i16 %y) + ret %divovf16 %divr +} +; CHECK-LABEL: udiv32 +; CHECK: {{[ ]}}divl{{[ ]}} +define %divovf32 @udiv32(i32 %x, i32 %y) { +entry: + %divr = call %divovf32 @llvm.safe.udiv.i32(i32 %x, i32 %y) + ret %divovf32 %divr +} +; CHECK-LABEL: udiv64 +; CHECK: {{[ ]}}divq{{[ ]}} +define %divovf64 @udiv64(i64 %x, i64 %y) { +entry: + %divr = call %divovf64 @llvm.safe.udiv.i64(i64 %x, i64 %y) + ret %divovf64 %divr +} +; CHECK-LABEL: srem8 +; CHECK: idivb{{[ ]}} +define %divovf8 @srem8(i8 %x, i8 %y) { +entry: + %remr = call %divovf8 @llvm.safe.srem.i8(i8 %x, i8 %y) + ret %divovf8 %remr +} +; CHECK-LABEL: srem16 +; CHECK: idivw{{[ ]}} +define %divovf16 @srem16(i16 %x, i16 %y) { +entry: + %remr = call %divovf16 @llvm.safe.srem.i16(i16 %x, i16 %y) + ret %divovf16 %remr +} +; CHECK-LABEL: srem32 +; CHECK: idivl{{[ ]}} +define %divovf32 @srem32(i32 %x, i32 %y) { +entry: + %remr = call %divovf32 @llvm.safe.srem.i32(i32 %x, i32 %y) + ret %divovf32 %remr +} +; CHECK-LABEL: srem64 +; CHECK: idivq{{[ ]}} +define %divovf64 @srem64(i64 %x, i64 %y) { +entry: + %remr = call %divovf64 @llvm.safe.srem.i64(i64 %x, i64 %y) + ret %divovf64 %remr +} +; CHECK-LABEL: urem8 +; CHECK: {{[ ]}}divb{{[ ]}} +define %divovf8 @urem8(i8 %x, i8 %y) { +entry: + %remr = call %divovf8 @llvm.safe.urem.i8(i8 %x, i8 %y) + ret %divovf8 %remr +} +; CHECK-LABEL: urem16 +; CHECK: {{[ ]}}divw{{[ ]}} +define %divovf16 @urem16(i16 %x, i16 %y) { +entry: + %remr = call %divovf16 @llvm.safe.urem.i16(i16 %x, i16 %y) + ret %divovf16 %remr +} +; CHECK-LABEL: urem32 +; CHECK: {{[ ]}}divl{{[ ]}} +define %divovf32 @urem32(i32 %x, i32 %y) { +entry: + %remr = call %divovf32 @llvm.safe.urem.i32(i32 %x, i32 %y) + ret %divovf32 %remr +} +; CHECK-LABEL: urem64 +; CHECK: {{[ ]}}divq{{[ ]}} +define %divovf64 @urem64(i64 %x, i64 %y) { +entry: + %remr = call %divovf64 @llvm.safe.urem.i64(i64 %x, i64 %y) + ret %divovf64 %remr +} + +!llvm.ident = !{!0} + +!0 = metadata !{metadata !"clang version 3.5.0 "}