Skip to content

Commit 3b9677e

Browse files
committed
[Polly] Track defined behavior for PHI predecessor computation.
ZoneAlgorithms's computePHI relies on being provided with consistent a schedule to compute the statement prodecessors of a statement containing PHINodes. Otherwise unexpected results such as PHI nodes with multiple predecessors can occur which would result in problems in the algorithms expecting consistent data. In the added test case, statement instances are scrubbed from the SCoP their execution would result in undefined behavior (Due to a nsw overflow). As already being undefined behavior in LLVM-IR, neither AssumedContext nor InvalidContext are updated, giving computePHI no means to avoid these cases. Intoduce a new SCoP property, the DefinedBehaviorContext, that among the runtime-checked conditions, also tracks the assumptions not needing a runtime check, in particular those affecting the assumed control flow. This replaces the manual combination of the 3 other contexts that was already done in computePHI and setNewAccessRelation. Currently, the only additional assumption is that loop induction variables will nsw flag for not wrap, but potentially more can be added. Use in hasFeasibleRuntimeContext, isl::ast_build and gisting are other potential uses. To limit computational complexity, the DefinedBehaviorContext is not availabe if it grows too large (atm hardcoded to 8 disjuncts). Possible other fixes include bailing out in computePHI when inconsistencies are detected, choose an arbitrary value for inconsistent cases (since it is undefined behavior anyways), or make the code receiving the result from ComputePHI handle inconsistent data. All of them reduce the quality of implementation having to bail out more often and disabling the ability to assert on actually wrong results. This fixes llvm.org/PR48783.
1 parent 02e8a5a commit 3b9677e

32 files changed

+252
-56
lines changed

polly/include/polly/ScopInfo.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,22 @@ class Scop {
18051805
/// need to be "false". Otherwise they behave the same.
18061806
isl::set InvalidContext;
18071807

1808+
/// The context under which the SCoP must have defined behavior. Optimizer and
1809+
/// code generator can assume that the SCoP will only be executed with
1810+
/// parameter values within this context. This might be either because we can
1811+
/// prove that other values are impossible or explicitly have undefined
1812+
/// behavior, such as due to no-wrap flags. If this becomes too complex, can
1813+
/// also be nullptr.
1814+
///
1815+
/// In contrast to Scop::AssumedContext and Scop::InvalidContext, these do not
1816+
/// need to be checked at runtime.
1817+
///
1818+
/// Scop::Context on the other side is an overapproximation and does not
1819+
/// include all requirements, but is always defined. However, there is still
1820+
/// no guarantee that there is no undefined behavior in
1821+
/// DefinedBehaviorContext.
1822+
isl::set DefinedBehaviorContext;
1823+
18081824
/// The schedule of the SCoP
18091825
///
18101826
/// The schedule of the SCoP describes the execution order of the statements
@@ -2200,6 +2216,19 @@ class Scop {
22002216
/// @return The constraint on parameter of this Scop.
22012217
isl::set getContext() const;
22022218

2219+
/// Return the context where execution behavior is defined. Might return
2220+
/// nullptr.
2221+
isl::set getDefinedBehaviorContext() const { return DefinedBehaviorContext; }
2222+
2223+
/// Return the define behavior context, or if not available, its approximation
2224+
/// from all other contexts.
2225+
isl::set getBestKnownDefinedBehaviorContext() const {
2226+
if (DefinedBehaviorContext)
2227+
return DefinedBehaviorContext;
2228+
2229+
return Context.intersect_params(AssumedContext).subtract(InvalidContext);
2230+
}
2231+
22032232
/// Return space of isl context parameters.
22042233
///
22052234
/// Returns the set of context parameters that are currently constrained. In
@@ -2254,6 +2283,10 @@ class Scop {
22542283
bool trackAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
22552284
AssumptionSign Sign, BasicBlock *BB);
22562285

2286+
/// Add the conditions from @p Set (or subtract them if @p Sign is
2287+
/// AS_RESTRICTION) to the defined behaviour context.
2288+
void intersectDefinedBehavior(isl::set Set, AssumptionSign Sign);
2289+
22572290
/// Add assumptions to assumed context.
22582291
///
22592292
/// The assumptions added will be assumed to hold during the execution of the
@@ -2272,8 +2305,9 @@ class Scop {
22722305
/// (needed/assumptions) or negative (invalid/restrictions).
22732306
/// @param BB The block in which this assumption was taken. Used to
22742307
/// calculate hotness when emitting remark.
2308+
/// @param RTC Does the assumption require a runtime check?
22752309
void addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
2276-
AssumptionSign Sign, BasicBlock *BB);
2310+
AssumptionSign Sign, BasicBlock *BB, bool RTC = true);
22772311

22782312
/// Mark the scop as invalid.
22792313
///

polly/include/polly/Support/ScopHelper.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ struct Assumption {
6868

6969
/// An optional block whose domain can simplify the assumption.
7070
llvm::BasicBlock *BB;
71+
72+
// Whether the assumption must be checked at runtime.
73+
bool RequiresRTC;
7174
};
7275

7376
using RecordedAssumptionsTy = llvm::SmallVector<Assumption, 8>;
@@ -88,9 +91,11 @@ using RecordedAssumptionsTy = llvm::SmallVector<Assumption, 8>;
8891
/// set, the domain of that block will be used to simplify the
8992
/// actual assumption in @p Set once it is added. This is useful
9093
/// if the assumption was created prior to the domain.
94+
/// @param RTC Does the assumption require a runtime check?
9195
void recordAssumption(RecordedAssumptionsTy *RecordedAssumptions,
9296
AssumptionKind Kind, isl::set Set, llvm::DebugLoc Loc,
93-
AssumptionSign Sign, llvm::BasicBlock *BB = nullptr);
97+
AssumptionSign Sign, llvm::BasicBlock *BB = nullptr,
98+
bool RTC = true);
9499

95100
/// Type to remap values.
96101
using ValueMapT = llvm::DenseMap<llvm::AssertingVH<llvm::Value>,

polly/lib/Analysis/ScopBuilder.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -794,15 +794,15 @@ bool ScopBuilder::addLoopBoundsToHeaderDomain(
794794
auto Parts = partitionSetParts(HeaderBBDom, LoopDepth);
795795
HeaderBBDom = Parts.second;
796796

797-
// Check if there is a <nsw> tagged AddRec for this loop and if so do not add
798-
// the bounded assumptions to the context as they are already implied by the
799-
// <nsw> tag.
800-
if (scop->hasNSWAddRecForLoop(L))
801-
return true;
797+
// Check if there is a <nsw> tagged AddRec for this loop and if so do not
798+
// require a runtime check. The assumption is already implied by the <nsw>
799+
// tag.
800+
bool RequiresRTC = !scop->hasNSWAddRecForLoop(L);
802801

803802
isl::set UnboundedCtx = Parts.first.params();
804803
recordAssumption(&RecordedAssumptions, INFINITELOOP, UnboundedCtx,
805-
HeaderBB->getTerminator()->getDebugLoc(), AS_RESTRICTION);
804+
HeaderBB->getTerminator()->getDebugLoc(), AS_RESTRICTION,
805+
nullptr, RequiresRTC);
806806
return true;
807807
}
808808

@@ -1495,7 +1495,7 @@ void ScopBuilder::addRecordedAssumptions() {
14951495

14961496
if (!AS.BB) {
14971497
scop->addAssumption(AS.Kind, AS.Set, AS.Loc, AS.Sign,
1498-
nullptr /* BasicBlock */);
1498+
nullptr /* BasicBlock */, AS.RequiresRTC);
14991499
continue;
15001500
}
15011501

@@ -1519,7 +1519,8 @@ void ScopBuilder::addRecordedAssumptions() {
15191519
else /* (AS.Sign == AS_ASSUMPTION) */
15201520
S = isl_set_params(isl_set_subtract(Dom, S));
15211521

1522-
scop->addAssumption(AS.Kind, isl::manage(S), AS.Loc, AS_RESTRICTION, AS.BB);
1522+
scop->addAssumption(AS.Kind, isl::manage(S), AS.Loc, AS_RESTRICTION, AS.BB,
1523+
AS.RequiresRTC);
15231524
}
15241525
}
15251526

polly/lib/Analysis/ScopInfo.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ int const polly::MaxDisjunctsInDomain = 20;
118118
// number of disjunct when adding non-convex sets to the context.
119119
static int const MaxDisjunctsInContext = 4;
120120

121+
// Be a bit more generous for the defined behavior context which is used less
122+
// often.
123+
static int const MaxDisjunktsInDefinedBehaviourContext = 8;
124+
121125
static cl::opt<bool> PollyRemarksMinimal(
122126
"polly-remarks-minimal",
123127
cl::desc("Do not emit remarks about assumptions that are known"),
@@ -1077,12 +1081,11 @@ void MemoryAccess::setNewAccessRelation(isl::map NewAccess) {
10771081
if (isRead()) {
10781082
// Check whether there is an access for every statement instance.
10791083
isl::set StmtDomain = getStatement()->getDomain();
1080-
StmtDomain =
1081-
StmtDomain.intersect_params(getStatement()->getParent()->getContext());
1082-
StmtDomain = subtractParams(
1083-
StmtDomain, getStatement()->getParent()->getInvalidContext());
1084+
isl::set DefinedContext =
1085+
getStatement()->getParent()->getBestKnownDefinedBehaviorContext();
1086+
StmtDomain = StmtDomain.intersect_params(DefinedContext);
10841087
isl::set NewDomain = NewAccess.domain();
1085-
assert(StmtDomain.is_subset(NewDomain) &&
1088+
assert(!StmtDomain.is_subset(NewDomain).is_false() &&
10861089
"Partial READ accesses not supported");
10871090
}
10881091

@@ -1561,6 +1564,7 @@ void Scop::buildContext() {
15611564
Context = isl::set::universe(Space);
15621565
InvalidContext = isl::set::empty(Space);
15631566
AssumedContext = isl::set::universe(Space);
1567+
DefinedBehaviorContext = isl::set::universe(Space);
15641568
}
15651569

15661570
void Scop::addParameterBounds() {
@@ -1569,6 +1573,7 @@ void Scop::addParameterBounds() {
15691573
ConstantRange SRange = SE->getSignedRange(Parameter);
15701574
Context = addRangeBoundsToSet(Context, SRange, PDim++, isl::dim::param);
15711575
}
1576+
intersectDefinedBehavior(Context, AS_ASSUMPTION);
15721577
}
15731578

15741579
static std::vector<isl::id> getFortranArrayIds(Scop::array_range Arrays) {
@@ -1682,6 +1687,8 @@ void Scop::simplifyContexts() {
16821687
// only executed for the case m >= 0, it is sufficient to assume p >= 0.
16831688
AssumedContext = simplifyAssumptionContext(AssumedContext, *this);
16841689
InvalidContext = InvalidContext.align_params(getParamSpace());
1690+
simplify(DefinedBehaviorContext);
1691+
DefinedBehaviorContext = DefinedBehaviorContext.align_params(getParamSpace());
16851692
}
16861693

16871694
isl::set Scop::getDomainConditions(const ScopStmt *Stmt) const {
@@ -2109,9 +2116,14 @@ bool Scop::trackAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
21092116
}
21102117

21112118
void Scop::addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
2112-
AssumptionSign Sign, BasicBlock *BB) {
2119+
AssumptionSign Sign, BasicBlock *BB,
2120+
bool RequiresRTC) {
21132121
// Simplify the assumptions/restrictions first.
21142122
Set = Set.gist_params(getContext());
2123+
intersectDefinedBehavior(Set, Sign);
2124+
2125+
if (!RequiresRTC)
2126+
return;
21152127

21162128
if (!trackAssumption(Kind, Set, Loc, Sign, BB))
21172129
return;
@@ -2122,6 +2134,26 @@ void Scop::addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
21222134
InvalidContext = InvalidContext.unite(Set).coalesce();
21232135
}
21242136

2137+
void Scop::intersectDefinedBehavior(isl::set Set, AssumptionSign Sign) {
2138+
if (!DefinedBehaviorContext)
2139+
return;
2140+
2141+
if (Sign == AS_ASSUMPTION)
2142+
DefinedBehaviorContext = DefinedBehaviorContext.intersect(Set);
2143+
else
2144+
DefinedBehaviorContext = DefinedBehaviorContext.subtract(Set);
2145+
2146+
// Limit the complexity of the context. If complexity is exceeded, simplify
2147+
// the set and check again.
2148+
if (DefinedBehaviorContext.n_basic_set() >
2149+
MaxDisjunktsInDefinedBehaviourContext) {
2150+
simplify(DefinedBehaviorContext);
2151+
if (DefinedBehaviorContext.n_basic_set() >
2152+
MaxDisjunktsInDefinedBehaviourContext)
2153+
DefinedBehaviorContext = nullptr;
2154+
}
2155+
}
2156+
21252157
void Scop::invalidate(AssumptionKind Kind, DebugLoc Loc, BasicBlock *BB) {
21262158
LLVM_DEBUG(dbgs() << "Invalidate SCoP because of reason " << Kind << "\n");
21272159
addAssumption(Kind, isl::set::empty(getParamSpace()), Loc, AS_ASSUMPTION, BB);
@@ -2139,6 +2171,12 @@ void Scop::printContext(raw_ostream &OS) const {
21392171
OS.indent(4) << "Invalid Context:\n";
21402172
OS.indent(4) << InvalidContext << "\n";
21412173

2174+
OS.indent(4) << "Defined Behavior Context:\n";
2175+
if (DefinedBehaviorContext)
2176+
OS.indent(4) << DefinedBehaviorContext << "\n";
2177+
else
2178+
OS.indent(4) << "<unavailable>\n";
2179+
21422180
unsigned Dim = 0;
21432181
for (const SCEV *Parameter : Parameters)
21442182
OS.indent(4) << "p" << Dim++ << ": " << *Parameter << "\n";

polly/lib/Support/ScopHelper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ void polly::splitEntryBlockForAlloca(BasicBlock *EntryBlock, Pass *P) {
224224
void polly::recordAssumption(polly::RecordedAssumptionsTy *RecordedAssumptions,
225225
polly::AssumptionKind Kind, isl::set Set,
226226
DebugLoc Loc, polly::AssumptionSign Sign,
227-
BasicBlock *BB) {
227+
BasicBlock *BB, bool RTC) {
228228
assert((Set.is_params() || BB) &&
229229
"Assumptions without a basic block must be parameter sets");
230230
if (RecordedAssumptions)
231-
RecordedAssumptions->push_back({Kind, Sign, Set, Loc, BB});
231+
RecordedAssumptions->push_back({Kind, Sign, Set, Loc, BB, RTC});
232232
}
233233

234234
/// The SCEVExpander will __not__ generate any code for an existing SDiv/SRem

polly/lib/Transform/DeLICM.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,11 @@ class DeLICMImpl : public ZoneAlgorithm {
868868

869869
// { DomainRead[] -> DomainWrite[] }
870870
auto PerPHIWrites = computePerPHI(SAI);
871+
if (!PerPHIWrites) {
872+
LLVM_DEBUG(
873+
dbgs() << " Reject because cannot determine incoming values\n");
874+
return false;
875+
}
871876

872877
// { DomainWrite[] -> Element[] }
873878
auto WritesTarget = PerPHIWrites.apply_domain(PHITarget).reverse();

polly/lib/Transform/ForwardOpTree.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ class ForwardOpTreeImpl : ZoneAlgorithm {
306306
// { Domain[] -> Element[] }
307307
isl::map Result;
308308

309+
// Make irrelevant elements not interfere.
310+
Domain = Domain.intersect_params(S->getContext());
311+
309312
// MemoryAccesses can read only elements from a single array
310313
// (i.e. not: { Dom[0] -> A[0]; Dom[1] -> B[1] }).
311314
// Look through all spaces until we find one that contains at least the

polly/lib/Transform/ZoneAlgo.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,13 @@ isl::union_map ZoneAlgorithm::computePerPHI(const ScopArrayInfo *SAI) {
541541
if (It != PerPHIMaps.end())
542542
return It->second;
543543

544+
// Cannot reliably compute immediate predecessor for undefined executions, so
545+
// bail out if we do not know. This in particular applies to undefined control
546+
// flow.
547+
isl::set DefinedContext = S->getDefinedBehaviorContext();
548+
if (!DefinedContext)
549+
return nullptr;
550+
544551
assert(SAI->isPHIKind());
545552

546553
// { DomainPHIWrite[] -> Scatter[] }
@@ -565,8 +572,7 @@ isl::union_map ZoneAlgorithm::computePerPHI(const ScopArrayInfo *SAI) {
565572
isl::map PHIWriteTimes = BeforeRead.intersect_range(WriteTimes);
566573

567574
// Remove instances outside the context.
568-
PHIWriteTimes = PHIWriteTimes.intersect_params(S->getAssumedContext());
569-
PHIWriteTimes = subtractParams(PHIWriteTimes, S->getInvalidContext());
575+
PHIWriteTimes = PHIWriteTimes.intersect_params(DefinedContext);
570576

571577
isl::map LastPerPHIWrites = PHIWriteTimes.lexmax();
572578

@@ -1025,6 +1031,13 @@ void ZoneAlgorithm::computeNormalizedPHIs() {
10251031
auto *PHI = cast<PHINode>(MA->getAccessInstruction());
10261032
const ScopArrayInfo *SAI = MA->getOriginalScopArrayInfo();
10271033

1034+
// Determine which instance of the PHI statement corresponds to which
1035+
// incoming value. Skip if we cannot determine PHI predecessors.
1036+
// { PHIDomain[] -> IncomingDomain[] }
1037+
isl::union_map PerPHI = computePerPHI(SAI);
1038+
if (!PerPHI)
1039+
continue;
1040+
10281041
// { PHIDomain[] -> PHIValInst[] }
10291042
isl::map PHIValInst = makeValInst(PHI, &Stmt, Stmt.getSurroundingLoop());
10301043

@@ -1048,11 +1061,6 @@ void ZoneAlgorithm::computeNormalizedPHIs() {
10481061
IncomingValInsts = IncomingValInsts.add_map(IncomingValInst);
10491062
}
10501063

1051-
// Determine which instance of the PHI statement corresponds to which
1052-
// incoming value.
1053-
// { PHIDomain[] -> IncomingDomain[] }
1054-
isl::union_map PerPHI = computePerPHI(SAI);
1055-
10561064
// { PHIValInst[] -> IncomingValInst[] }
10571065
isl::union_map PHIMap =
10581066
PerPHI.apply_domain(PHIValInst).apply_range(IncomingValInsts);

polly/test/DeLICM/pr41656.ll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: opt %loadPolly -polly-delicm -analyze < %s | FileCheck %s
1+
; RUN: opt %loadPolly -polly-scops -polly-delicm -analyze < %s | FileCheck %s
22
;
33
; llvm.org/PR41656
44
;
@@ -81,8 +81,14 @@ attributes #2 = { nounwind }
8181
!6 = !{!"double", !3, i64 0}
8282

8383

84+
; CHECK: Invalid Context:
85+
; CHECK-NEXT: [call24] -> { : call24 <= 2 }
86+
; CHECK: Defined Behavior Context:
87+
; CHECK-NEXT: [call24] -> { : 3 <= call24 <= 2147483647 }
88+
8489
; Only write to scalar if call24 >= 3 (i.e. not in invalid context)
85-
; Since it should be never executed otherwise, the condition is not strictly necessary.
90+
; Since it should be never executed otherwise, the condition is not strictly necessary.
91+
; CHECK-LABEL: DeLICM result:
8692
; CHECK: Stmt_for_body_us_preheader_i
8793
; CHECK-NEXT: MustWriteAccess := [Reduction Type: NONE] [Scalar: 1]
8894
; CHECK-NEXT: [call24] -> { Stmt_for_body_us_preheader_i[] -> MemRef_t_1__phi[] };

0 commit comments

Comments
 (0)