}
}
-/// 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<ICVValue> {
- using InstInfo = DenseMapInfo<Instruction *>;
- using ValueInfo = DenseMapInfo<Value *>;
-
- 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;
/// Populate the Attributor with abstract attribute opportunities in the
/// function.
void registerAAs() {
- for (Function *F : SCC) {
- if (F->isDeclaration())
- continue;
+ if (SCC.empty())
+ return;
+
+ // Create CallSite AA for all Getters.
+ for (int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) {
+ auto ICVInfo = OMPInfoCache.ICVs[static_cast<InternalControlVar>(Idx)];
+
+ auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter];
+
+ auto CreateAA = [&](Use &U, Function &Caller) {
+ CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI);
+ if (!CI)
+ return false;
+
+ auto &CB = cast<CallBase>(*CI);
+
+ IRPosition CBPos = IRPosition::callsite_function(CB);
+ A.getOrCreateAAFor<AAICVTracker>(CBPos);
+ return false;
+ };
- A.getOrCreateAAFor<AAICVTracker>(IRPosition::function(*F));
+ GetterRFI.foreachUse(SCC, CreateAA);
}
}
};
using Base = StateWrapper<BooleanState, AbstractAttribute>;
AAICVTracker(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
+ void initialize(Attributor &A) override {
+ Function *F = getAnchorScope();
+ if (!F || !A.isFunctionIPOAmendable(*F))
+ indicatePessimisticFixpoint();
+ }
+
/// Returns true if value is assumed to be tracked.
bool isAssumedTracked() const { return getAssumed(); }
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<Value *> getReplacementValue(InternalControlVar ICV,
+ const Instruction *I,
+ Attributor &A) const {
+ return None;
+ }
+
+ /// 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<Value *>
+ getUniqueReplacementValue(InternalControlVar ICV) const = 0;
+
+ // Currently only nthreads is being tracked.
+ // this array will only grow with time.
+ InternalControlVar TrackableICVs[1] = {ICV_nthreads};
/// See AbstractAttribute::getName()
const std::string getName() const override { return "AAICVTracker"; }
: AAICVTracker(IRP, A) {}
// FIXME: come up with better string.
- const std::string getAsStr() const override { return "ICVTracker"; }
+ const std::string getAsStr() const override { return "ICVTrackerFunction"; }
// FIXME: come up with some stats.
void trackStatistics() const override {}
- /// TODO: decide whether to deduplicate here, or use current
- /// deduplicateRuntimeCalls function.
+ /// We don't manifest anything for this AA.
ChangeStatus manifest(Attributor &A) override {
- ChangeStatus Changed = ChangeStatus::UNCHANGED;
-
- for (InternalControlVar &ICV : TrackableICVs)
- if (deduplicateICVGetters(ICV, A))
- Changed = ChangeStatus::CHANGED;
-
- return Changed;
- }
-
- bool deduplicateICVGetters(InternalControlVar &ICV, Attributor &A) {
- auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache());
- auto &ICVInfo = OMPInfoCache.ICVs[ICV];
- auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter];
-
- bool Changed = false;
-
- auto ReplaceAndDeleteCB = [&](Use &U, Function &Caller) {
- CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI);
- Instruction *UserI = cast<Instruction>(U.getUser());
- Value *ReplVal = getReplacementValue(ICV, UserI, A);
-
- if (!ReplVal || !CI)
- return false;
-
- A.removeCallSite(CI);
- CI->replaceAllUsesWith(ReplVal);
- CI->eraseFromParent();
- Changed = true;
- return true;
- };
-
- GetterRFI.foreachUse(ReplaceAndDeleteCB, getAnchorScope());
- return Changed;
+ return ChangeStatus::UNCHANGED;
}
// Map of ICV to their values at specific program point.
- EnumeratedArray<SmallSetVector<ICVValue, 4>, InternalControlVar,
+ EnumeratedArray<DenseMap<Instruction *, Value *>, InternalControlVar,
InternalControlVar::ICV___last>
- ICVValuesMap;
-
- // Currently only nthreads is being tracked.
- // this array will only grow with time.
- InternalControlVar TrackableICVs[1] = {ICV_nthreads};
+ ICVReplacementValuesMap;
ChangeStatus updateImpl(Attributor &A) override {
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
for (InternalControlVar ICV : TrackableICVs) {
auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
+ auto &ValuesMap = ICVReplacementValuesMap[ICV];
auto TrackValues = [&](Use &U, Function &) {
CallInst *CI = OpenMPOpt::getCallIfRegularCall(U);
if (!CI)
// FIXME: handle setters with more that 1 arguments.
/// Track new value.
- if (ICVValuesMap[ICV].insert(ICVValue(CI, CI->getArgOperand(0))))
+ if (ValuesMap.insert(std::make_pair(CI, CI->getArgOperand(0))).second)
HasChanged = ChangeStatus::CHANGED;
return false;
};
+ auto CallCheck = [&](Instruction &I) {
+ Optional<Value *> ReplVal = getValueForCall(A, &I, ICV);
+ if (ReplVal.hasValue() &&
+ ValuesMap.insert(std::make_pair(&I, *ReplVal)).second)
+ HasChanged = ChangeStatus::CHANGED;
+
+ return true;
+ };
+
+ // Track all changes of an ICV.
SetterRFI.foreachUse(TrackValues, F);
+
+ A.checkForAllInstructions(CallCheck, *this, {Instruction::Call},
+ /* CheckBBLivenessOnly */ true);
+
+ /// TODO: Figure out a way to avoid adding entry in
+ /// ICVReplacementValuesMap
+ Instruction *Entry = &F->getEntryBlock().front();
+ if (HasChanged == ChangeStatus::CHANGED && !ValuesMap.count(Entry))
+ ValuesMap.insert(std::make_pair(Entry, nullptr));
}
return HasChanged;
}
- /// Return the value with which \p I can be replaced for specific \p ICV.
- Value *getReplacementValue(InternalControlVar ICV, const Instruction *I,
- Attributor &A) override {
- const BasicBlock *CurrBB = I->getParent();
+ /// Hepler to check if \p I is a call and get the value for it if it is
+ /// unique.
+ Optional<Value *> getValueForCall(Attributor &A, const Instruction *I,
+ InternalControlVar &ICV) const {
+
+ const auto *CB = dyn_cast<CallBase>(I);
+ if (!CB)
+ return None;
- auto &ValuesSet = ICVValuesMap[ICV];
auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache());
auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter];
+ auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
+ Function *CalledFunction = CB->getCalledFunction();
- for (const auto &ICVVal : ValuesSet) {
- if (CurrBB == ICVVal.Inst->getParent()) {
- if (!ICVVal.Inst->comesBefore(I))
- continue;
+ if (CalledFunction == GetterRFI.Declaration)
+ return None;
+ if (CalledFunction == SetterRFI.Declaration) {
+ if (ICVReplacementValuesMap[ICV].count(I))
+ return ICVReplacementValuesMap[ICV].lookup(I);
+
+ return nullptr;
+ }
+
+ // Since we don't know, assume it changes the ICV.
+ if (CalledFunction->isDeclaration())
+ return nullptr;
+
+ const auto &ICVTrackingAA =
+ A.getAAFor<AAICVTracker>(*this, IRPosition::callsite_returned(*CB));
+
+ if (ICVTrackingAA.isAssumedTracked())
+ return ICVTrackingAA.getUniqueReplacementValue(ICV);
+
+ // If we don't know, assume it changes.
+ return nullptr;
+ }
+
+ // We don't check unique value for a function, so return None.
+ Optional<Value *>
+ getUniqueReplacementValue(InternalControlVar ICV) const override {
+ return None;
+ }
+
+ /// Return the value with which \p I can be replaced for specific \p ICV.
+ Optional<Value *> getReplacementValue(InternalControlVar ICV,
+ const Instruction *I,
+ Attributor &A) const override {
+ const auto &ValuesMap = ICVReplacementValuesMap[ICV];
+ if (ValuesMap.count(I))
+ return ValuesMap.lookup(I);
+
+ SmallVector<const Instruction *, 16> Worklist;
+ SmallPtrSet<const Instruction *, 16> Visited;
+ Worklist.push_back(I);
- // both instructions are in the same BB and at \p I we know the ICV
- // value.
- while (I != ICVVal.Inst) {
- // we don't yet know if a call might update an ICV.
- // TODO: check callsite AA for value.
- if (const auto *CB = dyn_cast<CallBase>(I))
- if (CB->getCalledFunction() != GetterRFI.Declaration)
+ Optional<Value *> 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<Value *> NewReplVal = ValuesMap.lookup(CurrInst);
+ // Unknown value, track new.
+ if (!ReplVal.hasValue()) {
+ ReplVal = NewReplVal;
+ break;
+ }
+
+ // If we found a new value, we can't know the icv value anymore.
+ if (NewReplVal.hasValue())
+ if (ReplVal != NewReplVal)
return nullptr;
- I = I->getPrevNode();
+ break;
+ }
+
+ Optional<Value *> NewReplVal = getValueForCall(A, CurrInst, ICV);
+ if (!NewReplVal.hasValue())
+ continue;
+
+ // Unknown value, track new.
+ if (!ReplVal.hasValue()) {
+ ReplVal = NewReplVal;
+ break;
}
- // No call in between, return the value.
- return ICVVal.TrackedValue;
+ // if (NewReplVal.hasValue())
+ // We found a new value, we can't know the icv value anymore.
+ 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;
+
+ // 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;
+ return ReplVal;
+ }
+};
+
+struct AAICVTrackerFunctionReturned : AAICVTracker {
+ AAICVTrackerFunctionReturned(const IRPosition &IRP, Attributor &A)
+ : AAICVTracker(IRP, A) {}
+
+ // FIXME: come up with better string.
+ const std::string getAsStr() const override {
+ return "ICVTrackerFunctionReturned";
+ }
+
+ // FIXME: come up with some stats.
+ void trackStatistics() const override {}
+
+ /// We don't manifest anything for this AA.
+ ChangeStatus manifest(Attributor &A) override {
+ return ChangeStatus::UNCHANGED;
+ }
+
+ // Map of ICV to their values at specific program point.
+ EnumeratedArray<Optional<Value *>, InternalControlVar,
+ InternalControlVar::ICV___last>
+ ICVReplacementValuesMap;
+
+ /// Return the value with which \p I can be replaced for specific \p ICV.
+ Optional<Value *>
+ getUniqueReplacementValue(InternalControlVar ICV) const override {
+ return ICVReplacementValuesMap[ICV];
+ }
+
+ ChangeStatus updateImpl(Attributor &A) override {
+ ChangeStatus Changed = ChangeStatus::UNCHANGED;
+ const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>(
+ *this, IRPosition::function(*getAnchorScope()));
+
+ if (!ICVTrackingAA.isAssumedTracked())
+ return indicatePessimisticFixpoint();
+
+ for (InternalControlVar ICV : TrackableICVs) {
+ Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
+ Optional<Value *> UniqueICVValue;
+
+ auto CheckReturnInst = [&](Instruction &I) {
+ Optional<Value *> NewReplVal =
+ ICVTrackingAA.getReplacementValue(ICV, &I, A);
+
+ // If we found a second ICV value there is no unique returned value.
+ if (UniqueICVValue.hasValue() && UniqueICVValue != NewReplVal)
+ return false;
+
+ UniqueICVValue = NewReplVal;
+
+ return true;
+ };
+
+ if (!A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret},
+ /* CheckBBLivenessOnly */ true))
+ UniqueICVValue = nullptr;
+
+ if (UniqueICVValue == ReplVal)
+ continue;
+
+ ReplVal = UniqueICVValue;
+ Changed = ChangeStatus::CHANGED;
+ }
+
+ return Changed;
+ }
+};
+
+struct AAICVTrackerCallSite : AAICVTracker {
+ AAICVTrackerCallSite(const IRPosition &IRP, Attributor &A)
+ : AAICVTracker(IRP, A) {}
+
+ void initialize(Attributor &A) override {
+ Function *F = getAnchorScope();
+ if (!F || !A.isFunctionIPOAmendable(*F))
+ indicatePessimisticFixpoint();
+
+ // We only initialize this AA for getters, so we need to know which ICV it
+ // gets.
+ auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache());
+ for (InternalControlVar ICV : TrackableICVs) {
+ auto ICVInfo = OMPInfoCache.ICVs[ICV];
+ auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter];
+ if (Getter.Declaration == getAssociatedFunction()) {
+ AssociatedICV = ICVInfo.Kind;
+ return;
+ }
+ }
+
+ /// Unknown ICV.
+ indicatePessimisticFixpoint();
+ }
+
+ ChangeStatus manifest(Attributor &A) override {
+ if (!ReplVal.hasValue() || !ReplVal.getValue())
+ return ChangeStatus::UNCHANGED;
+
+ A.changeValueAfterManifest(*getCtxI(), **ReplVal);
+ A.deleteAfterManifest(*getCtxI());
+
+ return ChangeStatus::CHANGED;
+ }
+
+ // FIXME: come up with better string.
+ const std::string getAsStr() const override { return "ICVTrackerCallSite"; }
+
+ // FIXME: come up with some stats.
+ void trackStatistics() const override {}
+
+ InternalControlVar AssociatedICV;
+ Optional<Value *> ReplVal;
+
+ ChangeStatus updateImpl(Attributor &A) override {
+ const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>(
+ *this, IRPosition::function(*getAnchorScope()));
+
+ // We don't have any information, so we assume it changes the ICV.
+ if (!ICVTrackingAA.isAssumedTracked())
+ return indicatePessimisticFixpoint();
+
+ Optional<Value *> NewReplVal =
+ ICVTrackingAA.getReplacementValue(AssociatedICV, getCtxI(), A);
+
+ if (ReplVal == NewReplVal)
+ return ChangeStatus::UNCHANGED;
+
+ ReplVal = NewReplVal;
+ return ChangeStatus::CHANGED;
+ }
+
+ // Return the value with which associated value can be replaced for specific
+ // \p ICV.
+ Optional<Value *>
+ getUniqueReplacementValue(InternalControlVar ICV) const override {
+ return ReplVal;
+ }
+};
+
+struct AAICVTrackerCallSiteReturned : AAICVTracker {
+ AAICVTrackerCallSiteReturned(const IRPosition &IRP, Attributor &A)
+ : AAICVTracker(IRP, A) {}
+
+ // FIXME: come up with better string.
+ const std::string getAsStr() const override {
+ return "ICVTrackerCallSiteReturned";
+ }
+
+ // FIXME: come up with some stats.
+ void trackStatistics() const override {}
+
+ /// We don't manifest anything for this AA.
+ ChangeStatus manifest(Attributor &A) override {
+ return ChangeStatus::UNCHANGED;
+ }
+
+ // Map of ICV to their values at specific program point.
+ EnumeratedArray<Optional<Value *>, InternalControlVar,
+ InternalControlVar::ICV___last>
+ ICVReplacementValuesMap;
+
+ /// Return the value with which associated value can be replaced for specific
+ /// \p ICV.
+ Optional<Value *>
+ getUniqueReplacementValue(InternalControlVar ICV) const override {
+ return ICVReplacementValuesMap[ICV];
+ }
+
+ ChangeStatus updateImpl(Attributor &A) override {
+ ChangeStatus Changed = ChangeStatus::UNCHANGED;
+ const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>(
+ *this, IRPosition::returned(*getAssociatedFunction()));
+
+ // We don't have any information, so we assume it changes the ICV.
+ if (!ICVTrackingAA.isAssumedTracked())
+ return indicatePessimisticFixpoint();
+
+ for (InternalControlVar ICV : TrackableICVs) {
+ Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
+ Optional<Value *> NewReplVal =
+ ICVTrackingAA.getUniqueReplacementValue(ICV);
+
+ if (ReplVal == NewReplVal)
+ continue;
+
+ ReplVal = NewReplVal;
+ Changed = ChangeStatus::CHANGED;
+ }
+ return Changed;
}
};
} // namespace
case IRPosition::IRP_INVALID:
case IRPosition::IRP_FLOAT:
case IRPosition::IRP_ARGUMENT:
- case IRPosition::IRP_RETURNED:
- case IRPosition::IRP_CALL_SITE_RETURNED:
case IRPosition::IRP_CALL_SITE_ARGUMENT:
- case IRPosition::IRP_CALL_SITE:
- llvm_unreachable("ICVTracker can only be created for function position!");
+ llvm_unreachable("ICVTracker: invalid IRPosition!");
case IRPosition::IRP_FUNCTION:
AA = new (A.Allocator) AAICVTrackerFunction(IRP, A);
break;
+ case IRPosition::IRP_RETURNED:
+ AA = new (A.Allocator) AAICVTrackerFunctionReturned(IRP, A);
+ break;
+ case IRPosition::IRP_CALL_SITE_RETURNED:
+ AA = new (A.Allocator) AAICVTrackerCallSiteReturned(IRP, A);
+ break;
+ case IRPosition::IRP_CALL_SITE:
+ AA = new (A.Allocator) AAICVTrackerCallSite(IRP, A);
+ break;
}
return *AA;
OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator,
/*CGSCC*/ Functions, OMPInModule.getKernels());
- Attributor A(Functions, InfoCache, CGUpdater);
+ SetVector<Function *> ModuleSlice(InfoCache.ModuleSlice.begin(),
+ InfoCache.ModuleSlice.end());
+ Attributor A(ModuleSlice, InfoCache, CGUpdater);
// TODO: Compute the module slice we are allowed to look at.
OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A);
*(Functions.back()->getParent()), AG, Allocator,
/*CGSCC*/ Functions, OMPInModule.getKernels());
- Attributor A(Functions, InfoCache, CGUpdater);
+ SetVector<Function *> ModuleSlice(InfoCache.ModuleSlice.begin(),
+ InfoCache.ModuleSlice.end());
+ Attributor A(ModuleSlice, InfoCache, CGUpdater);
// TODO: Compute the module slice we are allowed to look at.
OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A);
@.str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
@0 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8
+; doesn't modify any ICVs.
+define i32 @icv_free_use(i32 %0) {
+; CHECK-LABEL: define {{[^@]+}}@icv_free_use
+; CHECK-SAME: (i32 [[TMP0:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP0]], 1
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %2 = add nsw i32 %0, 1
+ ret i32 %2
+}
+
+define i32 @bad_use(i32 %0) {
+; CHECK-LABEL: define {{[^@]+}}@bad_use
+; CHECK-SAME: (i32 [[TMP0:%.*]])
+; CHECK-NEXT: tail call void @use(i32 [[TMP0]])
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP0]], 1
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ tail call void @use(i32 %0)
+ %2 = add nsw i32 %0, 1
+ ret i32 %2
+}
+
define dso_local i32 @foo(i32 %0, i32 %1) {
; CHECK-LABEL: define {{[^@]+}}@foo
; CHECK-SAME: (i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
ret void
}
+define dso_local i32 @bar1(i32 %0, i32 %1) {
+; CHECK-LABEL: define {{[^@]+}}@bar1
+; CHECK-SAME: (i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT: tail call void @omp_set_num_threads(i32 [[TMP4]])
+; CHECK-NEXT: tail call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @0, i32 0, void (i32*, i32*, ...)* bitcast (void (i32*, i32*)* @.omp_outlined..2 to void (i32*, i32*, ...)*))
+; CHECK-NEXT: [[TMP5:%.*]] = tail call i32 @omp_get_max_threads()
+; CHECK-NEXT: tail call void @use(i32 [[TMP5]])
+; CHECK-NEXT: ret i32 0
+;
+ %3 = icmp sgt i32 %0, %1
+ %4 = select i1 %3, i32 %0, i32 %1
+ tail call void @omp_set_num_threads(i32 %4)
+ %5 = tail call i32 @omp_get_max_threads()
+ tail call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @0, i32 0, void (i32*, i32*, ...)* bitcast (void (i32*, i32*)* @.omp_outlined..2 to void (i32*, i32*, ...)*))
+ %6 = tail call i32 @omp_get_max_threads()
+ tail call void @use(i32 %6)
+ ret i32 0
+}
+
+define internal void @.omp_outlined..2(i32* %0, i32* %1) {
+; CHECK-LABEL: define {{[^@]+}}@.omp_outlined..2
+; CHECK-SAME: (i32* [[TMP0:%.*]], i32* [[TMP1:%.*]])
+; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @omp_get_max_threads()
+; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @icv_free_use(i32 [[TMP3]])
+; CHECK-NEXT: tail call void @omp_set_num_threads(i32 10)
+; CHECK-NEXT: [[TMP5:%.*]] = tail call i32 @icv_free_use(i32 10)
+; CHECK-NEXT: [[TMP6:%.*]] = tail call i32 @icv_free_use(i32 10)
+; CHECK-NEXT: ret void
+;
+ %3 = tail call i32 @omp_get_max_threads()
+ %4 = tail call i32 @icv_free_use(i32 %3)
+ tail call void @omp_set_num_threads(i32 10)
+ %5 = tail call i32 @omp_get_max_threads()
+ %6 = tail call i32 @icv_free_use(i32 %5)
+ %7 = tail call i32 @omp_get_max_threads()
+ %8 = tail call i32 @icv_free_use(i32 %7)
+ ret void
+}
+define void @test(i1 %0) {
+; CHECK-LABEL: define {{[^@]+}}@test
+; 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 [[TMP4:%.*]], label [[TMP3:%.*]]
+; CHECK: 3:
+; CHECK-NEXT: call void @use(i32 10)
+; CHECK-NEXT: br label [[TMP4]]
+; CHECK: 4:
+; CHECK-NEXT: [[TMP5:%.*]] = call i32 @omp_get_max_threads()
+; CHECK-NEXT: call void @use(i32 [[TMP5]])
+; CHECK-NEXT: ret void
+;
+ call void @omp_set_num_threads(i32 2)
+ %2 = icmp eq i1 %0, 0
+ br i1 %2, label %4, label %3
+
+3: ; preds = %1
+ call void @use(i32 10)
+ br label %4
+
+4: ; preds = %3, %1
+ %5 = call i32 @omp_get_max_threads()
+ call void @use(i32 %5)
+ ret void
+}
+
+define void @test1(i1 %0) {
+; CHECK-LABEL: define {{[^@]+}}@test1
+; 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: call void @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()
+ call void @use(i32 %6)
+ ret void
+}
+
+define void @bad_use_test(i1 %0) {
+; CHECK-LABEL: define {{[^@]+}}@bad_use_test
+; 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 @bad_use(i32 10)
+; CHECK-NEXT: br label [[TMP5]]
+; CHECK: 5:
+; CHECK-NEXT: [[TMP6:%.*]] = call i32 @omp_get_max_threads()
+; CHECK-NEXT: call void @use(i32 [[TMP6]])
+; 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 @bad_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 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 @omp_get_max_threads()
+; CHECK-NEXT: [[TMP7:%.*]] = call i32 @icv_free_use(i32 [[TMP6]])
+; 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:%.*]])
+; 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: [[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 @omp_get_max_threads()
+; CHECK-NEXT: [[TMP6:%.*]] = call i32 @icv_free_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
+ 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}