Index: lib/Target/ARM/ARMTargetTransformInfo.h =================================================================== --- lib/Target/ARM/ARMTargetTransformInfo.h +++ lib/Target/ARM/ARMTargetTransformInfo.h @@ -180,6 +180,11 @@ bool UseMaskForCond = false, bool UseMaskForGaps = false); + bool isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, + TargetLibraryInfo *LibInfo, + TTI::HardwareLoopInfo &HWLoopInfo); + void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP); Index: lib/Target/ARM/ARMTargetTransformInfo.cpp =================================================================== --- lib/Target/ARM/ARMTargetTransformInfo.cpp +++ lib/Target/ARM/ARMTargetTransformInfo.cpp @@ -36,6 +36,10 @@ #define DEBUG_TYPE "armtti" +static cl::opt DisableLowOverheadLoops( + "disable-arm-loloops", cl::Hidden, cl::init(true), + cl::desc("Disable the generation of low-overhead loops")); + bool ARMTTIImpl::areInlineCompatible(const Function *Caller, const Function *Callee) const { const TargetMachine &TM = getTLI()->getTargetMachine(); @@ -628,6 +632,122 @@ UseMaskForCond, UseMaskForGaps); } +bool ARMTTIImpl::isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, + TargetLibraryInfo *LibInfo, + TTI::HardwareLoopInfo &HWLoopInfo) { + // FIXME: Low-overhead branches are only supported in the 'low-overhead branch' + // extension of v8.1-m. + if (!ST->hasV8_1MMainlineOps() || DisableLowOverheadLoops) + return false; + + // For now, for simplicity, only support loops with one exit block. + if (!L->getExitBlock() || !SE.getBackedgeTakenCount(L)) + return false; + + const SCEV *BackedgeTakenCount = SE.getBackedgeTakenCount(L); + if (isa(BackedgeTakenCount)) + return false; + + const SCEV *TripCountSCEV = + SE.getAddExpr(BackedgeTakenCount, + SE.getOne(BackedgeTakenCount->getType())); + + // We need to store the trip count in LR, a 32-bit register. + if (SE.getUnsignedRangeMax(TripCountSCEV).getBitWidth() > 32) + return false; + + // Event, LO_BRANCH_INFO + // LE, LETP instruction, Conditionally set + // Execution reaches LO_BRANCH_INFO.END_ADDR, Conditionally cleared + // BF, BFX, BFL, BFLX, BFCSEL instruction, Set + // Instructions that modifies the PC when LO_BRANCH_INFO.BF is set, Cleared + + // Making a call will trash LR and invalid the LO_BRANCH_INFO, so there's + // little point in generating a hardware loop if that's going to happen. + auto MaybeCall = [this](Instruction &I) { + + // Will a call be lowered to a bl? + // FIXME Handle calls, such as intrinsics that won't generate calls. + if (isa(I)) + return true; + + // Will an instruction get lowered to a lib call? + const ARMTargetLowering *TLI = getTLI(); + unsigned ISD = TLI->InstructionOpcodeToISD(I.getOpcode()); + EVT VT = TLI->getValueType(DL, I.getType(), true); + if (TLI->getOperationAction(ISD, VT) == TargetLowering::LibCall) + return true; + + // FIXME: Unfortunately the approach of checking the Operation Action does + // not catch all cases of Legalization that use library calls. Our + // Legalization step categorizes some transformations into library calls as + // Custom, Expand or even Legal when doing type legalization. So for now + // we have to special case for instance the SDIV of 64bit integers and the + // use of floating point emulation. + if (VT.isInteger() && VT.getSizeInBits() >= 64) { + switch (ISD) { + default: + break; + case ISD::SDIV: + case ISD::UDIV: + case ISD::SREM: + case ISD::UREM: + case ISD::SDIVREM: + case ISD::UDIVREM: + return true; + } + } + + // If the instruction is not of a floating point type or a conversion from + // a floating point type, then it is safe to assume no FP emulation will be + // used. + Type *Ty = (I.getOpcode() == Instruction::FPToSI || + I.getOpcode() == Instruction::FPToUI) ? + I.getOperand(0)->getType() : I.getType(); + + // Assume any other integer operations are okay. + if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy()) + return false; + + // We'll need a libcall to perform double precision operations on a single + // precision only FPU. + if (Ty->isDoubleTy() && !ST->hasFP64()) + return false; + + // We'll need a library call to handle most floats when using soft. + if (TLI->useSoftFloat()) { + switch (I.getOpcode()) { + default: + return true; + case Instruction::Alloca: + case Instruction::Load: + case Instruction::Store: + case Instruction::Select: + case Instruction::PHI: + break; + } + } + return false; + }; + + // Scan the instructions to see if there's any that we know will turn into a + // call. + for (auto *BB : L->getBlocks()) + for (auto &I : *BB) + if (MaybeCall(I)) + return false; + + // TODO: Check whether the trip count calculation is expensive. If L is the + // inner loop but we know it has a low trip count, calculating that trip + // count (in the parent loop) may be detrimental. + + LLVMContext &C = L->getHeader()->getContext(); + HWLoopInfo.CounterInReg = true; + HWLoopInfo.CountType = Type::getInt32Ty(C); + return true; +} + void ARMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP) { // Only currently enable these preferences for M-Class cores. Index: test/Transforms/HardwareLoops/ARM/calls.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/calls.ll @@ -0,0 +1,33 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s + +; CHECK-LABEL: skip_call +; CHECK-NOT: call void @llvm.set.loop.iterations +; CHECK-NOT: call i32 @llvm.loop.decrement + +define i32 @skip_call(i32 %n) { +entry: + %cmp6 = icmp eq i32 %n, 0 + br i1 %cmp6, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.08 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.07 = phi i32 [ %add, %while.body ], [ 0, %while.body.preheader ] + %call = tail call i32 bitcast (i32 (...)* @bar to i32 ()*)() #2 + %add = add nsw i32 %call, %res.07 + %inc1 = add nuw i32 %i.08, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +declare i32 @bar(...) local_unnamed_addr #1 + Index: test/Transforms/HardwareLoops/ARM/counter.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/counter.ll @@ -0,0 +1,35 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -o - | FileCheck %s + +@g = common local_unnamed_addr global i32* null, align 4 + +; CHECK-LABEL: counter_too_large +; CHECK-NOT: call void @llvm.set.loop.iterations +; CHECK-NOT: call i32 @llvm.loop.decrement + +define i32 @counter_too_large(i64 %n) { +entry: + %cmp7 = icmp eq i64 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.lr.ph + +while.body.lr.ph: + %0 = load i32*, i32** @g, align 4 + br label %while.body + +while.body: + %i.09 = phi i64 [ 0, %while.body.lr.ph ], [ %inc1, %while.body ] + %res.08 = phi i32 [ 0, %while.body.lr.ph ], [ %add, %while.body ] + %idxprom = trunc i64 %i.09 to i32 + %arrayidx = getelementptr inbounds i32, i32* %0, i32 %idxprom + %1 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %1, %res.08 + %inc1 = add nuw i64 %i.09, 1 + %cmp = icmp ult i64 %inc1, %n + br i1 %cmp, label %while.body, label %while.end.loopexit + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} Index: test/Transforms/HardwareLoops/ARM/do-rem.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/do-rem.ll @@ -0,0 +1,259 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s + +@g = common local_unnamed_addr global i32* null, align 4 + +; CHECK-LABEL: do_with_i32_urem +; CHECK: while.body.preheader: +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %while.body.preheader ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit + +define i32 @do_with_i32_urem(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i32 [ %add, %while.body ], [ 0, %while.body.preheader ] + %rem = urem i32 %i.09, 5 + %add = add i32 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i32_srem +; CHECK: while.body.preheader: +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %while.body.preheader ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit + +define i32 @do_with_i32_srem(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i32 [ %add, %while.body ], [ 0, %while.body.preheader ] + %rem = srem i32 %i.09, 5 + %add = sub i32 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i32_udiv +; CHECK: while.body.preheader: +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %while.body.preheader ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit + +define i32 @do_with_i32_udiv(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i32 [ %add, %while.body ], [ 0, %while.body.preheader ] + %rem = udiv i32 %i.09, 5 + %add = add i32 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i32_sdiv +; CHECK: while.body.preheader: +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %while.body.preheader ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit + +define i32 @do_with_i32_sdiv(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i32 [ %add, %while.body ], [ 0, %while.body.preheader ] + %rem = sdiv i32 %i.09, 5 + %add = sub i32 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i64_urem +; CHECK-NOT: llvm.set.loop.iterations +; CHECK-NOT: llvm.loop.decrement +define i64 @do_with_i64_urem(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i64 [ %add, %while.body ], [ 0, %while.body.preheader ] + %conv = zext i32 %i.09 to i64 + %rem = urem i64 %conv, 5 + %add = add i64 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i64 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i64 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i64_srem +; CHECK-NOT: llvm.set.loop.iterations +; CHECK-NOT: llvm.loop.decrement +define i64 @do_with_i64_srem(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i64 [ %add, %while.body ], [ 0, %while.body.preheader ] + %conv = zext i32 %i.09 to i64 + %rem = srem i64 %conv, 5 + %add = sub i64 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i64 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i64 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i64_udiv +; CHECK-NOT: llvm.set.loop.iterations +; CHECK-NOT: llvm.loop.decrement +define i64 @do_with_i64_udiv(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i64 [ %add, %while.body ], [ 0, %while.body.preheader ] + %conv = zext i32 %i.09 to i64 + %rem = udiv i64 %conv, 5 + %add = add i64 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i64 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i64 %res.0.lcssa +} + +; CHECK-LABEL: do_with_i64_sdiv +; CHECK-NOT: call void @llvm.set.loop.iterations +; CHECK-NOT: call i32 @llvm.loop.decrement +define i64 @do_with_i64_sdiv(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.preheader + +while.body.preheader: + br label %while.body + +while.body: + %i.09 = phi i32 [ %inc1, %while.body ], [ 0, %while.body.preheader ] + %res.08 = phi i64 [ %add, %while.body ], [ 0, %while.body.preheader ] + %conv = zext i32 %i.09 to i64 + %rem = sdiv i64 %conv, 5 + %add = sub i64 %rem, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i64 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i64 %res.0.lcssa +} Index: test/Transforms/HardwareLoops/ARM/fp-emulation.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/fp-emulation.ll @@ -0,0 +1,203 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HARD +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -mattr=+soft-float -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SOFT + +; CHECK-LABEL: test_fptosi +; CHECK: while.body.lr.ph: +; CHECK: [[CMP:%[^ ]+]] = icmp ugt i32 %n, 1 +; CHECK: [[COUNT:%[^ ]+]] = select i1 [[CMP]], i32 %n, i32 1 +; CHECK: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %if.end4 ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %cleanup.loopexit + +define void @test_fptosi(i32 %n, i32** %g, double** %d) { +entry: + %n.off = add i32 %n, -1 + %0 = icmp ult i32 %n.off, 500 + br i1 %0, label %while.body.lr.ph, label %cleanup + +while.body.lr.ph: + %1 = load double*, double** %d, align 4 + %2 = load i32*, i32** %g, align 4 + br label %while.body + +while.body: + %i.012 = phi i32 [ 0, %while.body.lr.ph ], [ %inc, %if.end4 ] + %rem = urem i32 %i.012, 10 + %tobool = icmp eq i32 %rem, 0 + br i1 %tobool, label %if.end4, label %if.then2 + +if.then2: + %arrayidx = getelementptr inbounds double, double* %1, i32 %i.012 + %3 = load double, double* %arrayidx, align 8 + %conv = fptosi double %3 to i32 + %arrayidx3 = getelementptr inbounds i32, i32* %2, i32 %i.012 + store i32 %conv, i32* %arrayidx3, align 4 + br label %if.end4 + +if.end4: + %inc = add nuw i32 %i.012, 1 + %cmp1 = icmp ult i32 %inc, %n + br i1 %cmp1, label %while.body, label %cleanup.loopexit + +cleanup.loopexit: + br label %cleanup + +cleanup: + ret void +} + +; CHECK-LABEL: test_fptoui +; CHECK: while.body.lr.ph: +; CHECK: [[CMP:%[^ ]+]] = icmp ugt i32 %n, 1 +; CHECK: [[COUNT:%[^ ]+]] = select i1 [[CMP]], i32 %n, i32 1 +; CHECK: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %if.end4 ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %cleanup.loopexit + +define void @test_fptoui(i32 %n, i32** %g, double** %d) { +entry: + %n.off = add i32 %n, -1 + %0 = icmp ult i32 %n.off, 500 + br i1 %0, label %while.body.lr.ph, label %cleanup + +while.body.lr.ph: + %1 = load double*, double** %d, align 4 + %2 = load i32*, i32** %g, align 4 + br label %while.body + +while.body: + %i.012 = phi i32 [ 0, %while.body.lr.ph ], [ %inc, %if.end4 ] + %rem = urem i32 %i.012, 10 + %tobool = icmp eq i32 %rem, 0 + br i1 %tobool, label %if.end4, label %if.then2 + +if.then2: + %arrayidx = getelementptr inbounds double, double* %1, i32 %i.012 + %3 = load double, double* %arrayidx, align 8 + %conv = fptoui double %3 to i32 + %arrayidx3 = getelementptr inbounds i32, i32* %2, i32 %i.012 + store i32 %conv, i32* %arrayidx3, align 4 + br label %if.end4 + +if.end4: + %inc = add nuw i32 %i.012, 1 + %cmp1 = icmp ult i32 %inc, %n + br i1 %cmp1, label %while.body, label %cleanup.loopexit + +cleanup.loopexit: + br label %cleanup + +cleanup: + ret void +} + +; CHECK-LABEL: load_store_float +; CHECK: while.body.lr.ph: +; CHECK: [[CMP:%[^ ]+]] = icmp ugt i32 %n, 1 +; CHECK: [[COUNT:%[^ ]+]] = select i1 [[CMP]], i32 %n, i32 1 +; CHECK: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %if.end4 ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %cleanup.loopexit + +define void @load_store_float(i32 %n, double** %d, double** %g) { +entry: + %n.off = add i32 %n, -1 + %0 = icmp ult i32 %n.off, 500 + br i1 %0, label %while.body.lr.ph, label %cleanup + +while.body.lr.ph: + %1 = load double*, double** %d, align 4 + %2 = load double*, double** %g, align 4 + br label %while.body + +while.body: + %i.012 = phi i32 [ 0, %while.body.lr.ph ], [ %inc, %if.end4 ] + %rem = urem i32 %i.012, 10 + %tobool = icmp eq i32 %rem, 0 + br i1 %tobool, label %if.end4, label %if.then2 + +if.then2: + %arrayidx = getelementptr inbounds double, double* %1, i32 %i.012 + %3 = load double, double* %arrayidx, align 8 + %arrayidx3 = getelementptr inbounds double, double* %2, i32 %i.012 + store double %3, double* %arrayidx3, align 8 + br label %if.end4 + +if.end4: + %inc = add nuw i32 %i.012, 1 + %cmp1 = icmp ult i32 %inc, %n + br i1 %cmp1, label %while.body, label %cleanup.loopexit + +cleanup.loopexit: + br label %cleanup + +cleanup: + ret void +} + +; CHECK-LABEL: fp_add +; CHECK: while.body.lr.ph: + +; CHECK-SOFT-NOT: call void @llvm.set.loop.iterations + +; CHECK-HARD: [[CMP:%[^ ]+]] = icmp ugt i32 %n, 1 +; CHECK-HARD: [[COUNT:%[^ ]+]] = select i1 [[CMP]], i32 %n, i32 1 +; CHECK-HARD: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK: br label %while.body + +; CHECK-SOFT-NOT: call i32 @llvm.loop.decrement + +; CHECK-HARD: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %if.end4 ] +; CHECK-HARD: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK-HARD: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK-HARD: br i1 [[CMP]], label %while.body, label %cleanup.loopexit + +define void @fp_add(i32 %n, float** %d, float** %g) { +entry: + %n.off = add i32 %n, -1 + %0 = icmp ult i32 %n.off, 500 + br i1 %0, label %while.body.lr.ph, label %cleanup + +while.body.lr.ph: + %1 = load float*, float** %d, align 4 + %2 = load float*, float** %g, align 4 + br label %while.body + +while.body: + %i.012 = phi i32 [ 0, %while.body.lr.ph ], [ %inc, %if.end4 ] + %rem = urem i32 %i.012, 10 + %tobool = icmp eq i32 %rem, 0 + br i1 %tobool, label %if.end4, label %if.then2 + +if.then2: + %arrayidx = getelementptr inbounds float, float* %1, i32 %i.012 + %3 = load float, float* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds float, float* %2, i32 %i.012 + %4 = load float, float* %arrayidx3, align 4 + %add = fadd float %3, %4 + store float %add, float* %arrayidx3, align 4 + br label %if.end4 + +if.end4: + %inc = add nuw i32 %i.012, 1 + %cmp1 = icmp ult i32 %inc, %n + br i1 %cmp1, label %while.body, label %cleanup.loopexit + +cleanup.loopexit: + br label %cleanup + +cleanup: + ret void +} Index: test/Transforms/HardwareLoops/ARM/simple-do.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/simple-do.ll @@ -0,0 +1,155 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s + +; RUN: opt -mtriple=thumbv8m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s --check-prefix=DISABLED + +; DISABLED-NOT: llvm.set.loop.iterations +; DISABLED-NOT: llvm.loop.decrement + +@g = common local_unnamed_addr global i32* null, align 4 + +; CHECK-LABEL: do_copy +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %entry ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end +define i32 @do_copy(i32 %n, i32* nocapture %p, i32* nocapture readonly %q) { +entry: + br label %while.body + +while.body: + %q.addr.05 = phi i32* [ %incdec.ptr, %while.body ], [ %q, %entry ] + %p.addr.04 = phi i32* [ %incdec.ptr1, %while.body ], [ %p, %entry ] + %x.addr.03 = phi i32 [ %dec, %while.body ], [ %n, %entry ] + %dec = add nsw i32 %x.addr.03, -1 + %incdec.ptr = getelementptr inbounds i32, i32* %q.addr.05, i32 1 + %0 = load i32, i32* %q.addr.05, align 4 + %incdec.ptr1 = getelementptr inbounds i32, i32* %p.addr.04, i32 1 + store i32 %0, i32* %p.addr.04, align 4 + %tobool = icmp eq i32 %dec, 0 + br i1 %tobool, label %while.end, label %while.body + +while.end: + ret i32 0 +} + +; CHECK-LABEL: do_inc1 +; CHECK: while.body.lr.ph: +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %n) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %n, %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit + +define i32 @do_inc1(i32 %n) { +entry: + %cmp7 = icmp eq i32 %n, 0 + br i1 %cmp7, label %while.end, label %while.body.lr.ph + +while.body.lr.ph: + %0 = load i32*, i32** @g, align 4 + br label %while.body + +while.body: + %i.09 = phi i32 [ 0, %while.body.lr.ph ], [ %inc1, %while.body ] + %res.08 = phi i32 [ 0, %while.body.lr.ph ], [ %add, %while.body ] + %arrayidx = getelementptr inbounds i32, i32* %0, i32 %i.09 + %1 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %1, %res.08 + %inc1 = add nuw i32 %i.09, 1 + %exitcond = icmp eq i32 %inc1, %n + br i1 %exitcond, label %while.end.loopexit, label %while.body + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_inc2 +; CHECK: while.body.lr.ph: +; CHECK: [[ROUND:%[^ ]+]] = add i32 %n, -1 +; CHECK: [[HALVE:%[^ ]+]] = lshr i32 [[ROUND]], 1 +; CHECK: [[COUNT:%[^ ]+]] = add i32 [[HALVE]], 1 +; CHECK: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit +define i32 @do_inc2(i32 %n) { +entry: + %cmp7 = icmp sgt i32 %n, 0 + br i1 %cmp7, label %while.body.lr.ph, label %while.end + +while.body.lr.ph: + %0 = load i32*, i32** @g, align 4 + br label %while.body + +while.body: + %i.09 = phi i32 [ 0, %while.body.lr.ph ], [ %add1, %while.body ] + %res.08 = phi i32 [ 0, %while.body.lr.ph ], [ %add, %while.body ] + %arrayidx = getelementptr inbounds i32, i32* %0, i32 %i.09 + %1 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %1, %res.08 + %add1 = add nuw nsw i32 %i.09, 2 + %cmp = icmp slt i32 %add1, %n + br i1 %cmp, label %while.body, label %while.end.loopexit + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} + +; CHECK-LABEL: do_dec2 + +; CHECK: while.body.lr.ph: +; CHECK: [[ROUND:%[^ ]+]] = add i32 %n, 1 +; CHECK: [[CMP:%[^ ]+]] = icmp slt i32 %n, 2 +; CHECK: [[SMIN:%[^ ]+]] = select i1 [[CMP]], i32 %n, i32 2 +; CHECK: [[SUB:%[^ ]+]] = sub i32 [[ROUND]], [[SMIN]] +; CHECK: [[HALVE:%[^ ]+]] = lshr i32 [[SUB]], 1 +; CHECK: [[COUNT:%[^ ]+]] = add i32 [[HALVE]], 1 +; CHECK: call void @llvm.set.loop.iterations.i32(i32 [[COUNT]]) +; CHECK-NEXT: br label %while.body + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ [[COUNT]], %while.body.lr.ph ], [ [[LOOP_DEC:%[^ ]+]], %while.body ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body, label %while.end.loopexit +define i32 @do_dec2(i32 %n) { +entry: + %cmp6 = icmp sgt i32 %n, 0 + br i1 %cmp6, label %while.body.lr.ph, label %while.end + +while.body.lr.ph: + %0 = load i32*, i32** @g, align 4 + br label %while.body + +while.body: + %i.08 = phi i32 [ %n, %while.body.lr.ph ], [ %sub, %while.body ] + %res.07 = phi i32 [ 0, %while.body.lr.ph ], [ %add, %while.body ] + %arrayidx = getelementptr inbounds i32, i32* %0, i32 %i.08 + %1 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %1, %res.07 + %sub = add nsw i32 %i.08, -2 + %cmp = icmp sgt i32 %i.08, 2 + br i1 %cmp, label %while.body, label %while.end.loopexit + +while.end.loopexit: + br label %while.end + +while.end: + %res.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.end.loopexit ] + ret i32 %res.0.lcssa +} Index: test/Transforms/HardwareLoops/ARM/structure.ll =================================================================== --- /dev/null +++ test/Transforms/HardwareLoops/ARM/structure.ll @@ -0,0 +1,72 @@ +; RUN: opt -mtriple=thumbv8.1m.main-arm-none-eabi -hardware-loops -disable-arm-loloops=false %s -S -o - | FileCheck %s + +; CHECK-LABEL: early_exit +; CHECK-NOT: llvm.set.loop.iterations +; CHECK-NOT: llvm.loop.decrement +define i32 @early_exit(i32* nocapture readonly %a, i32 %max, i32 %n) { +entry: + br label %do.body + +do.body: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %if.end ] + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %i.0 + %0 = load i32, i32* %arrayidx, align 4 + %cmp = icmp sgt i32 %0, %max + br i1 %cmp, label %do.end, label %if.end + +if.end: + %inc = add nuw i32 %i.0, 1 + %cmp1 = icmp ult i32 %inc, %n + br i1 %cmp1, label %do.body, label %if.end.do.end_crit_edge + +if.end.do.end_crit_edge: + %arrayidx2.phi.trans.insert = getelementptr inbounds i32, i32* %a, i32 %inc + %.pre = load i32, i32* %arrayidx2.phi.trans.insert, align 4 + br label %do.end + +do.end: + %1 = phi i32 [ %.pre, %if.end.do.end_crit_edge ], [ %0, %do.body ] + ret i32 %1 +} + +; CHECK-LABEL: nested +; CHECK-NOT: call void @llvm.set.loop.iterations.i32(i32 %N) +; CHECK: br i1 %cmp20, label %while.end7, label %while.cond1.preheader.us + +; CHECK: call void @llvm.set.loop.iterations.i32(i32 %N) +; CHECK: br label %while.body3.us + +; CHECK: [[REM:%[^ ]+]] = phi i32 [ %N, %while.cond1.preheader.us ], [ [[LOOP_DEC:%[^ ]+]], %while.body3.us ] +; CHECK: [[LOOP_DEC]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[REM]], i32 1) +; CHECK: [[CMP:%[^ ]+]] = icmp ne i32 [[LOOP_DEC]], 0 +; CHECK: br i1 [[CMP]], label %while.body3.us, label %while.cond1.while.end_crit_edge.us + +; CHECK-NOT: [[LOOP_DEC1:%[^ ]+]] = call i1 @llvm.loop.decrement.i32(i32 1) +; CHECK-NOT: br i1 [[LOOP_DEC1]], label %while.cond1.preheader.us, label %while.end7 +define void @nested(i32* nocapture %A, i32 %N) { +entry: + %cmp20 = icmp eq i32 %N, 0 + br i1 %cmp20, label %while.end7, label %while.cond1.preheader.us + +while.cond1.preheader.us: + %i.021.us = phi i32 [ %inc6.us, %while.cond1.while.end_crit_edge.us ], [ 0, %entry ] + %mul.us = mul i32 %i.021.us, %N + br label %while.body3.us + +while.body3.us: + %j.019.us = phi i32 [ 0, %while.cond1.preheader.us ], [ %inc.us, %while.body3.us ] + %add.us = add i32 %j.019.us, %mul.us + %arrayidx.us = getelementptr inbounds i32, i32* %A, i32 %add.us + store i32 %add.us, i32* %arrayidx.us, align 4 + %inc.us = add nuw i32 %j.019.us, 1 + %exitcond = icmp eq i32 %inc.us, %N + br i1 %exitcond, label %while.cond1.while.end_crit_edge.us, label %while.body3.us + +while.cond1.while.end_crit_edge.us: + %inc6.us = add nuw i32 %i.021.us, 1 + %exitcond23 = icmp eq i32 %inc6.us, %N + br i1 %exitcond23, label %while.end7, label %while.cond1.preheader.us + +while.end7: + ret void +}