diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -228,6 +228,8 @@ /// are floating values that do not have a corresponding attribute list /// position. struct IRPosition { + // NOTE: In the future this definition can be changed to support recursive + // functions. using CallBaseContext = CallBase; /// The positions we distinguish in the IR. diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -430,8 +430,9 @@ /// Clamp the information known for all returned values of a function /// (identified by \p QueryingAA) into \p S. template -static void clampReturnedValueStates(Attributor &A, const AAType &QueryingAA, - StateType &S) { +static void clampReturnedValueStates( + Attributor &A, const AAType &QueryingAA, StateType &S, + const IRPosition::CallBaseContext *CBContext = nullptr) { LLVM_DEBUG(dbgs() << "[Attributor] Clamp return value states for " << QueryingAA << " into " << S << "\n"); @@ -448,7 +449,7 @@ // Callback for each possibly returned value. auto CheckReturnValue = [&](Value &RV) -> bool { - const IRPosition &RVPos = IRPosition::value(RV); + const IRPosition &RVPos = IRPosition::value(RV, CBContext); const AAType &AA = A.getAAFor(QueryingAA, RVPos); LLVM_DEBUG(dbgs() << "[Attributor] RV: " << RV << " AA: " << AA.getAsStr() << " @ " << RVPos << "\n"); @@ -470,7 +471,8 @@ /// Helper class for generic deduction: return value -> returned position. template + typename StateType = typename BaseType::StateType, + bool PropagateCallBaseContext = false> struct AAReturnedFromReturnedValues : public BaseType { AAReturnedFromReturnedValues(const IRPosition &IRP, Attributor &A) : BaseType(IRP, A) {} @@ -478,7 +480,9 @@ /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { StateType S(StateType::getBestState(this->getState())); - clampReturnedValueStates(A, *this, S); + clampReturnedValueStates( + A, *this, S, + PropagateCallBaseContext ? this->getCallBaseContext() : nullptr); // TODO: If we know we visited all returned values, thus no are assumed // dead, we can take the known information from the state T. return clampStateAndIndicateChange(this->getState(), S); @@ -532,17 +536,57 @@ S ^= *T; } -/// Helper class for generic deduction: call site argument -> argument position. +/// This function is the bridge between argument position and the call base +/// context. template +bool getArgumentStateFromCallBaseContext(Attributor &A, + BaseType &QueryingAttribute, + IRPosition &Pos, StateType &State) { + assert((Pos.getPositionKind() == IRPosition::IRP_ARGUMENT) && + "Expected an 'argument' position !"); + const CallBase *CBContext = Pos.getCallBaseContext(); + if (!CBContext) + return false; + + int ArgNo = Pos.getCallSiteArgNo(); + assert(ArgNo >= 0 && "Invalid Arg No!"); + + const auto &AA = A.getAAFor( + QueryingAttribute, IRPosition::callsite_argument(*CBContext, ArgNo)); + const StateType &CBArgumentState = + static_cast(AA.getState()); + + LLVM_DEBUG(dbgs() << "[Attributor] Briding Call site context to argument" + << "Position:" << Pos << "CB Arg state:" << CBArgumentState + << "\n"); + + // NOTE: If we want to do call site grouping it should happen here. + State ^= CBArgumentState; + return true; +} + +/// Helper class for generic deduction: call site argument -> argument position. +template struct AAArgumentFromCallSiteArguments : public BaseType { AAArgumentFromCallSiteArguments(const IRPosition &IRP, Attributor &A) : BaseType(IRP, A) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { - StateType S(StateType::getBestState(this->getState())); + StateType S = StateType::getBestState(this->getState()); + + if (BridgeCallBaseContext) { + bool Success = + getArgumentStateFromCallBaseContext( + A, *this, this->getIRPosition(), S); + if (Success) + return clampStateAndIndicateChange(this->getState(), S); + } clampCallSiteArgumentStates(A, *this, S); + // TODO: If we know we visited all incoming values, thus no are assumed // dead, we can take the known information from the state T. return clampStateAndIndicateChange(this->getState(), S); @@ -551,7 +595,8 @@ /// Helper class for generic replication: function returned -> cs returned. template + typename StateType = typename BaseType::StateType, + bool IntroduceCallBaseContext = false> struct AACallSiteReturnedFromReturned : public BaseType { AACallSiteReturnedFromReturned(const IRPosition &IRP, Attributor &A) : BaseType(IRP, A) {} @@ -569,7 +614,13 @@ if (!AssociatedFunction) return S.indicatePessimisticFixpoint(); - IRPosition FnPos = IRPosition::returned(*AssociatedFunction); + CallBase &CBContext = static_cast(this->getAnchorValue()); + if (IntroduceCallBaseContext) + LLVM_DEBUG(dbgs() << "[Attributor] Introducing call base context:" + << CBContext << "\n"); + + IRPosition FnPos = IRPosition::returned( + *AssociatedFunction, IntroduceCallBaseContext ? &CBContext : nullptr); const AAType &AA = A.getAAFor(*this, FnPos); return clampStateAndIndicateChange(S, AA.getState()); } @@ -7090,9 +7141,11 @@ struct AAValueConstantRangeArgument final : AAArgumentFromCallSiteArguments< - AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState> { + AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState, + true /* BridgeCallBaseContext */> { using Base = AAArgumentFromCallSiteArguments< - AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState>; + AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState, + true /* BridgeCallBaseContext */>; AAValueConstantRangeArgument(const IRPosition &IRP, Attributor &A) : Base(IRP, A) {} @@ -7113,9 +7166,14 @@ struct AAValueConstantRangeReturned : AAReturnedFromReturnedValues { - using Base = AAReturnedFromReturnedValues; + AAValueConstantRangeImpl, + AAValueConstantRangeImpl::StateType, + /* PropogateCallBaseContext */ true> { + using Base = + AAReturnedFromReturnedValues; AAValueConstantRangeReturned(const IRPosition &IRP, Attributor &A) : Base(IRP, A) {} @@ -7184,13 +7242,13 @@ if (!LHS->getType()->isIntegerTy() || !RHS->getType()->isIntegerTy()) return false; - auto &LHSAA = - A.getAAFor(*this, IRPosition::value(*LHS)); + auto &LHSAA = A.getAAFor( + *this, IRPosition::value(*LHS, getCallBaseContext())); QuerriedAAs.push_back(&LHSAA); auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI); - auto &RHSAA = - A.getAAFor(*this, IRPosition::value(*RHS)); + auto &RHSAA = A.getAAFor( + *this, IRPosition::value(*RHS, getCallBaseContext())); QuerriedAAs.push_back(&RHSAA); auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI); @@ -7213,8 +7271,8 @@ if (!OpV.getType()->isIntegerTy()) return false; - auto &OpAA = - A.getAAFor(*this, IRPosition::value(OpV)); + auto &OpAA = A.getAAFor( + *this, IRPosition::value(OpV, getCallBaseContext())); QuerriedAAs.push_back(&OpAA); T.unionAssumed( OpAA.getAssumed().castOp(CastI->getOpcode(), getState().getBitWidth())); @@ -7231,11 +7289,11 @@ if (!LHS->getType()->isIntegerTy() || !RHS->getType()->isIntegerTy()) return false; - auto &LHSAA = - A.getAAFor(*this, IRPosition::value(*LHS)); + auto &LHSAA = A.getAAFor( + *this, IRPosition::value(*LHS, getCallBaseContext())); QuerriedAAs.push_back(&LHSAA); - auto &RHSAA = - A.getAAFor(*this, IRPosition::value(*RHS)); + auto &RHSAA = A.getAAFor( + *this, IRPosition::value(*RHS, getCallBaseContext())); QuerriedAAs.push_back(&RHSAA); auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI); @@ -7366,10 +7424,16 @@ struct AAValueConstantRangeCallSiteReturned : AACallSiteReturnedFromReturned { + AAValueConstantRangeImpl, + AAValueConstantRangeImpl::StateType, + /* IntroduceCallBaseContext */ true> { AAValueConstantRangeCallSiteReturned(const IRPosition &IRP, Attributor &A) : AACallSiteReturnedFromReturned(IRP, A) {} + AAValueConstantRangeImpl, + AAValueConstantRangeImpl::StateType, + /* IntroduceCallBaseContext */ true>(IRP, + A) { + } /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { diff --git a/llvm/test/Transforms/Attributor/cb_range.ll b/llvm/test/Transforms/Attributor/cb_range.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/cb_range.ll @@ -0,0 +1,192 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; call site specific analysis is disabled + +; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM,CHECK_DISABLED,NOT_CGSCC_NPM_DISABLED,NOT_CGSCC_OPM_DISABLED,NOT_TUNIT_NPM_DISABLED,IS__TUNIT_____DISABLED,IS________OPM_DISABLED,IS__TUNIT_OPM_DISABLED + +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM,CHECK_DISABLED,NOT_CGSCC_OPM_DISABLED,NOT_CGSCC_NPM_DISABLED,NOT_TUNIT_OPM_DISABLED,IS__TUNIT_____DISABLED,IS________NPM_DISABLED,IS__TUNIT_NPM_DISABLED + +; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM,CHECK_DISABLED,NOT_TUNIT_NPM_DISABLED,NOT_TUNIT_OPM_DISABLED,NOT_CGSCC_NPM_DISABLED,IS__CGSCC_____DISABLED,IS________OPM_DISABLED,IS__CGSCC_OPM_DISABLED + +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM,CHECK_DISABLED,NOT_TUNIT_NPM_DISABLED,NOT_TUNIT_OPM_DISABLED,NOT_CGSCC_OPM_DISABLED,IS__CGSCC_____DISABLED,IS________NPM_DISABLED,IS__CGSCC_NPM_DISABLED + +; call site specific analysis is enabled + +; RUN: opt -attributor -enable-new-pm=0 -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM,CHECK_ENABLED,NOT_CGSCC_NPM_ENABLED,NOT_CGSCC_OPM_ENABLED,NOT_TUNIT_NPM_ENABLED,IS__TUNIT_____ENABLED,IS________OPM_ENABLED,IS__TUNIT_OPM_ENABLED + +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM,CHECK_ENABLED,NOT_CGSCC_OPM_ENABLED,NOT_CGSCC_NPM_ENABLED,NOT_TUNIT_OPM_ENABLED,IS__TUNIT_____ENABLED,IS________NPM_ENABLED,IS__TUNIT_NPM_ENABLED + +; RUN: opt -attributor-cgscc -attributor-enable-call-site-specific-deduction=true -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM,CHECK_ENABLED,NOT_TUNIT_NPM_ENABLED,NOT_TUNIT_OPM_ENABLED,NOT_CGSCC_NPM_ENABLED,IS__CGSCC_____ENABLED,IS________OPM_ENABLED,IS__CGSCC_OPM_ENABLED + +; Some update script generated tests fail in the test1_ncheck function. +; This is the run line that causes the fail. +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM,CHECK_ENABLED,NOT_TUNIT_NPM_ENABLED,NOT_TUNIT_OPM_ENABLED,NOT_CGSCC_OPM_ENABLED,IS__CGSCC_____ENABLED,IS________NPM_ENABLED,IS__CGSCC_NPM_ENABLED + + +define i32 @test_range(i32 %unknown) { +; CHECK-LABEL: define {{[^@]+}}@test_range +; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[UNKNOWN]], 100 +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 100, i32 0 +; CHECK-NEXT: ret i32 [[TMP2]] +; + %1 = icmp sgt i32 %unknown, 100 + %2 = select i1 %1, i32 100, i32 0 + ret i32 %2 +} + +define i32 @test1(i32 %unknown, i32 %b) { +; IS__TUNIT____-LABEL: define {{[^@]+}}@test1 +; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] { +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0:!range !.*]] +; IS__TUNIT____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]] +; IS__TUNIT____-NEXT: ret i32 [[TMP2]] +; +; IS__CGSCC____-LABEL: define {{[^@]+}}@test1 +; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] { +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1:#.*]], [[RNG0:!range !.*]] +; IS__CGSCC____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]] +; IS__CGSCC____-NEXT: ret i32 [[TMP2]] +; + %1 = call i32 @test_range(i32 %unknown) + %2 = sub nsw i32 %1, %b + ret i32 %2 +} + +define i32 @test2(i32 %unknown, i32 %b) { +; IS__TUNIT____-LABEL: define {{[^@]+}}@test2 +; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] { +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0]] +; IS__TUNIT____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]] +; IS__TUNIT____-NEXT: ret i32 [[TMP2]] +; +; IS__CGSCC____-LABEL: define {{[^@]+}}@test2 +; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] { +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1]], [[RNG0]] +; IS__CGSCC____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]] +; IS__CGSCC____-NEXT: ret i32 [[TMP2]] +; + %1 = call i32 @test_range(i32 %unknown) + %2 = add nsw i32 %1, %b + ret i32 %2 +} + +; Positive checks + +define i32 @test1_pcheck(i32 %unknown) { +; NOT_CGSCC_NPM_DISABLED-LABEL: define {{[^@]+}}@test1_pcheck +; NOT_CGSCC_NPM_DISABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 90 +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; NOT_CGSCC_NPM_DISABLED-NEXT: ret i32 [[TMP3]] +; +; IS__CGSCC_NPM_DISABLED-LABEL: define {{[^@]+}}@test1_pcheck +; IS__CGSCC_NPM_DISABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; IS__CGSCC_NPM_DISABLED-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1:#.*]], [[RNG1:!range !.*]] +; IS__CGSCC_NPM_DISABLED-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 90 +; IS__CGSCC_NPM_DISABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__CGSCC_NPM_DISABLED-NEXT: ret i32 [[TMP3]] +; +; CHECK_ENABLED-LABEL: define {{[^@]+}}@test1_pcheck +; CHECK_ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; CHECK_ENABLED-NEXT: ret i32 1 +; + %1 = call i32 @test1(i32 %unknown, i32 20) + %2 = icmp sle i32 %1, 90 + %3 = zext i1 %2 to i32 + ret i32 %3 +} + +define i32 @test2_pcheck(i32 %unknown) { +; CHECK_DISABLED-LABEL: define {{[^@]+}}@test2_pcheck +; CHECK_DISABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; CHECK_DISABLED-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) +; CHECK_DISABLED-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 20 +; CHECK_DISABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; CHECK_DISABLED-NEXT: ret i32 [[TMP3]] +; +; CHECK_ENABLED-LABEL: define {{[^@]+}}@test2_pcheck +; CHECK_ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] { +; CHECK_ENABLED-NEXT: ret i32 1 +; + %1 = call i32 @test2(i32 %unknown, i32 20) + %2 = icmp sge i32 %1, 20 + %3 = zext i1 %2 to i32 + ret i32 %3 +} + +; Negative checks + +define i32 @test1_ncheck(i32 %unknown) { +; NOT_CGSCC_NPM_DISABLED-LABEL: define {{[^@]+}}@test1_ncheck +; NOT_CGSCC_NPM_DISABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] { +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10 +; NOT_CGSCC_NPM_DISABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; NOT_CGSCC_NPM_DISABLED-NEXT: ret i32 [[TMP3]] +; + +; Test passes if you remove this check block. +; Or Manually adding a _DISABLED suffix fixes the issue. +; I added "ignore me" to the end of the check line to disable this for now. + +; IS__CGSCC_NPM-LABEL ignore me : define {{[^@]+}}@test1_ncheck +; IS__CGSCC_NPM-SAME ignore me : (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; IS__CGSCC_NPM-NEXT ignore me : [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1:#.*]], [[RNG1:!range !.*]] +; IS__CGSCC_NPM-NEXT ignore me : [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10 +; IS__CGSCC_NPM-NEXT ignore me : [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__CGSCC_NPM-NEXT ignore me : ret i32 [[TMP3]] +; +; IS__TUNIT_____ENABLED-LABEL: define {{[^@]+}}@test1_ncheck +; IS__TUNIT_____ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; IS__TUNIT_____ENABLED-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR0]], [[RNG1:!range !.*]] +; IS__TUNIT_____ENABLED-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10 +; IS__TUNIT_____ENABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__TUNIT_____ENABLED-NEXT: ret i32 [[TMP3]] +; + +; This fails to match in the last run line. +; This is because IS__CGSCC_NPM-LABEL already processed the function signiture. + +; IS__CGSCC_____ENABLED-LABEL: define {{[^@]+}}@test1_ncheck +; IS__CGSCC_____ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] { +; IS__CGSCC_____ENABLED-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1:#.*]], [[RNG1:!range !.*]] +; IS__CGSCC_____ENABLED-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10 +; IS__CGSCC_____ENABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__CGSCC_____ENABLED-NEXT: ret i32 [[TMP3]] +; + + %1 = call i32 @test1(i32 %unknown, i32 20) + %2 = icmp sle i32 %1, 10 + %3 = zext i1 %2 to i32 + ret i32 %3 +} + +define i32 @test2_ncheck(i32 %unknown) { +; CHECK_DISABLED-LABEL: define {{[^@]+}}@test2_ncheck +; CHECK_DISABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] { +; CHECK_DISABLED-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) +; CHECK_DISABLED-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30 +; CHECK_DISABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; CHECK_DISABLED-NEXT: ret i32 [[TMP3]] +; +; IS__TUNIT_____ENABLED-LABEL: define {{[^@]+}}@test2_ncheck +; IS__TUNIT_____ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] { +; IS__TUNIT_____ENABLED-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR0]], [[RNG2:!range !.*]] +; IS__TUNIT_____ENABLED-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30 +; IS__TUNIT_____ENABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__TUNIT_____ENABLED-NEXT: ret i32 [[TMP3]] +; +; IS__CGSCC_____ENABLED-LABEL: define {{[^@]+}}@test2_ncheck +; IS__CGSCC_____ENABLED-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] { +; IS__CGSCC_____ENABLED-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1]], [[RNG2:!range !.*]] +; IS__CGSCC_____ENABLED-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30 +; IS__CGSCC_____ENABLED-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32 +; IS__CGSCC_____ENABLED-NEXT: ret i32 [[TMP3]] +; + %1 = call i32 @test2(i32 %unknown, i32 20) + %2 = icmp sge i32 %1, 30 + %3 = zext i1 %2 to i32 + ret i32 %3 +} +; CHECK: !0 = !{i32 0, i32 101}