[OpenMPOpt] ICV tracking for calls

Introduce two new AAs. AAICVTrackerFunctionReturned which checks if a
function can have a unique ICV value after it is finished, and
AAICVCallSiteReturned which checks AAICVTrackerFunctionReturned for a
call site. This enables us to check the value of a call and if it
changes the ICV. This also changes the approach in
`getReplacementValues()` to a worklist-based approach so we can explore
all relevant BBs.

Differential Revision: https://reviews.llvm.org/D85544
This commit is contained in:
sstefan1 2020-08-12 12:20:53 +02:00
parent eedf18fc1f
commit b0b32e6490
3 changed files with 889 additions and 116 deletions

View File

@ -901,13 +901,15 @@ bool Attributor::checkForAllInstructions(function_ref<bool(Instruction &)> Pred,
// TODO: use the function scope once we have call site AAReturnedValues.
const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction);
const auto &LivenessAA =
getAAFor<AAIsDead>(QueryingAA, QueryIRP, /* TrackDependence */ false);
const auto *LivenessAA =
CheckBBLivenessOnly ? nullptr
: &(getAAFor<AAIsDead>(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;

View File

@ -89,43 +89,6 @@ static void foreachUse(Function &F, CBTy CB,
}
}
/// 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;
@ -1017,11 +980,28 @@ private:
/// Populate the Attributor with abstract attribute opportunities in the
/// function.
void registerAAs() {
for (Function *F : SCC) {
if (F->isDeclaration())
continue;
if (SCC.empty())
return;
A.getOrCreateAAFor<AAICVTracker>(IRPosition::function(*F));
// 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;
};
GetterRFI.foreachUse(SCC, CreateAA);
}
}
};
@ -1223,6 +1203,12 @@ struct AAICVTracker : public StateWrapper<BooleanState, AbstractAttribute> {
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(); }
@ -1233,8 +1219,21 @@ struct AAICVTracker : public StateWrapper<BooleanState, AbstractAttribute> {
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"; }
@ -1255,57 +1254,20 @@ struct AAICVTrackerFunction : public 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;
@ -1317,6 +1279,7 @@ struct AAICVTrackerFunction : public AAICVTracker {
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)
@ -1324,51 +1287,338 @@ struct AAICVTrackerFunction : public AAICVTracker {
// 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);
// 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)
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);
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;
}
// No call in between, return the value.
return ICVVal.TrackedValue;
Optional<Value *> NewReplVal = getValueForCall(A, CurrInst, ICV);
if (!NewReplVal.hasValue())
continue;
// Unknown value, track new.
if (!ReplVal.hasValue()) {
ReplVal = NewReplVal;
break;
}
// 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);
}
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;
}
}
// No value was tracked.
return nullptr;
/// 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
@ -1382,14 +1632,20 @@ AAICVTracker &AAICVTracker::createForPosition(const IRPosition &IRP,
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;
@ -1439,7 +1695,9 @@ PreservedAnalyses OpenMPOptPass::run(LazyCallGraph::SCC &C,
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);
@ -1516,7 +1774,9 @@ struct OpenMPOptLegacyPass : public CallGraphSCCPass {
*(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);

View File

@ -7,6 +7,29 @@
@.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:%.*]])
@ -105,5 +128,493 @@ define internal void @.omp_outlined..1(i32* %0, i32* %1) {
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}