Index: llvm/trunk/lib/Transforms/Scalar/GuardWidening.cpp =================================================================== --- llvm/trunk/lib/Transforms/Scalar/GuardWidening.cpp +++ llvm/trunk/lib/Transforms/Scalar/GuardWidening.cpp @@ -82,6 +82,11 @@ "it is considered frequently taken"), cl::init(1000)); +static cl::opt + WidenBranchGuards("guard-widening-widen-branch-guards", cl::Hidden, + cl::desc("Whether or not we should widen guards " + "expressed as branches by widenable conditions"), + cl::init(true)); namespace { @@ -92,6 +97,10 @@ "Bad guard intrinsic?"); return GI->getArgOperand(0); } + if (isGuardAsWidenableBranch(I)) { + auto *Cond = cast(I)->getCondition(); + return cast(Cond)->getOperand(0); + } return cast(I)->getCondition(); } @@ -262,8 +271,16 @@ void widenGuard(Instruction *ToWiden, Value *NewCondition, bool InvertCondition) { Value *Result; - widenCondCommon(ToWiden->getOperand(0), NewCondition, ToWiden, Result, + widenCondCommon(getCondition(ToWiden), NewCondition, ToWiden, Result, InvertCondition); + Value *WidenableCondition = nullptr; + if (isGuardAsWidenableBranch(ToWiden)) { + auto *Cond = cast(ToWiden)->getCondition(); + WidenableCondition = cast(Cond)->getOperand(1); + } + if (WidenableCondition) + Result = BinaryOperator::CreateAnd(Result, WidenableCondition, + "guard.chk", ToWiden); setCondition(ToWiden, Result); } @@ -281,6 +298,14 @@ }; } +static bool isSupportedGuardInstruction(const Instruction *Insn) { + if (isGuard(Insn)) + return true; + if (WidenBranchGuards && isGuardAsWidenableBranch(Insn)) + return true; + return false; +} + bool GuardWideningImpl::run() { DenseMap> GuardsInBlock; bool Changed = false; @@ -300,7 +325,7 @@ auto &CurrentList = GuardsInBlock[BB]; for (auto &I : *BB) - if (isGuard(&I)) + if (isSupportedGuardInstruction(&I)) CurrentList.push_back(cast(&I)); for (auto *II : CurrentList) @@ -322,7 +347,7 @@ for (auto *I : EliminatedGuardsAndBranches) if (!WidenedGuards.count(I)) { assert(isa(getCondition(I)) && "Should be!"); - if (isGuard(I)) + if (isSupportedGuardInstruction(I)) eliminateGuard(I); else { assert(isa(I) && @@ -452,6 +477,8 @@ auto MaybeHoistingOutOfIf = [&]() { auto *DominatingBlock = DominatingGuard->getParent(); auto *DominatedBlock = DominatedInstr->getParent(); + if (isGuardAsWidenableBranch(DominatingGuard)) + DominatingBlock = cast(DominatingGuard)->getSuccessor(0); // Same Block? if (DominatedBlock == DominatingBlock) Index: llvm/trunk/test/Transforms/GuardWidening/basic_widenable_condition_guards.ll =================================================================== --- llvm/trunk/test/Transforms/GuardWidening/basic_widenable_condition_guards.ll +++ llvm/trunk/test/Transforms/GuardWidening/basic_widenable_condition_guards.ll @@ -0,0 +1,1041 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -guard-widening-widen-branch-guards=true -guard-widening < %s | FileCheck %s +; RUN: opt -S -guard-widening-widen-branch-guards=true -passes=guard-widening < %s | FileCheck %s + +; Basic test case: we wide the first check to check both the +; conditions. +define void @f_0(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_0( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1:%.*]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +; Same as @f_0, but with using a more general notion of postdominance. +define void @f_1(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1:%.*]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: right: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + br label %merge + +right: ; preds = %guarded + br label %merge + +merge: ; preds = %right, %left + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %merge + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %merge + ret void +} + +; Like @f_1, but we have some code we need to hoist before we can +; widen a dominanting check. +define void @f_2(i32 %a, i32 %b) { +; CHECK-LABEL: @f_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: right: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ult i32 %a, 10 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + br label %merge + +right: ; preds = %guarded + br label %merge + +merge: ; preds = %right, %left + %cond_1 = icmp ult i32 %b, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %merge + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %merge + ret void +} + +; Negative test: don't hoist stuff out of control flow +; indiscriminately, since that can make us do more work than needed. +define void @f_3(i32 %a, i32 %b) { +; CHECK-LABEL: @f_3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; CHECK: right: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ult i32 %a, 10 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + %cond_1 = icmp ult i32 %b, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %left + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %left + ret void + +right: ; preds = %guarded + ret void +} + +; But hoisting out of control flow is fine if it makes a loop computed +; condition loop invariant. This behavior may require some tuning in +; the future. +define void @f_4(i32 %a, i32 %b) { +; CHECK-LABEL: @f_4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LOOP:%.*]], label [[LEAVE:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE]] +; CHECK: leave: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ult i32 %a, 10 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %loop, label %leave + +loop: ; preds = %guarded1, %guarded + %cond_1 = icmp ult i32 %b, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %loop + br i1 undef, label %loop, label %leave + +leave: ; preds = %guarded1, %guarded + ret void +} + +; Hoisting out of control flow is also fine if we can widen the +; dominating check without doing any extra work. +define void @f_5(i32 %a) { +; CHECK-LABEL: @f_5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ugt i32 [[A:%.*]], 7 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp uge i32 [[A]], 11 +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[COND_1:%.*]] = icmp ugt i32 [[A]], 10 +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; CHECK: right: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ugt i32 %a, 7 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + %cond_1 = icmp ugt i32 %a, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %left + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %left + ret void + +right: ; preds = %guarded + ret void +} + +; Negative test: the load from %a can be safely speculated to before +; the first guard, but there is no guarantee that it will produce the +; same value. +define void @f_6(i1* dereferenceable(32) %a, i1* %b, i1 %unknown) { +; CHECK-LABEL: @f_6( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = load i1, i1* [[A:%.*]] +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: store i1 [[UNKNOWN:%.*]], i1* [[B:%.*]] +; CHECK-NEXT: [[COND_1:%.*]] = load i1, i1* [[A]] +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = load i1, i1* %a + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + store i1 %unknown, i1* %b + %cond_1 = load i1, i1* %a + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +; All else equal, we try to widen the earliest guard we can. This +; heuristic can use some tuning. +define void @f_7(i32 %a, i1* %cond_buf) { +; CHECK-LABEL: @f_7( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_1:%.*]] = load volatile i1, i1* [[COND_BUF:%.*]] +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A:%.*]], 7 +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_3]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: [[COND_2:%.*]] = load volatile i1, i1* [[COND_BUF]] +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_2]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[COND_3]], [[WIDENABLE_COND7]] +; CHECK-NEXT: br i1 true, label [[GUARDED5:%.*]], label [[DEOPT6:%.*]], !prof !0 +; CHECK: deopt6: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded5: +; CHECK-NEXT: br label [[LEFT]] +; CHECK: right: +; CHECK-NEXT: ret void +; +entry: + %cond_1 = load volatile i1, i1* %cond_buf + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_1, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + %cond_2 = load volatile i1, i1* %cond_buf + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_2, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + br i1 undef, label %left, label %right + +left: ; preds = %guarded5, %guarded1 + %cond_3 = icmp ult i32 %a, 7 + %widenable_cond7 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond8 = and i1 %cond_3, %widenable_cond7 + br i1 %exiplicit_guard_cond8, label %guarded5, label %deopt6, !prof !0 + +deopt6: ; preds = %left + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded5: ; preds = %left + br label %left + +right: ; preds = %guarded1 + ret void +} + +; In this case the earliest dominating guard is in a loop, and we +; don't want to put extra work in there. This heuristic can use some +; tuning. +define void @f_8(i32 %a, i1 %cond_1, i1 %cond_2) { +; CHECK-LABEL: @f_8( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE:%.*]] +; CHECK: leave: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_2:%.*]], [[WIDENABLE_COND3]] +; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A:%.*]], 7 +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_2]], [[COND_3]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: br i1 undef, label [[LOOP2:%.*]], label [[LEAVE2:%.*]] +; CHECK: loop2: +; CHECK-NEXT: [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[COND_3]], [[WIDENABLE_COND7]] +; CHECK-NEXT: br i1 true, label [[GUARDED5:%.*]], label [[DEOPT6:%.*]], !prof !0 +; CHECK: deopt6: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded5: +; CHECK-NEXT: br label [[LOOP2]] +; CHECK: leave2: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: ; preds = %guarded, %entry + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_1, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %loop + br i1 undef, label %loop, label %leave + +leave: ; preds = %guarded + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_2, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %leave + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %leave + br i1 undef, label %loop2, label %leave2 + +loop2: ; preds = %guarded5, %guarded1 + %cond_3 = icmp ult i32 %a, 7 + %widenable_cond7 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond8 = and i1 %cond_3, %widenable_cond7 + br i1 %exiplicit_guard_cond8, label %guarded5, label %deopt6, !prof !0 + +deopt6: ; preds = %loop2 + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded5: ; preds = %loop2 + br label %loop2 + +leave2: ; preds = %guarded1 + ret void +} + +; In cases like these where there isn't any "obviously profitable" +; widening sites, we refuse to do anything. +define void @f_9(i32 %a, i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_9( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FIRST_LOOP:%.*]] +; CHECK: first_loop: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[FIRST_LOOP]], label [[SECOND_LOOP:%.*]] +; CHECK: second_loop: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: br label [[SECOND_LOOP]] +; +entry: + br label %first_loop + +first_loop: ; preds = %guarded, %entry + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %first_loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %first_loop + br i1 undef, label %first_loop, label %second_loop + +second_loop: ; preds = %guarded1, %guarded + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %second_loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %second_loop + br label %second_loop +} + +; Same situation as in @f_9: no "obviously profitable" widening sites, +; so we refuse to do anything. +define void @f_10(i32 %a, i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_10( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[NO_LOOP:%.*]] +; CHECK: no_loop: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: ; preds = %guarded, %entry + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %loop + br i1 undef, label %loop, label %no_loop + +no_loop: ; preds = %guarded + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %no_loop + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %no_loop + ret void +} + +; With guards in loops, we're okay hoisting out the guard into the +; containing loop. +define void @f_11(i32 %a, i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_11( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[OUTER_HEADER:%.*]] +; CHECK: outer_header: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1:%.*]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br label [[INNER:%.*]] +; CHECK: inner: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: br i1 undef, label [[INNER]], label [[OUTER_LATCH:%.*]] +; CHECK: outer_latch: +; CHECK-NEXT: br i1 undef, label [[OUTER_HEADER]], label [[EXIT:%.*]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + br label %outer_header + +outer_header: ; preds = %outer_latch, %entry + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %outer_header + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %outer_header + br label %inner + +inner: ; preds = %guarded1, %guarded + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %inner + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %inner + br i1 undef, label %inner, label %outer_latch + +outer_latch: ; preds = %guarded1 + br i1 undef, label %outer_header, label %exit + +exit: ; preds = %outer_latch + ret void +} + +; Checks that we are adequately guarded against exponential-time +; behavior when hoisting code. +define void @f_12(i32 %a0) { +; CHECK-LABEL: @f_12( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 true, [[WIDENABLE_COND]] +; CHECK-NEXT: [[A1:%.*]] = mul i32 [[A0:%.*]], [[A0]] +; CHECK-NEXT: [[A2:%.*]] = mul i32 [[A1]], [[A1]] +; CHECK-NEXT: [[A3:%.*]] = mul i32 [[A2]], [[A2]] +; CHECK-NEXT: [[A4:%.*]] = mul i32 [[A3]], [[A3]] +; CHECK-NEXT: [[A5:%.*]] = mul i32 [[A4]], [[A4]] +; CHECK-NEXT: [[A6:%.*]] = mul i32 [[A5]], [[A5]] +; CHECK-NEXT: [[A7:%.*]] = mul i32 [[A6]], [[A6]] +; CHECK-NEXT: [[A8:%.*]] = mul i32 [[A7]], [[A7]] +; CHECK-NEXT: [[A9:%.*]] = mul i32 [[A8]], [[A8]] +; CHECK-NEXT: [[A10:%.*]] = mul i32 [[A9]], [[A9]] +; CHECK-NEXT: [[A11:%.*]] = mul i32 [[A10]], [[A10]] +; CHECK-NEXT: [[A12:%.*]] = mul i32 [[A11]], [[A11]] +; CHECK-NEXT: [[A13:%.*]] = mul i32 [[A12]], [[A12]] +; CHECK-NEXT: [[A14:%.*]] = mul i32 [[A13]], [[A13]] +; CHECK-NEXT: [[A15:%.*]] = mul i32 [[A14]], [[A14]] +; CHECK-NEXT: [[A16:%.*]] = mul i32 [[A15]], [[A15]] +; CHECK-NEXT: [[A17:%.*]] = mul i32 [[A16]], [[A16]] +; CHECK-NEXT: [[A18:%.*]] = mul i32 [[A17]], [[A17]] +; CHECK-NEXT: [[A19:%.*]] = mul i32 [[A18]], [[A18]] +; CHECK-NEXT: [[A20:%.*]] = mul i32 [[A19]], [[A19]] +; CHECK-NEXT: [[A21:%.*]] = mul i32 [[A20]], [[A20]] +; CHECK-NEXT: [[A22:%.*]] = mul i32 [[A21]], [[A21]] +; CHECK-NEXT: [[A23:%.*]] = mul i32 [[A22]], [[A22]] +; CHECK-NEXT: [[A24:%.*]] = mul i32 [[A23]], [[A23]] +; CHECK-NEXT: [[A25:%.*]] = mul i32 [[A24]], [[A24]] +; CHECK-NEXT: [[A26:%.*]] = mul i32 [[A25]], [[A25]] +; CHECK-NEXT: [[A27:%.*]] = mul i32 [[A26]], [[A26]] +; CHECK-NEXT: [[A28:%.*]] = mul i32 [[A27]], [[A27]] +; CHECK-NEXT: [[A29:%.*]] = mul i32 [[A28]], [[A28]] +; CHECK-NEXT: [[A30:%.*]] = mul i32 [[A29]], [[A29]] +; CHECK-NEXT: [[COND:%.*]] = trunc i32 [[A30]] to i1 +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 true, [[COND]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 true, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + %a1 = mul i32 %a0, %a0 + %a2 = mul i32 %a1, %a1 + %a3 = mul i32 %a2, %a2 + %a4 = mul i32 %a3, %a3 + %a5 = mul i32 %a4, %a4 + %a6 = mul i32 %a5, %a5 + %a7 = mul i32 %a6, %a6 + %a8 = mul i32 %a7, %a7 + %a9 = mul i32 %a8, %a8 + %a10 = mul i32 %a9, %a9 + %a11 = mul i32 %a10, %a10 + %a12 = mul i32 %a11, %a11 + %a13 = mul i32 %a12, %a12 + %a14 = mul i32 %a13, %a13 + %a15 = mul i32 %a14, %a14 + %a16 = mul i32 %a15, %a15 + %a17 = mul i32 %a16, %a16 + %a18 = mul i32 %a17, %a17 + %a19 = mul i32 %a18, %a18 + %a20 = mul i32 %a19, %a19 + %a21 = mul i32 %a20, %a20 + %a22 = mul i32 %a21, %a21 + %a23 = mul i32 %a22, %a22 + %a24 = mul i32 %a23, %a23 + %a25 = mul i32 %a24, %a24 + %a26 = mul i32 %a25, %a25 + %a27 = mul i32 %a26, %a26 + %a28 = mul i32 %a27, %a27 + %a29 = mul i32 %a28, %a28 + %a30 = mul i32 %a29, %a29 + %cond = trunc i32 %a30 to i1 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +define void @f_13(i32 %a) { +; CHECK-LABEL: @f_13( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp ult i32 [[A]], 10 +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[COND_1:%.*]] = icmp slt i32 [[A]], 10 +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; CHECK: right: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ult i32 %a, 14 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + %cond_1 = icmp slt i32 %a, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %left + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %left + ret void + +right: ; preds = %guarded + ret void +} + +define void @f_14(i32 %a) { +; CHECK-LABEL: @f_14( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14 +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[COND_1:%.*]] = icmp sgt i32 [[A]], 10 +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; CHECK: right: +; CHECK-NEXT: ret void +; +entry: + %cond_0 = icmp ult i32 %a, 14 + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + br i1 undef, label %left, label %right + +left: ; preds = %guarded + %cond_1 = icmp sgt i32 %a, 10 + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %left + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %left + ret void + +right: ; preds = %guarded + ret void +} + +; Make sure we do not widen guard by trivial true conditions into something. +define void @f_15(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_15( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 true, [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 true, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +; Make sure we do not widen guard by trivial false conditions into something. +define void @f_16(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @f_16( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 false, [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 false, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +declare void @llvm.experimental.deoptimize.isVoid(...) + +; Function Attrs: inaccessiblememonly nounwind +declare i1 @llvm.experimental.widenable.condition() #0 + +attributes #0 = { inaccessiblememonly nounwind } + +!0 = !{!"branch_weights", i32 1048576, i32 1} Index: llvm/trunk/test/Transforms/GuardWidening/mixed_guards.ll =================================================================== --- llvm/trunk/test/Transforms/GuardWidening/mixed_guards.ll +++ llvm/trunk/test/Transforms/GuardWidening/mixed_guards.ll @@ -0,0 +1,74 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -guard-widening-widen-branch-guards=true -guard-widening < %s | FileCheck %s +; RUN: opt -S -guard-widening-widen-branch-guards=true -passes=guard-widening < %s | FileCheck %s + +; Interaction between intrinsic and widenable condition guards. + +declare void @llvm.experimental.guard(i1,...) + +declare void @llvm.experimental.deoptimize.isVoid(...) + +; Function Attrs: inaccessiblememonly nounwind +declare i1 @llvm.experimental.widenable.condition() #0 + +; Widen condition of intrinsic guard with a condition from widenable branch. +define void @test_01(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @test_01( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1:%.*]] +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] +; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] +; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 +; CHECK: deopt2: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded1: +; CHECK-NEXT: ret void +; +entry: + call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] + %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 + br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 + +deopt2: ; preds = %guarded + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded1: ; preds = %guarded + ret void +} + +; Widen condition of widenable condition guard with a condition from intrinsic. +define void @test_02(i1 %cond_0, i1 %cond_1) { +; CHECK-LABEL: @test_02( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] +; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1:%.*]] +; CHECK-NEXT: [[GUARD_CHK:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] +; CHECK-NEXT: br i1 [[GUARD_CHK]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 +; CHECK: deopt: +; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] +; CHECK-NEXT: ret void +; CHECK: guarded: +; CHECK-NEXT: ret void +; +entry: + %widenable_cond = call i1 @llvm.experimental.widenable.condition() + %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond + br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 + +deopt: ; preds = %entry + call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] + ret void + +guarded: ; preds = %entry + call void (i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] + ret void +} + +attributes #0 = { inaccessiblememonly nounwind } + +!0 = !{!"branch_weights", i32 1048576, i32 1}