diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -892,13 +892,15 @@ // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); - const auto &LivenessAA = - getAAFor(QueryingAA, QueryIRP, /* TrackDependence */ false); + const auto *LivenessAA = + CheckBBLivenessOnly ? nullptr + : &(getAAFor(QueryingAA, QueryIRP, + /* TrackDependence */ false)); auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction); if (!checkForAllInstructionsImpl(this, OpcodeInstMap, Pred, &QueryingAA, - &LivenessAA, Opcodes, CheckBBLivenessOnly)) + LivenessAA, Opcodes, CheckBBLivenessOnly)) return false; return true; diff --git a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp --- a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp +++ b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp @@ -82,43 +82,6 @@ } } -/// Helper struct to store tracked ICV values at specif instructions. -struct ICVValue { - Instruction *Inst; - Value *TrackedValue; - - ICVValue(Instruction *I, Value *Val) : Inst(I), TrackedValue(Val) {} -}; - -namespace llvm { - -// Provide DenseMapInfo for ICVValue -template <> struct DenseMapInfo { - using InstInfo = DenseMapInfo; - using ValueInfo = DenseMapInfo; - - static inline ICVValue getEmptyKey() { - return ICVValue(InstInfo::getEmptyKey(), ValueInfo::getEmptyKey()); - }; - - static inline ICVValue getTombstoneKey() { - return ICVValue(InstInfo::getTombstoneKey(), ValueInfo::getTombstoneKey()); - }; - - static unsigned getHashValue(const ICVValue &ICVVal) { - return detail::combineHashValue( - InstInfo::getHashValue(ICVVal.Inst), - ValueInfo::getHashValue(ICVVal.TrackedValue)); - } - - static bool isEqual(const ICVValue &LHS, const ICVValue &RHS) { - return InstInfo::isEqual(LHS.Inst, RHS.Inst) && - ValueInfo::isEqual(LHS.TrackedValue, RHS.TrackedValue); - } -}; - -} // end namespace llvm - namespace { struct AAICVTracker; @@ -1167,24 +1130,32 @@ static AAICVTracker &createForPosition(const IRPosition &IRP, Attributor &A); /// Return the value with which \p I can be replaced for specific \p ICV. - virtual Value *getReplacementValue(InternalControlVar ICV, - const Instruction *I, Attributor &A) = 0; + virtual Optional getReplacementValue(InternalControlVar ICV, + const Instruction *I, + Attributor &A) const = 0; + + /// Return an assumed unique ICV value if a single candidate is found. If + /// there cannot be one, return a nullptr. If it is not clear yet, return the + /// Optional::NoneType. + virtual Optional getUniqueReplacementValue(InternalControlVar ICV, + const Instruction *I, + Attributor &A) const = 0; + + /// Compute the unique ICV value, if possible, after the function is called. + virtual Optional getUniqueValue(InternalControlVar ICV, + Attributor &A) const = 0; /// Check if any value was tracked. virtual bool hasTrackedValue(InternalControlVar &ICV) const { return false; } - /// Check if any call potentially changes the ICV. - virtual bool hasUnknownCall(InternalControlVar &ICV, Attributor &A) const { - return false; - } - /// See AbstractAttribute::getName() const std::string getName() const override { return "AAICVTracker"; } /// See AbstractAttribute::getIdAddr() const char *getIdAddr() const override { return &ID; } - /// This function should return true if the type of the \p AA is AAICVTracker + /// This function should return true if the type of the \p AA is + /// AAICVTracker static bool classof(const AbstractAttribute *AA) { return (AA->getIdAddr() == &ID); } @@ -1240,26 +1211,13 @@ return Changed; } - // Map of ICV to their values at specific program point. - EnumeratedArray, InternalControlVar, - InternalControlVar::ICV___last> - ICVValuesMap; - // Map of ICV to their values at specific program point. EnumeratedArray, InternalControlVar, InternalControlVar::ICV___last> ICVReplacementValuesMap; bool hasTrackedValue(InternalControlVar &ICV) const override { - return !ICVValuesMap[ICV].empty(); - } - - bool hasUnknownCall(InternalControlVar &ICV, Attributor &A) const override { - for (BasicBlock &BB : *getAnchorScope()) - for (Instruction &I : BB) - if (callChangesICV(A, &I, ICV)) - return true; - return false; + return !ICVReplacementValuesMap[ICV].empty(); } // Currently only nthreads is being tracked. @@ -1284,7 +1242,9 @@ // FIXME: handle setters with more that 1 arguments. /// Track new value. - if (ICVValuesMap[ICV].insert(ICVValue(CI, CI->getArgOperand(0)))) + if (ICVReplacementValuesMap[ICV] + .insert(std::make_pair(CI, CI->getArgOperand(0))) + .second) HasChanged = ChangeStatus::CHANGED; return false; @@ -1296,11 +1256,13 @@ if (!CI) return false; - Value *ReplVal = getReplacementValue(ICV, CI, A); - if (ICVReplacementValuesMap[ICV] - .insert(std::make_pair(CI, ReplVal)) - .second) + Optional ReplVal = getReplacementValue(ICV, CI, A); + if (ReplVal.hasValue() && + ICVReplacementValuesMap[ICV] + .insert(std::make_pair(CI, ReplVal.getValue())) + .second) { HasChanged = ChangeStatus::CHANGED; + } assert((!ICVReplacementValuesMap[ICV].lookup(CI) || ICVReplacementValuesMap[ICV].lookup(CI) == ReplVal) && @@ -1309,8 +1271,25 @@ return false; }; + auto CheckReturnInst = [&](Instruction &I) { + Optional ReplVal = getReplacementValue(ICV, &I, A); + if (ReplVal.hasValue() && + ICVReplacementValuesMap[ICV] + .insert(std::make_pair(&I, ReplVal.getValue())) + .second) + HasChanged = ChangeStatus::CHANGED; + + return true; + }; + + // Track all changes of an ICV. SetterRFI.foreachUse(TrackValues, F); + // Map all getters to a value if possible. GetterRFI.foreachUse(MapReplacementValues, F); + // Get ICV value at every return instruction. If all return instruction + // have the same value, that is the ICV value after this function call. + A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret}, + /* CheckBBLivenessOnly */ true); } return HasChanged; @@ -1341,70 +1320,131 @@ A.getAAFor(*this, IRPosition::function(*CalledFunction)); if (ICVTrackingAA.isAssumedTracked()) - return !ICVTrackingAA.hasTrackedValue(ICV) && - ICVTrackingAA.hasUnknownCall(ICV, A); + return ICVTrackingAA.hasTrackedValue(ICV); return true; } - /// Return the value with which \p I can be replaced for specific \p ICV. - Value *getReplacementValue(InternalControlVar ICV, const Instruction *I, - Attributor &A) override { - if (ICVReplacementValuesMap[ICV].count(I)) - return ICVReplacementValuesMap[ICV].lookup(I); + Optional getUniqueReplacementValue(InternalControlVar ICV, + const Instruction *I, + Attributor &A) const override { + const auto &CB = cast(*I); + Function *CalledFunction = CB.getCalledFunction(); - const BasicBlock *CurrBB = I->getParent(); + if (CalledFunction->isDeclaration()) + return nullptr; - auto &ValuesSet = ICVValuesMap[ICV]; + const auto &ICVTrackingAA = + A.getAAFor(*this, IRPosition::function(*CalledFunction)); - for (const auto &ICVVal : ValuesSet) { - if (CurrBB == ICVVal.Inst->getParent()) { - if (!ICVVal.Inst->comesBefore(I)) - continue; + if (ICVTrackingAA.isAssumedTracked()) + return ICVTrackingAA.getUniqueValue(ICV, A); - // both instructions are in the same BB and at \p I we know the ICV - // value. - const Instruction *CurrInst = I; - while (CurrInst != ICVVal.Inst) { - // we don't yet know if a call might update an ICV. - // TODO: check callsite AA for value. - if (callChangesICV(A, CurrInst, ICV)) - return nullptr; + return nullptr; + } - CurrInst = CurrInst->getPrevNode(); - } + Optional getUniqueValue(InternalControlVar ICV, + Attributor &A) const override { + Optional UniqueICVValue; + auto CheckReturnInst = [&](Instruction &I) { + Optional ReplVal = getReplacementValue(ICV, &I, A); - // No call in between, return the value. - return ICVVal.TrackedValue; + // If we found a second ICV value there is no unique returned value. + if (UniqueICVValue.hasValue() && UniqueICVValue != ReplVal) { + UniqueICVValue = nullptr; + return false; } - auto &OMPInfoCache = static_cast(A.getInfoCache()); - auto &Explorer = OMPInfoCache.getMustBeExecutedContextExplorer(); - for (const BasicBlock *Pred : predecessors(CurrBB)) { - if (ICVVal.Inst->getParent() == Pred) { - if (!Explorer.findInContextOf(ICVVal.Inst, I)) - return nullptr; - Instruction *CurrInst = ICVVal.Inst->getNextNode(); - while (CurrInst != Pred->getTerminator()) { - // If any of the calls after the tracked value, might change the - // ICV, we don't know the value. - if (callChangesICV(A, CurrInst, ICV)) - return nullptr; - CurrInst = CurrInst->getNextNode(); + UniqueICVValue = ReplVal; + + return true; + }; + + if (!A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret}, + /* CheckBBLivenessOnly */ true)) + UniqueICVValue = nullptr; + + return UniqueICVValue; + } + + /// Return the value with which \p I can be replaced for specific \p ICV. + Optional getReplacementValue(InternalControlVar ICV, + const Instruction *I, + Attributor &A) const override { + const auto &ValuesMap = ICVReplacementValuesMap[ICV]; + if (ValuesMap.count(I)) + return ValuesMap.lookup(I); + + SmallVector Worklist; + SmallPtrSet Visited; + Worklist.push_back(I); + + Optional ReplVal; + + while (!Worklist.empty()) { + const Instruction *CurrInst = Worklist.pop_back_val(); + if (!Visited.insert(CurrInst).second) + continue; + + const BasicBlock *CurrBB = CurrInst->getParent(); + + // Go up and look for all potential setters/calls that might change the + // ICV. + while ((CurrInst = CurrInst->getPrevNode())) { + if (ValuesMap.count(CurrInst)) { + Optional NewReplVal = ValuesMap.lookup(CurrInst); + // Unknown value, track new. + if (!ReplVal.hasValue()) { + ReplVal = NewReplVal; + break; } - return ICVVal.TrackedValue; + + // If we found a new value, we can't know the icv value anymore. + if (NewReplVal.hasValue()) + if (ReplVal != NewReplVal) + return nullptr; + + break; + } + + if (!callChangesICV(A, CurrInst, ICV)) + continue; + + Optional NewReplVal = + getUniqueReplacementValue(ICV, CurrInst, A); + + // Unknown value, track new. + if (!ReplVal.hasValue()) { + ReplVal = NewReplVal; + break; } - // if any of the calls in a block that doesn't have tracked value might - // change the ICV, we don't know the value. - for (const Instruction &Inst : *Pred) - if (callChangesICV(A, &Inst, ICV)) + // If we found a new value, we can't know the icv value anymore. + if (NewReplVal.hasValue()) + if (ReplVal != NewReplVal) return nullptr; } + + // If we are in the same BB and we have a value, we are done. + if (CurrBB == I->getParent() && ReplVal.hasValue()) + return ReplVal; + + // If we are in the Entry BB and we don't have a value, we are done. + if (CurrBB == &(CurrBB->getParent()->getEntryBlock())) + if (!ReplVal.hasValue() && hasTrackedValue(ICV)) + ReplVal = nullptr; + + // Go through all predecessors and add terminators for analysis. + for (const BasicBlock *Pred : predecessors(CurrBB)) + if (const Instruction *Terminator = Pred->getTerminator()) + Worklist.push_back(Terminator); } - // No value was tracked. - return nullptr; + // Couldn't find a replacement value, ICV value unknown. + if (!ReplVal.hasValue()) + return None; + + return ReplVal.getValue(); } }; } // namespace diff --git a/llvm/test/Transforms/OpenMP/icv_tracking.ll b/llvm/test/Transforms/OpenMP/icv_tracking.ll --- a/llvm/test/Transforms/OpenMP/icv_tracking.ll +++ b/llvm/test/Transforms/OpenMP/icv_tracking.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --check-attributes ; RUN: opt -S -openmpopt < %s | FileCheck %s ; RUN: opt -S -passes=openmpopt < %s | FileCheck %s @@ -251,6 +251,84 @@ ret void } +define weak void @weak_known_unique_icv(i1 %0) { +; CHECK-LABEL: define {{[^@]+}}@weak_known_unique_icv +; CHECK-SAME: (i1 [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 2) +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i1 [[TMP0]], false +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]] +; CHECK: 3: +; CHECK-NEXT: [[TMP4:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP5]] +; CHECK: 5: +; CHECK-NEXT: [[TMP6:%.*]] = call i32 @icv_free_use(i32 2) +; CHECK-NEXT: ret void +; + call void @omp_set_num_threads(i32 2) + %2 = icmp eq i1 %0, 0 + br i1 %2, label %5, label %3 + +3: ; preds = %1 + %4 = call i32 @icv_free_use(i32 10) + br label %5 + +5: ; preds = %3, %1 + %6 = call i32 @omp_get_max_threads() + %7 = call i32 @icv_free_use(i32 %6) + ret void +} + +define void @known_unique_icv(i1 %0) { +; CHECK-LABEL: define {{[^@]+}}@known_unique_icv +; CHECK-SAME: (i1 [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 2) +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i1 [[TMP0]], false +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]] +; CHECK: 3: +; CHECK-NEXT: [[TMP4:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP5]] +; CHECK: 5: +; CHECK-NEXT: [[TMP6:%.*]] = call i32 @icv_free_use(i32 2) +; CHECK-NEXT: ret void +; + call void @omp_set_num_threads(i32 2) + %2 = icmp eq i1 %0, 0 + br i1 %2, label %5, label %3 + +3: ; preds = %1 + %4 = call i32 @icv_free_use(i32 10) + br label %5 + +5: ; preds = %3, %1 + %6 = call i32 @omp_get_max_threads() + %7 = call i32 @icv_free_use(i32 %6) + ret void +} + +define i32 @no_unique_icv(i1 %0) { +; CHECK-LABEL: define {{[^@]+}}@no_unique_icv +; CHECK-SAME: (i1 [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 4) +; CHECK-NEXT: br i1 [[TMP0]], label [[TMP3:%.*]], label [[TMP2:%.*]] +; CHECK: 2: +; CHECK-NEXT: call void @omp_set_num_threads(i32 2) +; CHECK-NEXT: br label [[TMP3]] +; CHECK: 3: +; CHECK-NEXT: [[TMP4:%.*]] = call i32 @omp_get_max_threads() +; CHECK-NEXT: ret i32 [[TMP4]] +; + call void @omp_set_num_threads(i32 4) + br i1 %0, label %3, label %2 + +2: ; preds = %1 + call void @omp_set_num_threads(i32 2) + br label %3 + +3: ; preds = %1, %2 + %4 = call i32 @omp_get_max_threads() + ret i32 %4 +} + define void @test2(i1 %0) { ; CHECK-LABEL: define {{[^@]+}}@test2 ; CHECK-SAME: (i1 [[TMP0:%.*]]) @@ -263,6 +341,32 @@ ; CHECK-NEXT: [[TMP5:%.*]] = call i32 @omp_get_max_threads() ; CHECK-NEXT: call void @use(i32 [[TMP5]]) ; CHECK-NEXT: ret void +; +%2 = icmp eq i1 %0, 0 + br i1 %2, label %4, label %3 + +3: ; preds = %1 + call void @omp_set_num_threads(i32 4) + br label %4 + +4: ; preds = %3, %1 + %5 = call i32 @omp_get_max_threads() + call void @use(i32 %5) + ret void +} + +define void @test3(i1 %0) { +; CHECK-LABEL: define {{[^@]+}}@test3 +; CHECK-SAME: (i1 [[TMP0:%.*]]) +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i1 [[TMP0]], false +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP4:%.*]], label [[TMP3:%.*]] +; CHECK: 3: +; CHECK-NEXT: call void @omp_set_num_threads(i32 4) +; CHECK-NEXT: br label [[TMP4]] +; CHECK: 4: +; CHECK-NEXT: call void @weak_known_unique_icv(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP5:%.*]] = call i32 @icv_free_use(i32 2) +; CHECK-NEXT: ret void ; %2 = icmp eq i1 %0, 0 br i1 %2, label %4, label %3 @@ -271,11 +375,244 @@ call void @omp_set_num_threads(i32 4) br label %4 +4: ; preds = %3, %1 + call void @weak_known_unique_icv(i1 %0) + %5 = call i32 @omp_get_max_threads() + %6 = call i32 @icv_free_use(i32 %5) + ret void +} + +declare void @__cxa_rethrow() + +define i32 @maybe_throw(i1 zeroext %0) { +; CHECK-LABEL: define {{[^@]+}}@maybe_throw +; CHECK-SAME: (i1 zeroext [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 4) +; CHECK-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]] +; CHECK: 2: +; CHECK-NEXT: tail call void @__cxa_rethrow() +; CHECK-NEXT: unreachable +; CHECK: 3: +; CHECK-NEXT: ret i32 -1 +; + call void @omp_set_num_threads(i32 4) + br i1 %0, label %2, label %3 + +2: ; preds = %1 + tail call void @__cxa_rethrow() #1 + unreachable + +3: ; preds = %1 + ret i32 -1 +} + +define void @test4(i1 %0) { +; CHECK-LABEL: define {{[^@]+}}@test4 +; CHECK-SAME: (i1 [[TMP0:%.*]]) +; CHECK-NEXT: call void @known_unique_icv(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i1 [[TMP0]], false +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP4:%.*]], label [[TMP3:%.*]] +; CHECK: 3: +; CHECK-NEXT: [[VAL:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP4]] +; CHECK: 4: +; CHECK-NEXT: call void @use(i32 2) +; CHECK-NEXT: [[TMP5:%.*]] = call i32 @omp_get_max_threads() +; CHECK-NEXT: [[TMP6:%.*]] = call i32 @no_unique_icv(i1 [[TMP0]]) +; CHECK-NEXT: call void @use(i32 [[TMP5]]) +; CHECK-NEXT: ret void +; + call void @known_unique_icv(i1 %0) + %2 = icmp eq i1 %0, 0 + br i1 %2, label %4, label %3 + +3: ; preds = %1 + %val = call i32 @icv_free_use(i32 10) + br label %4 + 4: ; preds = %3, %1 %5 = call i32 @omp_get_max_threads() call void @use(i32 %5) + %6 = call i32 @omp_get_max_threads() + call i32 @no_unique_icv(i1 %0) + call void @use(i32 %6) ret void } +define void @test4_invoke(i1 %0) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +; CHECK-LABEL: define {{[^@]+}}@test4_invoke +; CHECK-SAME: (i1 [[TMP0:%.*]]) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) +; CHECK-NEXT: call void @known_unique_icv(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP2:%.*]] = invoke i32 @maybe_throw(i1 [[TMP0]]) +; CHECK-NEXT: to label [[CONT:%.*]] unwind label [[EXC:%.*]] +; CHECK: cont: +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i1 [[TMP0]], false +; CHECK-NEXT: br i1 [[TMP3]], label [[TMP5:%.*]], label [[TMP4:%.*]] +; CHECK: exc: +; CHECK-NEXT: [[LP:%.*]] = landingpad { i8*, i32 } +; CHECK-NEXT: filter [0 x i8*] zeroinitializer +; CHECK-NEXT: unreachable +; CHECK: 4: +; CHECK-NEXT: [[VAL:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP5]] +; CHECK: 5: +; CHECK-NEXT: call void @use(i32 2) +; CHECK-NEXT: ret void +; + call void @known_unique_icv(i1 %0) + invoke i32 @maybe_throw(i1 %0) + to label %cont unwind label %exc + +cont: + %3 = icmp eq i1 %0, 0 + br i1 %3, label %5, label %4 + +exc: + %lp = landingpad { i8*, i32 } + filter [0 x i8*] zeroinitializer + unreachable + +4: ; preds = %1 + %val = call i32 @icv_free_use(i32 10) + br label %5 + +5: ; preds = %3, %1 + %6 = call i32 @omp_get_max_threads() + call void @use(i32 %6) + ret void +} + +define i32 @test5(i32 %0) #0 { +; CHECK-LABEL: define {{[^@]+}}@test5 +; CHECK-SAME: (i32 [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 4) +; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[TMP0]], 3 +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]] +; CHECK: 3: +; CHECK-NEXT: call void @use(i32 4) +; CHECK-NEXT: br label [[TMP12:%.*]] +; CHECK: 4: +; CHECK-NEXT: [[TMP5:%.*]] = icmp sgt i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP8:%.*]] +; CHECK: 6: +; CHECK-NEXT: [[TMP7:%.*]] = call i32 @icv_free_use(i32 [[TMP0]]) +; CHECK-NEXT: br label [[TMP15:%.*]] +; CHECK: 8: +; CHECK-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TMP9]], label [[TMP10:%.*]], label [[TMP12]] +; CHECK: 10: +; CHECK-NEXT: [[TMP11:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP15]] +; CHECK: 12: +; CHECK-NEXT: [[TMP13:%.*]] = add nsw i32 [[TMP0]], 1 +; CHECK-NEXT: [[TMP14:%.*]] = call i32 @icv_free_use(i32 [[TMP13]]) +; CHECK-NEXT: br label [[TMP15]] +; CHECK: 15: +; CHECK-NEXT: [[TMP16:%.*]] = call i32 @omp_get_max_threads() +; CHECK-NEXT: [[TMP17:%.*]] = call i32 @icv_free_use(i32 [[TMP16]]) +; CHECK-NEXT: ret i32 [[TMP17]] +; + call void @omp_set_num_threads(i32 4) + %2 = icmp sgt i32 %0, 3 + br i1 %2, label %3, label %5 + +3: + %4 = call i32 @omp_get_max_threads() + call void @use(i32 %4) + br label %13 + +5: + %6 = icmp sgt i32 %0, 0 + br i1 %6, label %7, label %9 + +7: + %8 = call i32 @icv_free_use(i32 %0) + br label %16 + +9: + %10 = icmp eq i32 %0, 0 + br i1 %10, label %11, label %13 + +11: + %12 = call i32 @icv_free_use(i32 10) + br label %16 + +13: + %14 = add nsw i32 %0, 1 + %15 = call i32 @icv_free_use(i32 %14) + br label %16 + +16: + %17 = call i32 @omp_get_max_threads() + %18 = call i32 @icv_free_use(i32 %17) + ret i32 %18 +} + +define i32 @test6(i32 %0) { +; CHECK-LABEL: define {{[^@]+}}@test6 +; CHECK-SAME: (i32 [[TMP0:%.*]]) +; CHECK-NEXT: call void @omp_set_num_threads(i32 4) +; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[TMP0]], 3 +; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] +; CHECK: 3: +; CHECK-NEXT: [[TMP4:%.*]] = call i32 @icv_free_use(i32 10) +; CHECK-NEXT: br label [[TMP16:%.*]] +; CHECK: 5: +; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TMP6]], label [[TMP7:%.*]], label [[TMP9:%.*]] +; CHECK: 7: +; CHECK-NEXT: [[TMP8:%.*]] = call i32 @icv_free_use(i32 [[TMP0]]) +; CHECK-NEXT: br label [[TMP16]] +; CHECK: 9: +; CHECK-NEXT: [[TMP10:%.*]] = icmp eq i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP13:%.*]] +; CHECK: 11: +; CHECK-NEXT: [[TMP12:%.*]] = call i32 @icv_free_use(i32 5) +; CHECK-NEXT: br label [[TMP16]] +; CHECK: 13: +; CHECK-NEXT: [[TMP14:%.*]] = add nsw i32 [[TMP0]], 1 +; CHECK-NEXT: [[TMP15:%.*]] = call i32 @icv_free_use(i32 [[TMP14]]) +; CHECK-NEXT: br label [[TMP16]] +; CHECK: 16: +; CHECK-NEXT: [[TMP17:%.*]] = call i32 @icv_free_use(i32 4) +; CHECK-NEXT: ret i32 [[TMP17]] +; + call void @omp_set_num_threads(i32 4) + %2 = icmp sgt i32 %0, 3 + br i1 %2, label %3, label %5 + +3: ; preds = %1 + %4 = call i32 @icv_free_use(i32 10) + br label %16 + +5: ; preds = %1 + %6 = icmp sgt i32 %0, 0 + br i1 %6, label %7, label %9 + +7: ; preds = %5 + %8 = call i32 @icv_free_use(i32 %0) + br label %16 + +9: ; preds = %5 + %10 = icmp eq i32 %0, 0 + br i1 %10, label %11, label %13 + +11: ; preds = %9 + %12 = call i32 @icv_free_use(i32 5) + br label %16 + +13: ; preds = %9 + %14 = add nsw i32 %0, 1 + %15 = call i32 @icv_free_use(i32 %14) + br label %16 + +16: ; preds = %7, %13, %11, %3 + %17 = call i32 @omp_get_max_threads() + %18 = call i32 @icv_free_use(i32 %17) + ret i32 %18 +} + +declare i32 @__gxx_personality_v0(...) + !0 = !{!1} !1 = !{i64 2, i64 -1, i64 -1, i1 true}