[llvm] [BasicAA] Account for wrapping when using abs(Scale*V0 + (-Scale)*V1) >= abs(Scale) (PR #137753)

via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 28 21:52:11 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Craig Topper (topperc)

<details>
<summary>Changes</summary>

Similar to 1b7ef6aac8a3cad245c0ed14fe21725e31261f73, add a check to
only set MinAbsVarIndex if abs(Scale*V0) and abs((-Scale)*V1) won't
wrap. In the absence of IsNSW, try to use the bitwidths of the
original V and Scale to rule out wrapping,

---
Full diff: https://github.com/llvm/llvm-project/pull/137753.diff


2 Files Affected:

- (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+19-17) 
- (added) llvm/test/Transforms/InstCombine/RISCV/sifive-test-xor-int-min.ll (+102) 


``````````diff
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index cbec13c7808be..03977551afadd 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -1301,6 +1301,23 @@ AliasResult BasicAAResult::aliasGEP(
   if (Range1.intersectWith(Range2).isEmptySet())
     return AliasResult::NoAlias;
 
+  // Check if abs(V*Scale) >= abs(Scale) holds in the presence of
+  // potentially wrapping math.
+  auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) {
+    if (Var.IsNSW)
+      return true;
+
+    int ValOrigBW = Var.Val.V->getType()->getPrimitiveSizeInBits();
+    // If Scale is small enough so that abs(V*Scale) >= abs(Scale) holds.
+    // The max value of abs(V) is 2^ValOrigBW - 1. Multiplying with a
+    // constant smaller than 2^(bitwidth(Val) - ValOrigBW) won't wrap.
+    int MaxScaleValueBW = Var.Val.getBitWidth() - ValOrigBW;
+    if (MaxScaleValueBW <= 0)
+      return false;
+    return Var.Scale.ule(
+        APInt::getMaxValue(MaxScaleValueBW).zext(Var.Scale.getBitWidth()));
+  };
+
   // Try to determine the range of values for VarIndex such that
   // VarIndex <= -MinAbsVarIndex || MinAbsVarIndex <= VarIndex.
   std::optional<APInt> MinAbsVarIndex;
@@ -1309,22 +1326,6 @@ AliasResult BasicAAResult::aliasGEP(
     const VariableGEPIndex &Var = DecompGEP1.VarIndices[0];
     if (Var.Val.TruncBits == 0 &&
         isKnownNonZero(Var.Val.V, SimplifyQuery(DL, DT, &AC, Var.CxtI))) {
-      // Check if abs(V*Scale) >= abs(Scale) holds in the presence of
-      // potentially wrapping math.
-      auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) {
-        if (Var.IsNSW)
-          return true;
-
-        int ValOrigBW = Var.Val.V->getType()->getPrimitiveSizeInBits();
-        // If Scale is small enough so that abs(V*Scale) >= abs(Scale) holds.
-        // The max value of abs(V) is 2^ValOrigBW - 1. Multiplying with a
-        // constant smaller than 2^(bitwidth(Val) - ValOrigBW) won't wrap.
-        int MaxScaleValueBW = Var.Val.getBitWidth() - ValOrigBW;
-        if (MaxScaleValueBW <= 0)
-          return false;
-        return Var.Scale.ule(
-            APInt::getMaxValue(MaxScaleValueBW).zext(Var.Scale.getBitWidth()));
-      };
       // Refine MinAbsVarIndex, if abs(Scale*V) >= abs(Scale) holds in the
       // presence of potentially wrapping math.
       if (MultiplyByScaleNoWrap(Var)) {
@@ -1345,7 +1346,8 @@ AliasResult BasicAAResult::aliasGEP(
                         SimplifyQuery(DL, DT, &AC, /*CxtI=*/Var0.CxtI
                                                        ? Var0.CxtI
                                                        : Var1.CxtI)))
-      MinAbsVarIndex = Var0.Scale.abs();
+      if (MultiplyByScaleNoWrap(Var0) && MultiplyByScaleNoWrap(Var1))
+        MinAbsVarIndex = Var0.Scale.abs();
   }
 
   if (MinAbsVarIndex) {
diff --git a/llvm/test/Transforms/InstCombine/RISCV/sifive-test-xor-int-min.ll b/llvm/test/Transforms/InstCombine/RISCV/sifive-test-xor-int-min.ll
new file mode 100644
index 0000000000000..4966af4c23bf2
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/RISCV/sifive-test-xor-int-min.ll
@@ -0,0 +1,102 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -mtriple=riscv32 -passes='instcombine' -mtriple=riscv32-unknown-unknown-elf -S < %s \
+; RUN:   | FileCheck %s
+
+target triple = "riscv32-unknown-unknown-elf"
+
+define dso_local i32 @main() local_unnamed_addr #2 {
+; CHECK-LABEL: @main(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A0:%.*]] = alloca [16 x i32], align 4
+; CHECK-NEXT:    [[A2:%.*]] = alloca [16 x i32], align 4
+; CHECK-NEXT:    [[A3:%.*]] = alloca [16 x i32], align 4
+; CHECK-NEXT:    br label [[FOR_COND:%.*]]
+; CHECK:       for.cond:
+; CHECK-NEXT:    [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ult i32 [[I_0]], 6
+; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]]
+; CHECK:       for.cond.cleanup:
+; CHECK-NEXT:    [[ARRAYIDX7:%.*]] = getelementptr inbounds nuw i8, ptr [[A0]], i32 4
+; CHECK-NEXT:    [[INVARIANT_GEP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[A0]], i32 4
+; CHECK-NEXT:    br label [[FOR_BODY_I:%.*]]
+; CHECK:       for.body.i:
+; CHECK-NEXT:    [[I_019_I:%.*]] = phi i32 [ [[INC_I:%.*]], [[FOR_BODY_I]] ], [ -2147483648, [[FOR_COND_CLEANUP]] ]
+; CHECK-NEXT:    [[SUB_I:%.*]] = xor i32 [[I_019_I]], -2147483648
+; CHECK-NEXT:    [[ARRAYIDX_I:%.*]] = getelementptr inbounds nuw i32, ptr [[ARRAYIDX7]], i32 [[SUB_I]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX_I]], align 4
+; CHECK-NEXT:    [[ARRAYIDX1_I:%.*]] = getelementptr inbounds nuw i32, ptr [[A3]], i32 [[SUB_I]]
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX1_I]], align 4
+; CHECK-NEXT:    [[ADD2_I:%.*]] = add nsw i32 [[TMP1]], [[TMP0]]
+; CHECK-NEXT:    [[GEP_I:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP_I]], i32 [[I_019_I]]
+; CHECK-NEXT:    store i32 [[ADD2_I]], ptr [[GEP_I]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX_I]], align 4
+; CHECK-NEXT:    [[ARRAYIDX6_I:%.*]] = getelementptr inbounds nuw i32, ptr [[A2]], i32 [[SUB_I]]
+; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX6_I]], align 4
+; CHECK-NEXT:    [[ADD7_I:%.*]] = add nsw i32 [[TMP3]], [[TMP2]]
+; CHECK-NEXT:    [[ARRAYIDX8_I:%.*]] = getelementptr inbounds nuw i32, ptr [[A0]], i32 [[SUB_I]]
+; CHECK-NEXT:    store i32 [[ADD7_I]], ptr [[ARRAYIDX8_I]], align 4
+; CHECK-NEXT:    [[INC_I]] = add nuw nsw i32 [[I_019_I]], 1
+; CHECK-NEXT:    [[EXITCOND_NOT_I:%.*]] = icmp eq i32 [[INC_I]], -2147483642
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT_I]], label [[S2244_EXIT:%.*]], label [[FOR_BODY_I]]
+; CHECK:       s2244.exit:
+; CHECK-NEXT:    ret i32 0
+; CHECK:       for.body:
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i32, ptr [[A0]], i32 [[I_0]]
+; CHECK-NEXT:    store i32 1, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT:    [[ARRAYIDX5:%.*]] = getelementptr inbounds nuw i32, ptr [[A2]], i32 [[I_0]]
+; CHECK-NEXT:    store i32 1, ptr [[ARRAYIDX5]], align 4
+; CHECK-NEXT:    [[ARRAYIDX6:%.*]] = getelementptr inbounds nuw i32, ptr [[A3]], i32 [[I_0]]
+; CHECK-NEXT:    store i32 1, ptr [[ARRAYIDX6]], align 4
+; CHECK-NEXT:    [[INC]] = add nuw nsw i32 [[I_0]], 1
+; CHECK-NEXT:    br label [[FOR_COND]]
+;
+entry:
+  %a0 = alloca [16 x i32], align 4
+  %a2 = alloca [16 x i32], align 4
+  %a3 = alloca [16 x i32], align 4
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.body, %entry
+  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %cmp = icmp samesign ult i32 %i.0, 6
+  br i1 %cmp, label %for.body, label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  %arrayidx7 = getelementptr inbounds nuw i8, ptr %a0, i32 4
+  %invariant.gep.i = getelementptr i8, ptr %a0, i32 4
+  br label %for.body.i
+
+for.body.i:                                       ; preds = %for.body.i, %for.cond.cleanup
+  %i.019.i = phi i32 [ %inc.i, %for.body.i ], [ -2147483648, %for.cond.cleanup ]
+  %sub.i = xor i32 %i.019.i, -2147483648
+  %arrayidx.i = getelementptr inbounds nuw i32, ptr %arrayidx7, i32 %sub.i
+  %0 = load i32, ptr %arrayidx.i, align 4
+  %arrayidx1.i = getelementptr inbounds nuw i32, ptr %a3, i32 %sub.i
+  %1 = load i32, ptr %arrayidx1.i, align 4
+  %add2.i = add nsw i32 %1, %0
+  %gep.i = getelementptr i32, ptr %invariant.gep.i, i32 %i.019.i
+  store i32 %add2.i, ptr %gep.i, align 4
+  %2 = load i32, ptr %arrayidx.i, align 4
+  %arrayidx6.i = getelementptr inbounds nuw i32, ptr %a2, i32 %sub.i
+  %3 = load i32, ptr %arrayidx6.i, align 4
+  %add7.i = add nsw i32 %3, %2
+  %arrayidx8.i = getelementptr inbounds nuw i32, ptr %a0, i32 %sub.i
+  store i32 %add7.i, ptr %arrayidx8.i, align 4
+  %inc.i = add nuw nsw i32 %i.019.i, 1
+  %exitcond.not.i = icmp eq i32 %inc.i, -2147483642
+  br i1 %exitcond.not.i, label %s2244.exit, label %for.body.i
+
+s2244.exit:                                       ; preds = %for.body.i
+  %4 = load i32, ptr %a0, align 4
+  ret i32 0
+
+for.body:                                         ; preds = %for.cond
+  %arrayidx = getelementptr inbounds nuw i32, ptr %a0, i32 %i.0
+  store i32 1, ptr %arrayidx, align 4
+  %arrayidx5 = getelementptr inbounds nuw i32, ptr %a2, i32 %i.0
+  store i32 1, ptr %arrayidx5, align 4
+  %arrayidx6 = getelementptr inbounds nuw i32, ptr %a3, i32 %i.0
+  store i32 1, ptr %arrayidx6, align 4
+  %inc = add nuw nsw i32 %i.0, 1
+  br label %for.cond
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/137753


More information about the llvm-commits mailing list