[polly] 948a647 - [Polly] Assumptions used to derive domain must not be pruned by that domain (#190436)

via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 4 04:42:09 PDT 2026


Author: Michael Kruse
Date: 2026-04-04T13:42:04+02:00
New Revision: 948a64720b1ec018e13b4bb8250482f9615d7856

URL: https://github.com/llvm/llvm-project/commit/948a64720b1ec018e13b4bb8250482f9615d7856
DIFF: https://github.com/llvm/llvm-project/commit/948a64720b1ec018e13b4bb8250482f9615d7856.diff

LOG: [Polly] Assumptions used to derive domain must not be pruned by that domain (#190436)

The code that emits the conditions for whether a statement is executed
by checking whether we are in the statement's domain may apply
assumptions (such as an integer truncation being reversible). Later code
then assumes that these assumptions are only relevent for then the
statement is executed, but actually it is used for determining whether
it is executed.

Break this circular reasoning by introducing an `IsInsideDomain` flag
that can be set when the domain has not been verified yet.

Fixes #190128

Thanks to @thapgua for the bug report

Added: 
    polly/test/ScopInfo/issue190128.ll

Modified: 
    polly/include/polly/ScopBuilder.h
    polly/include/polly/ScopInfo.h
    polly/include/polly/Support/SCEVAffinator.h
    polly/lib/Analysis/ScopBuilder.cpp
    polly/lib/Analysis/ScopInfo.cpp
    polly/lib/Support/SCEVAffinator.cpp
    polly/lib/Support/ScopHelper.cpp
    polly/test/ScopInfo/zero_ext_of_truncate.ll
    polly/test/ScopInfo/zero_ext_of_truncate_2.ll

Removed: 
    


################################################################################
diff  --git a/polly/include/polly/ScopBuilder.h b/polly/include/polly/ScopBuilder.h
index e589a7f0b05d6..dc29dcb944b7f 100644
--- a/polly/include/polly/ScopBuilder.h
+++ b/polly/include/polly/ScopBuilder.h
@@ -110,10 +110,14 @@ class ScopBuilder final {
   /// This will fill @p ConditionSets with the conditions under which control
   /// will be moved from @p TI to its successors. Hence, @p ConditionSets will
   /// have as many elements as @p TI has successors.
+  ///
+  /// Set @p IsInsideDomain to false when building the conditions that check
+  /// whether @p BB is to be executed, since we are not in its domain yet.
   bool buildConditionSets(BasicBlock *BB, Instruction *TI, Loop *L,
                           __isl_keep isl_set *Domain,
                           DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets,
+                          bool IsInsideDomain = true);
 
   /// Build the conditions sets for the branch condition @p Condition in
   /// the @p Domain.
@@ -123,20 +127,28 @@ class ScopBuilder final {
   /// have as many elements as @p TI has successors. If @p TI is nullptr the
   /// context under which @p Condition is true/false will be returned as the
   /// new elements of @p ConditionSets.
+  ///
+  /// Set @p IsInsideDomain to false when building the conditions that check
+  /// whether @p BB is to be executed, since we are not in its domain yet.
   bool buildConditionSets(BasicBlock *BB, Value *Condition, Instruction *TI,
                           Loop *L, __isl_keep isl_set *Domain,
                           DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets,
+                          bool IsInsideDomain = true);
 
   /// Build the conditions sets for the switch @p SI in the @p Domain.
   ///
   /// This will fill @p ConditionSets with the conditions under which control
   /// will be moved from @p SI to its successors. Hence, @p ConditionSets will
   /// have as many elements as @p SI has successors.
+  ///
+  /// Set @p IsInsideDomain to false when building the conditions that check
+  /// whether @p BB is to be executed, since we are not in its domain yet.
   bool buildConditionSets(BasicBlock *BB, SwitchInst *SI, Loop *L,
                           __isl_keep isl_set *Domain,
                           DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+                          SmallVectorImpl<__isl_give isl_set *> &ConditionSets,
+                          bool IsInsideDomain = true);
 
   /// Build condition sets for unsigned ICmpInst(s).
   /// Special handling is required for unsigned operands to ensure that if
@@ -145,12 +157,15 @@ class ScopBuilder final {
   ///
   /// @param IsStrictUpperBound holds information on the predicate relation
   /// between TestVal and UpperBound, i.e,
+  ///
+  /// Set @p IsInsideDomain to false when building the conditions that check
+  /// whether @p BB is to be executed, since we are not in its domain yet.
   /// TestVal < UpperBound  OR  TestVal <= UpperBound
   __isl_give isl_set *buildUnsignedConditionSets(
       BasicBlock *BB, Value *Condition, __isl_keep isl_set *Domain,
       const SCEV *SCEV_TestVal, const SCEV *SCEV_UpperBound,
       DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-      bool IsStrictUpperBound);
+      bool IsStrictUpperBound, bool IsInsideDomain = true);
 
   /// Propagate the domain constraints through the region @p R.
   ///
@@ -235,13 +250,19 @@ class ScopBuilder final {
   /// @param InvalidDomainMap A map of BB to their invalid domains.
   /// @param E                The SCEV that should be translated.
   /// @param NonNegative      Flag to indicate the @p E has to be
-  /// non-negative.
+  ///                         non-negative.
+  /// @param IsInsideDomain If true, assumptions only need to apply during the
+  ///                       execution of @p BB. That is, when we know that we
+  ///                       are in its domain. Must be false if the SCEV is
+  ///                       evaluated outside a ScopStmt, or for code that
+  ///                       computes the domain (since while doing that, we
+  ///                       don't know whether we are in the domain yet).
   ///
   /// Note that this function will also adjust the invalid context
   /// accordingly.
   __isl_give isl_pw_aff *
   getPwAff(BasicBlock *BB, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-           const SCEV *E, bool NonNegative = false);
+           const SCEV *E, bool NonNegative = false, bool IsInsideDomain = true);
 
   /// Create equivalence classes for required invariant accesses.
   ///

diff  --git a/polly/include/polly/ScopInfo.h b/polly/include/polly/ScopInfo.h
index 4cfbffc134a3a..8532229b0dd71 100644
--- a/polly/include/polly/ScopInfo.h
+++ b/polly/include/polly/ScopInfo.h
@@ -2483,6 +2483,12 @@ class Scop final {
   ///           SCEVs known to not reference any loops in the SCoP can be
   ///           passed without a @p BB.
   /// @param NonNegative Flag to indicate the @p E has to be non-negative.
+  /// @param IsInsideDomain If true, assumptions only need to apply during the
+  ///                       execution of @p BB. That is, when we know that we
+  ///                       are in its domain. Must be false if the SCEV is
+  ///                       evaluated outside a ScopStmt, or for code that
+  ///                       computes the domain (since while doing that, we
+  ///                       don't know whether we are in the domain yet).
   ///
   /// Note that this function will always return a valid isl_pw_aff. However, if
   /// the translation of @p E was deemed to complex the SCoP is invalidated and
@@ -2490,7 +2496,8 @@ class Scop final {
   /// for complex cases without "error handling code" needed on the users side.
   PWACtx getPwAff(const SCEV *E, BasicBlock *BB = nullptr,
                   bool NonNegative = false,
-                  RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+                  RecordedAssumptionsTy *RecordedAssumptions = nullptr,
+                  bool IsInsideDomain = true);
 
   /// Compute the isl representation for the SCEV @p E
   ///

diff  --git a/polly/include/polly/Support/SCEVAffinator.h b/polly/include/polly/Support/SCEVAffinator.h
index 5fa78cc2431e7..8fe1ed44e16d9 100644
--- a/polly/include/polly/Support/SCEVAffinator.h
+++ b/polly/include/polly/Support/SCEVAffinator.h
@@ -33,12 +33,21 @@ class SCEVAffinator final : public llvm::SCEVVisitor<SCEVAffinator, PWACtx> {
 
   /// Translate a SCEV to an isl::pw_aff.
   ///
-  /// @param E  he expression that is translated.
+  /// @param E  The expression that is translated.
   /// @param BB The block in which @p E is executed.
+  /// @param RecordedAssumptions If set, assumptions that make the translation
+  ///                            valid are added here.
+  /// @param IsInsideDomain If true, assumptions only need to apply during the
+  ///                       execution of @p BB. That is, when we know that we
+  ///                       are in its domain. Must be false if the SCEV is
+  ///                       evaluated outside a ScopStmt, or for code that
+  ///                       computes the domain (since while doing that, we
+  ///                       don't know whether we are in the domain yet).
   ///
   /// @returns The isl representation of the SCEV @p E in @p Domain.
   PWACtx getPwAff(const llvm::SCEV *E, llvm::BasicBlock *BB = nullptr,
-                  RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+                  RecordedAssumptionsTy *RecordedAssumptions = nullptr,
+                  bool IsInsideDomain = true);
 
   /// Take the assumption that @p PWAC is non-negative.
   void takeNonNegativeAssumption(
@@ -66,6 +75,12 @@ class SCEVAffinator final : public llvm::SCEVVisitor<SCEVAffinator, PWACtx> {
   llvm::ScalarEvolution &SE;
   llvm::LoopInfo &LI;
   llvm::BasicBlock *BB;
+
+  /// Whether the evaluation takes place only when @p BB's domain has already
+  /// been checked.
+  /// @see getPwAff
+  bool IsInsideDomain = true;
+
   RecordedAssumptionsTy *RecordedAssumptions = nullptr;
 
   /// Target data for element size computing.

diff  --git a/polly/lib/Analysis/ScopBuilder.cpp b/polly/lib/Analysis/ScopBuilder.cpp
index ce96211d08a41..fe296c0450697 100644
--- a/polly/lib/Analysis/ScopBuilder.cpp
+++ b/polly/lib/Analysis/ScopBuilder.cpp
@@ -333,21 +333,12 @@ isl::set ScopBuilder::adjustDomainDimensions(isl::set Dom, Loop *OldL,
   return Dom;
 }
 
-/// Compute the isl representation for the SCEV @p E in this BB.
-///
-/// @param BB               The BB for which isl representation is to be
-/// computed.
-/// @param InvalidDomainMap A map of BB to their invalid domains.
-/// @param E                The SCEV that should be translated.
-/// @param NonNegative      Flag to indicate the @p E has to be non-negative.
-///
-/// Note that this function will also adjust the invalid context accordingly.
-
 __isl_give isl_pw_aff *
 ScopBuilder::getPwAff(BasicBlock *BB,
                       DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-                      const SCEV *E, bool NonNegative) {
-  PWACtx PWAC = scop->getPwAff(E, BB, NonNegative, &RecordedAssumptions);
+                      const SCEV *E, bool NonNegative, bool IsInsideDomain) {
+  PWACtx PWAC =
+      scop->getPwAff(E, BB, NonNegative, &RecordedAssumptions, IsInsideDomain);
   InvalidDomainMap[BB] = InvalidDomainMap[BB].unite(PWAC.second);
   return PWAC.first.release();
 }
@@ -363,14 +354,15 @@ ScopBuilder::getPwAff(BasicBlock *BB,
 __isl_give isl_set *ScopBuilder::buildUnsignedConditionSets(
     BasicBlock *BB, Value *Condition, __isl_keep isl_set *Domain,
     const SCEV *SCEV_TestVal, const SCEV *SCEV_UpperBound,
-    DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-    bool IsStrictUpperBound) {
+    DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, bool IsStrictUpperBound,
+    bool IsInsideDomain) {
   // Do not take NonNeg assumption on TestVal
   // as it might have MSB (Sign bit) set.
-  isl_pw_aff *TestVal = getPwAff(BB, InvalidDomainMap, SCEV_TestVal, false);
+  isl_pw_aff *TestVal = getPwAff(BB, InvalidDomainMap, SCEV_TestVal,
+                                 /*NonNegative=*/false, IsInsideDomain);
   // Take NonNeg assumption on UpperBound.
-  isl_pw_aff *UpperBound =
-      getPwAff(BB, InvalidDomainMap, SCEV_UpperBound, true);
+  isl_pw_aff *UpperBound = getPwAff(BB, InvalidDomainMap, SCEV_UpperBound,
+                                    /*NonNegative=*/true, IsInsideDomain);
 
   // 0 <= TestVal
   isl_set *First =
@@ -393,11 +385,12 @@ __isl_give isl_set *ScopBuilder::buildUnsignedConditionSets(
 bool ScopBuilder::buildConditionSets(
     BasicBlock *BB, SwitchInst *SI, Loop *L, __isl_keep isl_set *Domain,
     DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-    SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+    SmallVectorImpl<__isl_give isl_set *> &ConditionSets, bool IsInsideDomain) {
   Value *Condition = SI->getCondition();
 
   isl_pw_aff *LHS, *RHS;
-  LHS = getPwAff(BB, InvalidDomainMap, SE.getSCEVAtScope(Condition, L));
+  LHS = getPwAff(BB, InvalidDomainMap, SE.getSCEVAtScope(Condition, L),
+                 /*NonNegative=*/false, IsInsideDomain);
 
   unsigned NumSuccessors = SI->getNumSuccessors();
   ConditionSets.resize(NumSuccessors);
@@ -405,7 +398,8 @@ bool ScopBuilder::buildConditionSets(
     unsigned Idx = Case.getSuccessorIndex();
     ConstantInt *CaseValue = Case.getCaseValue();
 
-    RHS = getPwAff(BB, InvalidDomainMap, SE.getSCEV(CaseValue));
+    RHS = getPwAff(BB, InvalidDomainMap, SE.getSCEV(CaseValue),
+                   /*NonNegative=*/false, IsInsideDomain);
     isl_set *CaseConditionSet =
         buildConditionSet(ICmpInst::ICMP_EQ, isl::manage_copy(LHS),
                           isl::manage(RHS))
@@ -430,15 +424,17 @@ bool ScopBuilder::buildConditionSets(
     BasicBlock *BB, Value *Condition, Instruction *TI, Loop *L,
     __isl_keep isl_set *Domain,
     DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-    SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+    SmallVectorImpl<__isl_give isl_set *> &ConditionSets, bool IsInsideDomain) {
   isl_set *ConsequenceCondSet = nullptr;
 
   if (auto Load = dyn_cast<LoadInst>(Condition)) {
     const SCEV *LHSSCEV = SE.getSCEVAtScope(Load, L);
     const SCEV *RHSSCEV = SE.getZero(LHSSCEV->getType());
     bool NonNeg = false;
-    isl_pw_aff *LHS = getPwAff(BB, InvalidDomainMap, LHSSCEV, NonNeg);
-    isl_pw_aff *RHS = getPwAff(BB, InvalidDomainMap, RHSSCEV, NonNeg);
+    isl_pw_aff *LHS =
+        getPwAff(BB, InvalidDomainMap, LHSSCEV, NonNeg, IsInsideDomain);
+    isl_pw_aff *RHS =
+        getPwAff(BB, InvalidDomainMap, RHSSCEV, NonNeg, IsInsideDomain);
     ConsequenceCondSet = buildConditionSet(ICmpInst::ICMP_SLE, isl::manage(LHS),
                                            isl::manage(RHS))
                              .release();
@@ -462,10 +458,11 @@ bool ScopBuilder::buildConditionSets(
     auto Opcode = BinOp->getOpcode();
     assert(Opcode == Instruction::And || Opcode == Instruction::Or);
 
-    bool Valid = buildConditionSets(BB, BinOp->getOperand(0), TI, L, Domain,
-                                    InvalidDomainMap, ConditionSets) &&
-                 buildConditionSets(BB, BinOp->getOperand(1), TI, L, Domain,
-                                    InvalidDomainMap, ConditionSets);
+    bool Valid =
+        buildConditionSets(BB, BinOp->getOperand(0), TI, L, Domain,
+                           InvalidDomainMap, ConditionSets, IsInsideDomain) &&
+        buildConditionSets(BB, BinOp->getOperand(1), TI, L, Domain,
+                           InvalidDomainMap, ConditionSets, IsInsideDomain);
     if (!Valid) {
       while (!ConditionSets.empty())
         isl_set_free(ConditionSets.pop_back_val());
@@ -501,28 +498,29 @@ bool ScopBuilder::buildConditionSets(
 
     switch (ICond->getPredicate()) {
     case ICmpInst::ICMP_ULT:
-      ConsequenceCondSet =
-          buildUnsignedConditionSets(BB, Condition, Domain, LeftOperand,
-                                     RightOperand, InvalidDomainMap, true);
+      ConsequenceCondSet = buildUnsignedConditionSets(
+          BB, Condition, Domain, LeftOperand, RightOperand, InvalidDomainMap,
+          /*IsStrictUpperBound=*/true, IsInsideDomain);
       break;
     case ICmpInst::ICMP_ULE:
-      ConsequenceCondSet =
-          buildUnsignedConditionSets(BB, Condition, Domain, LeftOperand,
-                                     RightOperand, InvalidDomainMap, false);
+      ConsequenceCondSet = buildUnsignedConditionSets(
+          BB, Condition, Domain, LeftOperand, RightOperand, InvalidDomainMap,
+          /*IsStrictUpperBound=*/false, IsInsideDomain);
       break;
     case ICmpInst::ICMP_UGT:
-      ConsequenceCondSet =
-          buildUnsignedConditionSets(BB, Condition, Domain, RightOperand,
-                                     LeftOperand, InvalidDomainMap, true);
+      ConsequenceCondSet = buildUnsignedConditionSets(
+          BB, Condition, Domain, RightOperand, LeftOperand, InvalidDomainMap,
+          /*IsStrictUpperBound=*/true, IsInsideDomain);
       break;
     case ICmpInst::ICMP_UGE:
-      ConsequenceCondSet =
-          buildUnsignedConditionSets(BB, Condition, Domain, RightOperand,
-                                     LeftOperand, InvalidDomainMap, false);
+      ConsequenceCondSet = buildUnsignedConditionSets(
+          BB, Condition, Domain, RightOperand, LeftOperand, InvalidDomainMap,
+          /*IsStrictUpperBound=*/false, IsInsideDomain);
       break;
     default:
-      LHS = getPwAff(BB, InvalidDomainMap, LeftOperand, NonNeg);
-      RHS = getPwAff(BB, InvalidDomainMap, RightOperand, NonNeg);
+      LHS = getPwAff(BB, InvalidDomainMap, LeftOperand, NonNeg, IsInsideDomain);
+      RHS =
+          getPwAff(BB, InvalidDomainMap, RightOperand, NonNeg, IsInsideDomain);
       ConsequenceCondSet = buildConditionSet(ICond->getPredicate(),
                                              isl::manage(LHS), isl::manage(RHS))
                                .release();
@@ -566,10 +564,10 @@ bool ScopBuilder::buildConditionSets(
 bool ScopBuilder::buildConditionSets(
     BasicBlock *BB, Instruction *TI, Loop *L, __isl_keep isl_set *Domain,
     DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
-    SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+    SmallVectorImpl<__isl_give isl_set *> &ConditionSets, bool IsInsideDomain) {
   if (SwitchInst *SI = dyn_cast<SwitchInst>(TI))
     return buildConditionSets(BB, SI, L, Domain, InvalidDomainMap,
-                              ConditionSets);
+                              ConditionSets, IsInsideDomain);
 
   if (isa<UncondBrInst>(TI)) {
     ConditionSets.push_back(isl_set_copy(Domain));
@@ -578,7 +576,7 @@ bool ScopBuilder::buildConditionSets(
 
   Value *Condition = cast<CondBrInst>(TI)->getCondition();
   return buildConditionSets(BB, Condition, TI, L, Domain, InvalidDomainMap,
-                            ConditionSets);
+                            ConditionSets, IsInsideDomain);
 }
 
 bool ScopBuilder::propagateDomainConstraints(
@@ -756,7 +754,8 @@ bool ScopBuilder::addLoopBoundsToHeaderDomain(
       SmallVector<isl_set *, 8> ConditionSets;
       int idx = BI->getSuccessor(0) != HeaderBB;
       if (!buildConditionSets(LatchBB, TI, L, LatchBBDom.get(),
-                              InvalidDomainMap, ConditionSets))
+                              InvalidDomainMap, ConditionSets,
+                              /*IsInsideDomain=*/false))
         return false;
 
       // Free the non back edge condition set as we do not need it.
@@ -930,7 +929,7 @@ bool ScopBuilder::buildDomainsWithBranchConstraints(
     if (RN->isSubRegion())
       ConditionSets.push_back(Domain.copy());
     else if (!buildConditionSets(BB, TI, BBLoop, Domain.get(), InvalidDomainMap,
-                                 ConditionSets))
+                                 ConditionSets, /*IsInsideDomain=*/false))
       return false;
 
     // Now iterate over the successors and set their initial domain based on
@@ -1324,34 +1323,67 @@ void ScopBuilder::buildEscapingDependences(Instruction *Inst) {
 
 void ScopBuilder::addRecordedAssumptions() {
   for (auto &AS : llvm::reverse(RecordedAssumptions)) {
+    isl::set S = AS.Set;
+    AssumptionSign Sign = AS.Sign;
+
+    // Assumptions/restructions apply only when the code containing it is
+    // actually executed
+    if (AS.BB && !AS.Set.is_params()) {
+      // If the domain was deleted the assumptions are void.
+      isl::set Dom = scop->getDomainConditions(AS.BB);
+      if (Dom.is_null())
+        continue;
 
-    if (!AS.BB) {
-      scop->addAssumption(AS.Kind, AS.Set, AS.Loc, AS.Sign,
-                          nullptr /* BasicBlock */, AS.RequiresRTC);
-      continue;
+      // If a basic block was given use its domain to simplify the assumption.
+      // In case of restrictions we know they only have to hold on the domain,
+      // thus we can intersect them with the domain of the block. However, for
+      // assumptions the domain has to imply them, thus:
+      //                     _              _____
+      //   Dom => S   <==>   A v B   <==>   A - B
+      //
+      // To avoid the complement we will register A - B as a restriction not an
+      // assumption.
+      if (Sign == AS_RESTRICTION) {
+        S = std::move(S).intersect(std::move(Dom));
+      } else {
+        S = std::move(Dom).subtract(std::move(S));
+        Sign = AS_RESTRICTION;
+      }
     }
 
-    // If the domain was deleted the assumptions are void.
-    isl_set *Dom = scop->getDomainConditions(AS.BB).release();
-    if (!Dom)
-      continue;
-
-    // If a basic block was given use its domain to simplify the assumption.
-    // In case of restrictions we know they only have to hold on the domain,
-    // thus we can intersect them with the domain of the block. However, for
-    // assumptions the domain has to imply them, thus:
-    //                     _              _____
-    //   Dom => S   <==>   A v B   <==>   A - B
+    isl::set PSet = S.params();
+#ifndef NDEBUG
+    // .params() is an overapproximation; if an AS_ASSUMPTION says
     //
-    // To avoid the complement we will register A - B as a restriction not an
-    // assumption.
-    isl_set *S = AS.Set.copy();
-    if (AS.Sign == AS_RESTRICTION)
-      S = isl_set_params(isl_set_intersect(S, Dom));
-    else /* (AS.Sign == AS_ASSUMPTION) */
-      S = isl_set_params(isl_set_subtract(Dom, S));
+    //    [p] -> { [i] : p == 1 and i == 1 }
+    //
+    // the params space will be
+    //
+    //    [p] -> { [] : }
+    //
+    // (because there is at least one element with p == 1 in the set);
+    // if RequiresRTC is true, we will not include a check for p at all. The
+    // code above adds the domain constraints which don't need (and should not)
+    // to checked, but the actual assumption/restructions should have access to
+    // the parameters only.
+    if (AS.RequiresRTC && Sign == AS_RESTRICTION) {
+      // Overapproximation is OK in this case: failing more RTC checks than
+      // strictly necessary (Underapproximation of RTC-checked AS_ASSUMPTIONs
+      // would be as well)
+    } else if (!AS.RequiresRTC && Sign == AS_ASSUMPTION) {
+      // Overapproximation of defined behavior is OK: Only too optimistic
+      // assumptions could lead to invalid transformations; the universe set
+      // would be equivalent to "no assumptions" (Underapproximation of
+      // undefined behaviour would be as well)
+    } else {
+      isl::set ReconstructedSet =
+          S.get_space().universe_set().intersect_params(PSet);
+      assert(ReconstructedSet.is_subset(S) &&
+             "Must not overapproximate assumptions/restructions");
+    }
+#endif
 
-    scop->addAssumption(AS.Kind, isl::manage(S), AS.Loc, AS_RESTRICTION, AS.BB,
+    scop->addAssumption(AS.Kind, std::move(PSet), AS.Loc, Sign, AS.BB,
                         AS.RequiresRTC);
   }
 }

diff  --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp
index d3aaa7840ae80..cff7606411e5e 100644
--- a/polly/lib/Analysis/ScopInfo.cpp
+++ b/polly/lib/Analysis/ScopInfo.cpp
@@ -2169,13 +2169,14 @@ isl::ctx Scop::getIslCtx() const { return IslCtx.get(); }
 
 __isl_give PWACtx Scop::getPwAff(const SCEV *E, BasicBlock *BB,
                                  bool NonNegative,
-                                 RecordedAssumptionsTy *RecordedAssumptions) {
+                                 RecordedAssumptionsTy *RecordedAssumptions,
+                                 bool IsInsideDomain) {
   // First try to use the SCEVAffinator to generate a piecewise defined
   // affine function from @p E in the context of @p BB. If that tasks becomes to
   // complex the affinator might return a nullptr. In such a case we invalidate
   // the SCoP and return a dummy value. This way we do not need to add error
   // handling code to all users of this function.
-  auto PWAC = Affinator.getPwAff(E, BB, RecordedAssumptions);
+  PWACtx PWAC = Affinator.getPwAff(E, BB, RecordedAssumptions, IsInsideDomain);
   if (!PWAC.first.is_null()) {
     // TODO: We could use a heuristic and either use:
     //         SCEVAffinator::takeNonNegativeAssumption

diff  --git a/polly/lib/Support/SCEVAffinator.cpp b/polly/lib/Support/SCEVAffinator.cpp
index b55fa62f0e187..f8151d97591ff 100644
--- a/polly/lib/Support/SCEVAffinator.cpp
+++ b/polly/lib/Support/SCEVAffinator.cpp
@@ -115,8 +115,10 @@ PWACtx SCEVAffinator::getPWACtxFromPWA(isl::pw_aff PWA) {
 }
 
 PWACtx SCEVAffinator::getPwAff(const SCEV *Expr, BasicBlock *BB,
-                               RecordedAssumptionsTy *RecordedAssumptions) {
+                               RecordedAssumptionsTy *RecordedAssumptions,
+                               bool IsInsideDomain) {
   this->BB = BB;
+  this->IsInsideDomain = IsInsideDomain;
   this->RecordedAssumptions = RecordedAssumptions;
 
   if (BB) {
@@ -309,7 +311,7 @@ PWACtx SCEVAffinator::visitTruncateExpr(const SCEVTruncateExpr *Expr) {
   }
 
   recordAssumption(RecordedAssumptions, UNSIGNED, isl::manage(OutOfBoundsDom),
-                   DebugLoc(), AS_RESTRICTION, BB);
+                   DebugLoc(), AS_RESTRICTION, IsInsideDomain ? BB : nullptr);
 
   return OpPWAC;
 }

diff  --git a/polly/lib/Support/ScopHelper.cpp b/polly/lib/Support/ScopHelper.cpp
index a9333e23d2839..6408ec6bbde40 100644
--- a/polly/lib/Support/ScopHelper.cpp
+++ b/polly/lib/Support/ScopHelper.cpp
@@ -210,8 +210,6 @@ void polly::recordAssumption(polly::RecordedAssumptionsTy *RecordedAssumptions,
                              polly::AssumptionKind Kind, isl::set Set,
                              DebugLoc Loc, polly::AssumptionSign Sign,
                              BasicBlock *BB, bool RTC) {
-  assert((Set.is_params() || BB) &&
-         "Assumptions without a basic block must be parameter sets");
   if (RecordedAssumptions)
     RecordedAssumptions->push_back({Kind, Sign, Set, Loc, BB, RTC});
 }

diff  --git a/polly/test/ScopInfo/issue190128.ll b/polly/test/ScopInfo/issue190128.ll
new file mode 100644
index 0000000000000..a56fcfc926256
--- /dev/null
+++ b/polly/test/ScopInfo/issue190128.ll
@@ -0,0 +1,71 @@
+; RUN: opt %loadNPMPolly '-passes=polly-custom<scops>' -polly-print-scops -disable-output < %s 2>&1 | FileCheck %s
+;
+; https://github.com/llvm/llvm-project/issues/190128
+;
+; void func(int arg, unsigned char arr_4[11]) {
+;     int shl1 = 2147483592LL << arg; // 2147483592
+;     int trunc1 = (short)(shl1); // -56
+;     int start = trunc1 + 56; // = 0
+;     for (short i_0 = start; i_0 < 1; i_0 += 1) {
+;        arr_4[i_0] = i_0;
+;        for (int i_2 = 1; i_2 < 3; i_2 += 1)
+;          ; // somehow this matters -- 
diff erent CFG
+;     }
+; }
+;
+; The constraint -58 <= shl < 32768 (ignorable trunc range) must be checked in an RTC (here: InvalidConstant).
+; Alternatively, %conv6 could be used as a parameter, instead of %shl.
+
+; CHECK:      Context:
+; CHECK-NEXT: [shl] -> {  : -9223372036854775808 <= shl <= 9223372036854775800 }
+; CHECK:      Assumed Context:
+; CHECK-NEXT: [shl] -> {  :  }
+; CHECK:      Invalid Context:
+; CHECK-NEXT: [shl] -> { : shl <= -57 or shl >= 32768 }
+; CHECK:      Defined Behavior Context:
+; CHECK-NEXT: [shl] -> {  : -56 <= shl <= 32710 }
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+
+; Function Attrs: nofree noinline norecurse nosync nounwind memory(argmem: write) uwtable
+define dso_local void @func(i32 noundef %arg, ptr noundef writeonly captures(none) %arr_4) local_unnamed_addr #0 {
+entry:
+  %sh_prom = zext nneg i32 %arg to i64
+  %shl = shl i64 2147483592, %sh_prom
+  %conv1 = trunc i64 %shl to i16
+  %add = add i16 %conv1, 56
+  %cmp22 = icmp slt i16 %add, 1
+  br i1 %cmp22, label %for.body, label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.body, %entry
+  ret void
+
+for.body:                                         ; preds = %for.body, %entry
+  %i_0.023 = phi i16 [ %add15, %for.body ], [ %add, %entry ]
+  %conv6 = trunc i16 %i_0.023 to i8
+  %idxprom = sext i16 %i_0.023 to i64
+  %arrayidx = getelementptr inbounds i8, ptr %arr_4, i64 %idxprom
+  store i8 %conv6, ptr %arrayidx, align 1, !tbaa !8
+  %add15 = add nsw i16 %i_0.023, 1
+  %cmp = icmp ugt i16 %i_0.023, 32766
+  br i1 %cmp, label %for.body, label %for.cond.cleanup, !llvm.loop !9
+}
+
+attributes #0 = { nofree noinline norecurse nosync nounwind memory(argmem: write) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.ident = !{!3}
+!llvm.errno.tbaa = !{!4}
+
+!0 = !{i32 8, !"PIC Level", i32 2}
+!1 = !{i32 7, !"PIE Level", i32 2}
+!2 = !{i32 7, !"uwtable", i32 2}
+!3 = !{!"clang version 23.0.0git (/home/meinersbur/src/llvm/polly/_src/clang a2d3783b451c0c19a5eb09b1ab9a1c66d81ab6ca)"}
+!4 = !{!5, !5, i64 0}
+!5 = !{!"int", !6, i64 0}
+!6 = !{!"omnipotent char", !7, i64 0}
+!7 = !{!"Simple C/C++ TBAA"}
+!8 = !{!6, !6, i64 0}
+!9 = distinct !{!9, !10, !11}
+!10 = !{!"llvm.loop.mustprogress"}
+!11 = !{!"llvm.loop.unroll.disable"}

diff  --git a/polly/test/ScopInfo/zero_ext_of_truncate.ll b/polly/test/ScopInfo/zero_ext_of_truncate.ll
index b509951bbf0d5..e152f141d5617 100644
--- a/polly/test/ScopInfo/zero_ext_of_truncate.ll
+++ b/polly/test/ScopInfo/zero_ext_of_truncate.ll
@@ -9,10 +9,14 @@
 ;    }
 ;
 ; FIXME: The truncated value should be a parameter.
+; CHECK:         {{^ *}}Context:
+; CHECK-NEXT:    [N, tmp, M] -> { : -2147483648 <= N <= 2147483647 and -2147483648 <= tmp <= 2147483647 and -2147483648 <= M <= 2147483647 }
 ; CHECK:         Assumed Context:
 ; CHECK-NEXT:    [N, tmp, M] -> { : }
-; CHECK-NEXT:    Invalid Context:
-; CHECK-NEXT:    [N, tmp, M] -> { : N < 0 or (N > 0 and tmp >= 128) or (N > 0 and tmp < 0) or (N > 0 and M < 0) }
+; CHECK:         Invalid Context:
+; CHECK-NEXT:    [N, tmp, M] -> { : tmp <= -129 or tmp >= 128 or N < 0 or (N > 0 and tmp < 0) or (N > 0 and M < 0) }
+; CHECK:         Defined Behavior Context:
+; CHECK-NEXT:    [N, tmp, M] -> { : -128 <= tmp <= 127 and -2147483648 <= M <= 2147483647 and ((0 < N <= 2147483647 and tmp >= 0 and M >= 0) or N = 0) }
 ;
 ; CHECK:         Domain :=
 ; CHECK-NEXT:    [N, tmp, M] -> { Stmt_if_then[i0] : tmp >= 0 and M > tmp and 0 <= i0 < N };

diff  --git a/polly/test/ScopInfo/zero_ext_of_truncate_2.ll b/polly/test/ScopInfo/zero_ext_of_truncate_2.ll
index ea3356e01cc9f..ee68054b06096 100644
--- a/polly/test/ScopInfo/zero_ext_of_truncate_2.ll
+++ b/polly/test/ScopInfo/zero_ext_of_truncate_2.ll
@@ -8,10 +8,14 @@
 ;      }
 ;    }
 ;
+; CHECK:         {{^ *}}Context:
+; CHECK-NEXT:    [N, tmp] -> {  : -2147483648 <= N <= 2147483647 and -9223372036854775808 <= tmp <= 9223372036854775807 }
 ; CHECK:         Assumed Context:
-; CHECK-NEXT:    [N, tmp] -> { : }
-; CHECK-NEXT:    Invalid Context:
-; CHECK-NEXT:    [N, tmp] -> { : N > 0 and (tmp < 0 or tmp >= 2147483648) }
+; CHECK-NEXT:    [N, tmp] -> {  :  }
+; CHECK:         Invalid Context:
+; CHECK-NEXT:    [N, tmp] -> {  : tmp <= -2147483649 or tmp >= 2147483648 or (N > 0 and tmp < 0) }
+; CHECK:         Defined Behavior Context:
+; CHECK-NEXT:    [N, tmp] -> {  : -2147483648 <= tmp <= 2147483647 and ((0 < N <= 2147483647 and tmp >= 0) or N = 0) }
 ;
 ; CHECK:         Domain :=
 ; CHECK-NEXT:    [N, tmp] -> { Stmt_if_then[i0] : tmp >= 0 and tmp < i0 < N };


        


More information about the llvm-commits mailing list