diff options
| -rw-r--r-- | polly/include/polly/ScopInfo.h | 32 | ||||
| -rw-r--r-- | polly/lib/Analysis/ScopInfo.cpp | 88 | ||||
| -rw-r--r-- | polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_affine_loop.ll | 49 | ||||
| -rw-r--r-- | polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_non_affine_loop.ll | 49 | ||||
| -rw-r--r-- | polly/test/ScopInfo/partially_invariant_load_1.ll | 61 | ||||
| -rw-r--r-- | polly/test/ScopInfo/partially_invariant_load_2.ll | 70 | ||||
| -rw-r--r-- | polly/test/ScopInfo/remarks.ll | 2 |
7 files changed, 306 insertions, 45 deletions
diff --git a/polly/include/polly/ScopInfo.h b/polly/include/polly/ScopInfo.h index b386139596d..e7a61c4a35d 100644 --- a/polly/include/polly/ScopInfo.h +++ b/polly/include/polly/ScopInfo.h @@ -913,6 +913,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, /// @brief Ordered list type to hold accesses. using MemoryAccessList = std::forward_list<MemoryAccess *>; +/// @brief Helper structure for invariant memory accesses. +struct InvariantAccess { + /// @brief The memory access that is (partially) invariant. + MemoryAccess *MA; + + /// @brief The context under which the access is not invariant. + isl_set *NonHoistableCtx; +}; + +/// @brief Ordered container type to hold invariant accesses. +using InvariantAccessesTy = SmallVector<InvariantAccess, 8>; + /// @brief Type for equivalent invariant accesses and their domain context. /// /// The first element is the SCEV for the pointer/location that identifies this @@ -1223,7 +1235,7 @@ public: /// /// Note that scalar accesses that are caused by any access in @p InvMAs will /// be eliminated too. - void removeMemoryAccesses(MemoryAccessList &InvMAs); + void removeMemoryAccesses(InvariantAccessesTy &InvMAs); typedef MemoryAccessVec::iterator iterator; typedef MemoryAccessVec::const_iterator const_iterator; @@ -1627,14 +1639,15 @@ private: /// invariant location. void buildInvariantEquivalenceClasses(); - /// @brief Check if a memory access can be hoisted. + /// @brief Return the context under which the access cannot be hoisted. /// - /// @param Access The access to verify. + /// @param Access The access to check. /// @param Writes The set of all memory writes in the scop. /// - /// @return Return true if a memory access can be hoisted. - bool isHoistableAccess(MemoryAccess *Access, - __isl_keep isl_union_map *Writes); + /// @return Return the context under which the access cannot be hoisted or a + /// nullptr if it cannot be hoisted at all. + __isl_give isl_set *getNonHoistableCtx(MemoryAccess *Access, + __isl_keep isl_union_map *Writes); /// @brief Verify that all required invariant loads have been hoisted. /// @@ -1673,7 +1686,7 @@ private: void hoistInvariantLoads(); /// @brief Add invariant loads listed in @p InvMAs with the domain of @p Stmt. - void addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs); + void addInvariantLoads(ScopStmt &Stmt, InvariantAccessesTy &InvMAs); /// @brief Create an id for @p Param and store it in the ParameterIds map. void createParameterId(const SCEV *Param); @@ -2047,6 +2060,11 @@ public: /// @brief Add @p LI to the set of required invariant loads. void addRequiredInvariantLoad(LoadInst *LI) { DC.RequiredILS.insert(LI); } + /// @brief Return true if and only if @p LI is a required invariant load. + bool isRequiredInvariantLoad(LoadInst *LI) const { + return getRequiredInvariantLoads().count(LI); + } + const BoxedLoopsSetTy &getBoxedLoops() const { return DC.BoxedLoopsSet; } bool isNonAffineSubRegion(const Region *R) { diff --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp index 51a476d169b..0db3d833a7b 100644 --- a/polly/lib/Analysis/ScopInfo.cpp +++ b/polly/lib/Analysis/ScopInfo.cpp @@ -1711,14 +1711,15 @@ void ScopStmt::print(raw_ostream &OS) const { void ScopStmt::dump() const { print(dbgs()); } -void ScopStmt::removeMemoryAccesses(MemoryAccessList &InvMAs) { +void ScopStmt::removeMemoryAccesses(InvariantAccessesTy &InvMAs) { // Remove all memory accesses in @p InvMAs from this statement // together with all scalar accesses that were caused by them. // MK_Value READs have no access instruction, hence would not be removed by // this function. However, it is only used for invariant LoadInst accesses, // its arguments are always affine, hence synthesizable, and therefore there // are no MK_Value READ accesses to be removed. - for (MemoryAccess *MA : InvMAs) { + for (const auto &InvMA : InvMAs) { + auto *MA = InvMA.MA; auto Predicate = [&](MemoryAccess *Acc) { return Acc->getAccessInstruction() == MA->getAccessInstruction(); }; @@ -2855,8 +2856,12 @@ MemoryAccess *Scop::lookupBasePtrAccess(MemoryAccess *MA) { bool Scop::hasNonHoistableBasePtrInScop(MemoryAccess *MA, __isl_keep isl_union_map *Writes) { - if (auto *BasePtrMA = lookupBasePtrAccess(MA)) - return !isHoistableAccess(BasePtrMA, Writes); + if (auto *BasePtrMA = lookupBasePtrAccess(MA)) { + auto *NHCtx = getNonHoistableCtx(BasePtrMA, Writes); + bool Hoistable = NHCtx != nullptr; + isl_set_free(NHCtx); + return !Hoistable; + } auto *BaseAddr = SE->getSCEV(MA->getBaseAddr()); auto *PointerBase = dyn_cast<SCEVUnknown>(SE->getPointerBase(BaseAddr)); @@ -3278,7 +3283,8 @@ InvariantEquivClassTy *Scop::lookupInvariantEquivClass(Value *Val) { /// @brief Check if @p MA can always be hoisted without execution context. static bool canAlwaysBeHoisted(MemoryAccess *MA, bool StmtInvalidCtxIsEmpty, - bool MAInvalidCtxIsEmpty) { + bool MAInvalidCtxIsEmpty, + bool NonHoistableCtxIsEmpty) { LoadInst *LInst = cast<LoadInst>(MA->getAccessInstruction()); const DataLayout &DL = LInst->getParent()->getModule()->getDataLayout(); // TODO: We can provide more information for better but more expensive @@ -3287,6 +3293,12 @@ static bool canAlwaysBeHoisted(MemoryAccess *MA, bool StmtInvalidCtxIsEmpty, LInst->getAlignment(), DL)) return false; + // If the location might be overwritten we do not hoist it unconditionally. + // + // TODO: This is probably to conservative. + if (!NonHoistableCtxIsEmpty) + return false; + // If a dereferencable load is in a statement that is modeled precisely we can // hoist it. if (StmtInvalidCtxIsEmpty && MAInvalidCtxIsEmpty) @@ -3301,7 +3313,7 @@ static bool canAlwaysBeHoisted(MemoryAccess *MA, bool StmtInvalidCtxIsEmpty, return true; } -void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { +void Scop::addInvariantLoads(ScopStmt &Stmt, InvariantAccessesTy &InvMAs) { if (InvMAs.empty()) return; @@ -3315,9 +3327,11 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { DomainCtx = isl_set_subtract(DomainCtx, StmtInvalidCtx); if (isl_set_n_basic_set(DomainCtx) >= MaxDisjunctionsInDomain) { - auto *AccInst = InvMAs.front()->getAccessInstruction(); + auto *AccInst = InvMAs.front().MA->getAccessInstruction(); invalidate(COMPLEXITY, AccInst->getDebugLoc()); isl_set_free(DomainCtx); + for (auto &InvMA : InvMAs) + isl_set_free(InvMA.NonHoistableCtx); return; } @@ -3326,7 +3340,8 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { // hoisted loads are executed and we could not determine an order in which to // pre-load them. This happens because not only lower bounds are part of the // domain but also upper bounds. - for (MemoryAccess *MA : InvMAs) { + for (auto &InvMA : InvMAs) { + auto *MA = InvMA.MA; Instruction *AccInst = MA->getAccessInstruction(); if (SE->isSCEVable(AccInst->getType())) { SetVector<Value *> Values; @@ -3345,7 +3360,10 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { } } - for (MemoryAccess *MA : InvMAs) { + for (auto &InvMA : InvMAs) { + auto *MA = InvMA.MA; + auto *NHCtx = InvMA.NonHoistableCtx; + // Check for another invariant access that accesses the same location as // MA and if found consolidate them. Otherwise create a new equivalence // class at the end of InvariantEquivClasses. @@ -3354,16 +3372,19 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { const SCEV *PointerSCEV = SE->getSCEV(LInst->getPointerOperand()); auto *MAInvalidCtx = MA->getInvalidContext(); + bool NonHoistableCtxIsEmpty = isl_set_is_empty(NHCtx); bool MAInvalidCtxIsEmpty = isl_set_is_empty(MAInvalidCtx); isl_set *MACtx; // Check if we know that this pointer can be speculatively accessed. - if (canAlwaysBeHoisted(MA, StmtInvalidCtxIsEmpty, MAInvalidCtxIsEmpty)) { + if (canAlwaysBeHoisted(MA, StmtInvalidCtxIsEmpty, MAInvalidCtxIsEmpty, + NonHoistableCtxIsEmpty)) { MACtx = isl_set_universe(isl_set_get_space(DomainCtx)); isl_set_free(MAInvalidCtx); + isl_set_free(NHCtx); } else { MACtx = isl_set_copy(DomainCtx); - MACtx = isl_set_subtract(MACtx, MAInvalidCtx); + MACtx = isl_set_subtract(MACtx, isl_set_union(MAInvalidCtx, NHCtx)); MACtx = isl_set_gist_params(MACtx, getContext()); } @@ -3418,8 +3439,8 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) { isl_set_free(DomainCtx); } -bool Scop::isHoistableAccess(MemoryAccess *Access, - __isl_keep isl_union_map *Writes) { +__isl_give isl_set *Scop::getNonHoistableCtx(MemoryAccess *Access, + __isl_keep isl_union_map *Writes) { // TODO: Loads that are not loop carried, hence are in a statement with // zero iterators, are by construction invariant, though we // currently "hoist" them anyway. This is necessary because we allow @@ -3430,7 +3451,7 @@ bool Scop::isHoistableAccess(MemoryAccess *Access, BasicBlock *BB = Stmt.getEntryBlock(); if (Access->isScalarKind() || Access->isWrite() || !Access->isAffine()) - return false; + return nullptr; // Skip accesses that have an invariant base pointer which is defined but // not loaded inside the SCoP. This can happened e.g., if a readnone call @@ -3441,14 +3462,14 @@ bool Scop::isHoistableAccess(MemoryAccess *Access, // that it is invariant, thus it will be hoisted too. However, if there is // no base pointer origin we check that the base pointer is defined // outside the region. + auto *LI = cast<LoadInst>(Access->getAccessInstruction()); if (hasNonHoistableBasePtrInScop(Access, Writes)) - return false; + return nullptr; // Skip accesses in non-affine subregions as they might not be executed // under the same condition as the entry of the non-affine subregion. - auto *LI = cast<LoadInst>(Access->getAccessInstruction()); if (BB != LI->getParent()) - return false; + return nullptr; isl_map *AccessRelation = Access->getAccessRelation(); assert(!isl_map_is_empty(AccessRelation)); @@ -3456,7 +3477,7 @@ bool Scop::isHoistableAccess(MemoryAccess *Access, if (isl_map_involves_dims(AccessRelation, isl_dim_in, 0, Stmt.getNumIterators())) { isl_map_free(AccessRelation); - return false; + return nullptr; } AccessRelation = isl_map_intersect_domain(AccessRelation, Stmt.getDomain()); @@ -3464,10 +3485,22 @@ bool Scop::isHoistableAccess(MemoryAccess *Access, isl_union_map *Written = isl_union_map_intersect_range( isl_union_map_copy(Writes), isl_union_set_from_set(AccessRange)); - bool IsWritten = !isl_union_map_is_empty(Written); - isl_union_map_free(Written); + auto *WrittenCtx = isl_union_map_params(Written); + bool IsWritten = !isl_set_is_empty(WrittenCtx); + + if (!IsWritten) + return WrittenCtx; + + WrittenCtx = isl_set_remove_divs(WrittenCtx); + bool TooComplex = isl_set_n_basic_set(WrittenCtx) >= MaxDisjunctionsInDomain; + if (TooComplex || !isRequiredInvariantLoad(LI)) { + isl_set_free(WrittenCtx); + return nullptr; + } - return !IsWritten; + addAssumption(INVARIANTLOAD, isl_set_copy(WrittenCtx), LI->getDebugLoc(), + AS_RESTRICTION); + return WrittenCtx; } void Scop::verifyInvariantLoads() { @@ -3488,18 +3521,11 @@ void Scop::hoistInvariantLoads() { isl_union_map *Writes = getWrites(); for (ScopStmt &Stmt : *this) { - MemoryAccessList InvariantAccesses; + InvariantAccessesTy InvariantAccesses; for (MemoryAccess *Access : Stmt) - if (isHoistableAccess(Access, Writes)) - InvariantAccesses.push_front(Access); - - // We inserted invariant accesses always in the front but need them to be - // sorted in a "natural order". The statements are already sorted in - // reverse post order and that suffices for the accesses too. The reason - // we require an order in the first place is the dependences between - // invariant loads that can be caused by indirect loads. - InvariantAccesses.reverse(); + if (auto *NHCtx = getNonHoistableCtx(Access, Writes)) + InvariantAccesses.push_back({Access, NHCtx}); // Transfer the memory access from the statement to the SCoP. Stmt.removeMemoryAccesses(InvariantAccesses); diff --git a/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_affine_loop.ll b/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_affine_loop.ll index 128461e1947..85c833f432b 100644 --- a/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_affine_loop.ll +++ b/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_affine_loop.ll @@ -9,8 +9,7 @@ ; Negative test for INNERMOST. ; At the moment we will optimistically assume A[i] in the conditional before the inner ; loop might be invariant and expand the SCoP from the loop to include the conditional. However, -; during SCoP generation we will realize that A[i] is in fact not invariant (in this region = the body -; of the outer loop) and bail. +; during SCoP generation we will realize that A[i] is in not always invariant. ; ; Possible solutions could be: ; - Do not optimistically assume it to be invariant (as before this commit), however we would loose @@ -18,7 +17,51 @@ ; - Reduce the size of the SCoP if an assumed invariant access is in fact not invariant instead of ; rejecting the whole region. ; -; INNERMOST-NOT: Function: f +; INNERMOST: Function: f +; INNERMOST-NEXT: Region: %bb4---%bb3 +; INNERMOST-NEXT: Max Loop Depth: 1 +; INNERMOST-NEXT: Invariant Accesses: { +; INNERMOST-NEXT: ReadAccess := [Reduction Type: NONE] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb4[] -> MemRef_A[p_2] }; +; INNERMOST-NEXT: Execution Context: [tmp6, N, p_2] -> { : (tmp6 > 0 and p_2 >= N) or (tmp6 < 0 and p_2 >= N) or tmp6 = 0 } +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Context: +; INNERMOST-NEXT: [tmp6, N, p_2] -> { : -2147483648 <= tmp6 <= 2147483647 and -2147483648 <= N <= 2147483647 and 0 <= p_2 <= 1024 } +; INNERMOST-NEXT: Assumed Context: +; INNERMOST-NEXT: [tmp6, N, p_2] -> { : } +; INNERMOST-NEXT: Invalid Context: +; INNERMOST-NEXT: [tmp6, N, p_2] -> { : p_2 < N and (tmp6 < 0 or tmp6 > 0) } +; INNERMOST-NEXT: p0: %tmp6 +; INNERMOST-NEXT: p1: %N +; INNERMOST-NEXT: p2: {0,+,1}<nuw><nsw><%bb3> +; INNERMOST-NEXT: Arrays { +; INNERMOST-NEXT: i32 MemRef_A[*]; // Element size 4 +; INNERMOST-NEXT: i64 MemRef_indvars_iv_next2; // Element size 8 +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Arrays (Bounds as pw_affs) { +; INNERMOST-NEXT: i32 MemRef_A[*]; // Element size 4 +; INNERMOST-NEXT: i64 MemRef_indvars_iv_next2; // Element size 8 +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Alias Groups (0): +; INNERMOST-NEXT: n/a +; INNERMOST-NEXT: Statements { +; INNERMOST-NEXT: Stmt_bb11 +; INNERMOST-NEXT: Domain := +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb11[i0] : 0 <= i0 < N and (tmp6 < 0 or tmp6 > 0) }; +; INNERMOST-NEXT: Schedule := +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb11[i0] -> [0, i0] : tmp6 < 0 or tmp6 > 0 }; +; INNERMOST-NEXT: ReadAccess := [Reduction Type: +] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb11[i0] -> MemRef_A[i0] }; +; INNERMOST-NEXT: MustWriteAccess := [Reduction Type: +] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb11[i0] -> MemRef_A[i0] }; +; INNERMOST-NEXT: Stmt_bb18 +; INNERMOST-NEXT: Domain := +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb18[] }; +; INNERMOST-NEXT: Schedule := +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb18[] -> [1, 0] }; +; INNERMOST-NEXT: MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] +; INNERMOST-NEXT: [tmp6, N, p_2] -> { Stmt_bb18[] -> MemRef_indvars_iv_next2[] }; +; INNERMOST-NEXT: } ; ; ALL: Function: f ; ALL-NEXT: Region: %bb3---%bb19 diff --git a/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_non_affine_loop.ll b/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_non_affine_loop.ll index 2d0e3d2ffc5..e42ee981afd 100644 --- a/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_non_affine_loop.ll +++ b/polly/test/ScopInfo/NonAffine/non_affine_conditional_surrounding_non_affine_loop.ll @@ -13,8 +13,7 @@ ; Negative test for INNERMOST. ; At the moment we will optimistically assume A[i] in the conditional before the inner ; loop might be invariant and expand the SCoP from the loop to include the conditional. However, -; during SCoP generation we will realize that A[i] is in fact not invariant (in this region = the body -; of the outer loop) and bail. +; during SCoP generation we will realize that A[i] is only sometimes invariant. ; ; Possible solutions could be: ; - Do not optimistically assume it to be invariant (as before this commit), however we would loose @@ -22,7 +21,51 @@ ; - Reduce the size of the SCoP if an assumed invariant access is in fact not invariant instead of ; rejecting the whole region. ; -; INNERMOST-NOT: Function: f +; INNERMOST: Function: f +; INNERMOST-NEXT: Region: %bb4---%bb3 +; INNERMOST-NEXT: Max Loop Depth: 1 +; INNERMOST-NEXT: Invariant Accesses: { +; INNERMOST-NEXT: ReadAccess := [Reduction Type: NONE] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb4[] -> MemRef_A[p_2] }; +; INNERMOST-NEXT: Execution Context: [tmp6, p_1, p_2] -> { : (tmp6 > 0 and p_2 >= p_1) or (tmp6 < 0 and p_2 >= p_1) or tmp6 = 0 } +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Context: +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { : -2147483648 <= tmp6 <= 2147483647 and -2199023255552 <= p_1 <= 2199023254528 and 0 <= p_2 <= 1024 } +; INNERMOST-NEXT: Assumed Context: +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { : } +; INNERMOST-NEXT: Invalid Context: +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { : p_2 < p_1 and (tmp6 < 0 or tmp6 > 0) } +; INNERMOST-NEXT: p0: %tmp6 +; INNERMOST-NEXT: p1: {0,+,(sext i32 %N to i64)}<%bb3> +; INNERMOST-NEXT: p2: {0,+,1}<nuw><nsw><%bb3> +; INNERMOST-NEXT: Arrays { +; INNERMOST-NEXT: i32 MemRef_A[*]; // Element size 4 +; INNERMOST-NEXT: i64 MemRef_indvars_iv_next2; // Element size 8 +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Arrays (Bounds as pw_affs) { +; INNERMOST-NEXT: i32 MemRef_A[*]; // Element size 4 +; INNERMOST-NEXT: i64 MemRef_indvars_iv_next2; // Element size 8 +; INNERMOST-NEXT: } +; INNERMOST-NEXT: Alias Groups (0): +; INNERMOST-NEXT: n/a +; INNERMOST-NEXT: Statements { +; INNERMOST-NEXT: Stmt_bb12 +; INNERMOST-NEXT: Domain := +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb12[i0] : 0 <= i0 < p_1 and (tmp6 < 0 or tmp6 > 0) }; +; INNERMOST-NEXT: Schedule := +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb12[i0] -> [0, i0] : tmp6 < 0 or tmp6 > 0 }; +; INNERMOST-NEXT: ReadAccess := [Reduction Type: +] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb12[i0] -> MemRef_A[i0] }; +; INNERMOST-NEXT: MustWriteAccess := [Reduction Type: +] [Scalar: 0] +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb12[i0] -> MemRef_A[i0] }; +; INNERMOST-NEXT: Stmt_bb19 +; INNERMOST-NEXT: Domain := +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb19[] }; +; INNERMOST-NEXT: Schedule := +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb19[] -> [1, 0] }; +; INNERMOST-NEXT: MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] +; INNERMOST-NEXT: [tmp6, p_1, p_2] -> { Stmt_bb19[] -> MemRef_indvars_iv_next2[] }; +; INNERMOST-NEXT: } ; ; ALL: Function: f ; ALL-NEXT: Region: %bb3---%bb20 diff --git a/polly/test/ScopInfo/partially_invariant_load_1.ll b/polly/test/ScopInfo/partially_invariant_load_1.ll new file mode 100644 index 00000000000..de15a937e9e --- /dev/null +++ b/polly/test/ScopInfo/partially_invariant_load_1.ll @@ -0,0 +1,61 @@ +; RUN: opt %loadPolly -polly-scops -analyze < %s | FileCheck %s +; RUN: opt %loadPolly -polly-codegen -S < %s | FileCheck %s --check-prefix=IR +; +; CHECK: Invariant Accesses: { +; CHECK-NEXT: ReadAccess := [Reduction Type: NONE] [Scalar: 0] +; CHECK-NEXT: [N, tmp1] -> { Stmt_for_body[i0] -> MemRef_I[0] }; +; CHECK-NEXT: Execution Context: [N, tmp1] -> { : N > 0 and (tmp1 >= 43 or tmp1 <= 41) } +; CHECK-NEXT: } +; CHECK: Invalid Context: +; CHECK-NEXT: [N, tmp1] -> { : tmp1 = 42 and N > 0 } +; +; IR: polly.preload.begin: +; IR-NEXT: br i1 false, label %polly.start, label %for.cond +; +; void f(int *A, int *I, int N) { +; for (int i = 0; i < N; i++) { +; if (*I == 42) +; *I = 0; +; else +; A[i]++; +; } +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @f(i32* %A, i32* %I, i32 %N) { +entry: + %tmp = sext i32 %N to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, %tmp + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %tmp1 = load i32, i32* %I, align 4 + %cmp1 = icmp eq i32 %tmp1, 42 + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %for.body + store i32 0, i32* %I, align 4 + br label %if.end + +if.else: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp2, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.else, %if.then + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} diff --git a/polly/test/ScopInfo/partially_invariant_load_2.ll b/polly/test/ScopInfo/partially_invariant_load_2.ll new file mode 100644 index 00000000000..59e377e71e9 --- /dev/null +++ b/polly/test/ScopInfo/partially_invariant_load_2.ll @@ -0,0 +1,70 @@ +; RUN: opt %loadPolly -polly-scops -analyze < %s | FileCheck %s +; +; Check that we do not try to preload *I and assume p != 42. +; +; CHECK: Invariant Accesses: { +; CHECK-NEXT: ReadAccess := [Reduction Type: NONE] [Scalar: 0] +; CHECK-NEXT: [N, p, tmp1, q] -> { Stmt_if_then[i0] -> MemRef_I[0] }; +; CHECK-NEXT: Execution Context: [N, p, tmp1, q] -> { : 1 = 0 } +; CHECK-NEXT: } +; +; CHECK: Invalid Context: +; CHECK-NEXT: [N, p, tmp1, q] -> { : p = 42 and N > 0 } +; +; void f(int *A, int *I, int N, int p, int q) { +; for (int i = 0; i < N; i++) { +; if (p == 42) { +; *I = 0; +; if (*I == q) +; A[i] *= 2; +; } +; A[i]++; +; } +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @f(i32* %A, i32* %I, i32 %N, i32 %p, i32 %q) { +entry: + %tmp = sext i32 %N to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, %tmp + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp eq i32 %p, 42 + br i1 %cmp1, label %if.then, label %if.end4 + +if.then: ; preds = %for.body + store i32 0, i32* %I, align 4 + %tmp1 = load i32, i32* %I, align 4 + %cmp2 = icmp eq i32 %tmp1, %q + br i1 %cmp2, label %if.then3, label %if.end + +if.then3: ; preds = %if.then + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx, align 4 + %mul = shl nsw i32 %tmp2, 1 + store i32 %mul, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then3, %if.then + br label %if.end4 + +if.end4: ; preds = %if.end, %for.body + %arrayidx6 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp3 = load i32, i32* %arrayidx6, align 4 + %inc = add nsw i32 %tmp3, 1 + store i32 %inc, i32* %arrayidx6, align 4 + br label %for.inc + +for.inc: ; preds = %if.end4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} diff --git a/polly/test/ScopInfo/remarks.ll b/polly/test/ScopInfo/remarks.ll index 82b06a8ab9f..22302c42f0f 100644 --- a/polly/test/ScopInfo/remarks.ll +++ b/polly/test/ScopInfo/remarks.ll @@ -10,7 +10,7 @@ ; CHECK: remark: test/ScopInfo/remarks.c:9:15: Possibly aliasing pointer, use restrict keyword. ; CHECK: remark: test/ScopInfo/remarks.c:14:3: SCoP ends here. ; CHECK: remark: test/ScopInfo/remarks.c:19:3: SCoP begins here. -; CHECK: remark: test/ScopInfo/remarks.c:21:11: Invariant load assumption: [tmp] -> { : 1 = 0 } +; CHECK: remark: test/ScopInfo/remarks.c:21:11: Invariant load restriction: [tmp] -> { : tmp < 0 or tmp > 0 } ; CHECK: remark: test/ScopInfo/remarks.c:22:16: SCoP ends here but was dismissed. ; ; #include <stdio.h> |

