[llvm] [IPSCCP] Add range attribute handling. (PR #86747)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 10 12:08:47 PDT 2024


https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/86747

>From 9f2fb2a8a51fee3c26ba735e3c64300578ad8c3e Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Thu, 4 Apr 2024 20:47:10 +0200
Subject: [PATCH 1/2] [IPSCCP] add test for range attribute handling. NFC.

---
 llvm/test/Transforms/SCCP/range-attribute.ll | 163 +++++++++++++++++++
 1 file changed, 163 insertions(+)
 create mode 100644 llvm/test/Transforms/SCCP/range-attribute.ll

diff --git a/llvm/test/Transforms/SCCP/range-attribute.ll b/llvm/test/Transforms/SCCP/range-attribute.ll
new file mode 100644
index 00000000000000..ea1e88a7939b35
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/range-attribute.ll
@@ -0,0 +1,163 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
+
+declare void @use(i1)
+declare i32 @get_i32()
+
+define void @range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @range_attribute(
+; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C2]])
+; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT:    call void @use(i1 [[C4]])
+; CHECK-NEXT:    ret void
+;
+  %c1 = icmp ult i32 %v, 10
+  call void @use(i1 %c1)
+  %c2 = icmp ult i32 %v, 9
+  call void @use(i1 %c2)
+  %c3 = icmp ugt i32 %v, 9
+  call void @use(i1 %c3)
+  %c4 = icmp ugt i32 %v, 8
+  call void @use(i1 %c4)
+  ret void
+}
+
+define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
+; CHECK-LABEL: @range_attribute_single(
+; CHECK-NEXT:    ret i32 [[V:%.*]]
+;
+  ret i32 %v
+}
+
+define void @call_range_attribute() {
+; CHECK-LABEL: @call_range_attribute(
+; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C2]])
+; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT:    call void @use(i1 [[C4]])
+; CHECK-NEXT:    ret void
+;
+  %v = call range(i32 0, 10) i32 @get_i32()
+  %c1 = icmp ult i32 %v, 10
+  call void @use(i1 %c1)
+  %c2 = icmp ult i32 %v, 9
+  call void @use(i1 %c2)
+  %c3 = icmp ugt i32 %v, 9
+  call void @use(i1 %c3)
+  %c4 = icmp ugt i32 %v, 8
+  call void @use(i1 %c4)
+  ret void
+}
+
+
+declare range(i32 0, 10) i32 @get_i32_in_range()
+
+define void @call_range_result() {
+; CHECK-LABEL: @call_range_result(
+; CHECK-NEXT:    [[V:%.*]] = call i32 @get_i32_in_range()
+; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C2]])
+; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT:    call void @use(i1 [[C4]])
+; CHECK-NEXT:    ret void
+;
+  %v = call i32 @get_i32_in_range()
+  %c1 = icmp ult i32 %v, 10
+  call void @use(i1 %c1)
+  %c2 = icmp ult i32 %v, 9
+  call void @use(i1 %c2)
+  %c3 = icmp ugt i32 %v, 9
+  call void @use(i1 %c3)
+  %c4 = icmp ugt i32 %v, 8
+  call void @use(i1 %c4)
+  ret void
+}
+
+define internal i1 @ip_cmp_range_attribute(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_attribute(
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %c = icmp ult i32 %v, 10
+  ret i1 %c
+}
+
+define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @ip_range_attribute(
+; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]])
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %c = call i1 @ip_cmp_range_attribute(i32 %v)
+  ret i1 %c
+}
+
+define internal i1 @ip_cmp_range_call(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_call(
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %c = icmp ult i32 %v, 10
+  ret i1 %c
+}
+
+define i1 @ip_range_call() {
+; CHECK-LABEL: @ip_range_call(
+; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]])
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %v = call range(i32 0, 10) i32 @get_i32()
+  %c = call i1 @ip_cmp_range_call(i32 %v)
+  ret i1 %c
+}
+
+define internal i1 @ip_cmp_range_result(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_result(
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %c = icmp ult i32 %v, 10
+  ret i1 %c
+}
+
+define i1 @ip_range_result() {
+; CHECK-LABEL: @ip_range_result(
+; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]])
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %v = call range(i32 0, 10) i32 @get_i32()
+  %c = call i1 @ip_cmp_range_result(i32 %v)
+  ret i1 %c
+}
+
+define internal i1 @ip_cmp_with_range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @ip_cmp_with_range_attribute(
+; CHECK-NEXT:    ret i1 undef
+;
+  %c = icmp eq i32 %v, 5
+  ret i1 %c
+}
+
+define i1 @ip_range_attribute_constant() {
+; CHECK-LABEL: @ip_range_attribute_constant(
+; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_with_range_attribute(i32 5)
+; CHECK-NEXT:    ret i1 true
+;
+  %c = call i1 @ip_cmp_with_range_attribute(i32 5)
+  ret i1 %c
+}

>From fa71860e1937b916758693e268d5a16021c8b5c3 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Wed, 10 Apr 2024 21:05:00 +0200
Subject: [PATCH 2/2] [IPSCCP] Add range attribute handling.

---
 .../llvm/Transforms/Utils/SCCPSolver.h        |  4 ++
 llvm/lib/Transforms/IPO/SCCP.cpp              |  3 +-
 llvm/lib/Transforms/Utils/SCCPSolver.cpp      | 40 ++++++++++++++++++-
 llvm/test/Transforms/SCCP/range-attribute.ll  | 37 +++++++----------
 4 files changed, 57 insertions(+), 27 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
index 1a95f80812aabd..9f7ccd4a8a32cf 100644
--- a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
+++ b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
@@ -151,6 +151,10 @@ class SCCPSolver {
   /// works with both scalars and structs.
   void markOverdefined(Value *V);
 
+  /// trackValueOfArgument - Mark the specified argument overdefined unless it
+  /// have range attribute.  This works with both scalars and structs.
+  void trackValueOfArgument(Argument *V);
+
   // isStructLatticeConstant - Return true if all the lattice values
   // corresponding to elements of the structure are constants,
   // false otherwise.
diff --git a/llvm/lib/Transforms/IPO/SCCP.cpp b/llvm/lib/Transforms/IPO/SCCP.cpp
index b1f9b827dcbaf3..f8920541e6fd64 100644
--- a/llvm/lib/Transforms/IPO/SCCP.cpp
+++ b/llvm/lib/Transforms/IPO/SCCP.cpp
@@ -144,9 +144,8 @@ static bool runIPSCCP(
     // Assume the function is called.
     Solver.markBlockExecutable(&F.front());
 
-    // Assume nothing about the incoming arguments.
     for (Argument &AI : F.args())
-      Solver.markOverdefined(&AI);
+      Solver.trackValueOfArgument(&AI);
   }
 
   // Determine if we can track any of the module's global variables. If so, add
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 38fc7763c5b204..b82ed25a6d0cf5 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -428,6 +428,13 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
     return markConstant(ValueState[V], V, C);
   }
 
+  /// markConstantRange - Mark the object as constant range with \p CR. If the
+  /// object is not a constant range with the range \p CR, add it to the
+  /// instruction work list so that the users of the instruction are updated
+  /// later.
+  bool markConstantRange(ValueLatticeElement &IV, Value *V,
+                         const ConstantRange &CR);
+
   // markOverdefined - Make a value be marked as "overdefined". If the
   // value is not already overdefined, add it to the overdefined instruction
   // work list so that the users of the instruction are updated later.
@@ -788,6 +795,17 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
       markOverdefined(ValueState[V], V);
   }
 
+  void trackValueOfArgument(Argument *A) {
+    if (A->getType()->isIntegerTy()) {
+      if (std::optional<ConstantRange> Range = A->getRange()) {
+        markConstantRange(ValueState[A], A, *Range);
+        return;
+      }
+    }
+    // Assume nothing about the incoming arguments without range.
+    markOverdefined(A);
+  }
+
   bool isStructLatticeConstant(Function *F, StructType *STy);
 
   Constant *getConstant(const ValueLatticeElement &LV, Type *Ty) const;
@@ -873,6 +891,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
   return true;
 }
 
+bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
+                                        const ConstantRange &CR) {
+  if (!IV.markConstantRange(CR))
+    return false;
+  LLVM_DEBUG(dbgs() << "markConstantRange: " << CR << ": " << *V << '\n');
+  pushToWorkList(IV, V);
+  return true;
+}
+
 bool SCCPInstVisitor::markOverdefined(ValueLatticeElement &IV, Value *V) {
   if (!IV.markOverdefined())
     return false;
@@ -1581,10 +1608,15 @@ void SCCPInstVisitor::visitStoreInst(StoreInst &SI) {
 }
 
 static ValueLatticeElement getValueFromMetadata(const Instruction *I) {
-  if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
-    if (I->getType()->isIntegerTy())
+  if (I->getType()->isIntegerTy()) {
+    if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
       return ValueLatticeElement::getRange(
           getConstantRangeFromMetadata(*Ranges));
+
+    if (const auto *CB = dyn_cast<CallBase>(I))
+      if (std::optional<ConstantRange> Range = CB->getRange())
+        return ValueLatticeElement::getRange(*Range);
+  }
   if (I->hasMetadata(LLVMContext::MD_nonnull))
     return ValueLatticeElement::getNot(
         ConstantPointerNull::get(cast<PointerType>(I->getType())));
@@ -2090,6 +2122,10 @@ const SmallPtrSet<Function *, 16> SCCPSolver::getMRVFunctionsTracked() {
 
 void SCCPSolver::markOverdefined(Value *V) { Visitor->markOverdefined(V); }
 
+void SCCPSolver::trackValueOfArgument(Argument *V) {
+  Visitor->trackValueOfArgument(V);
+}
+
 bool SCCPSolver::isStructLatticeConstant(Function *F, StructType *STy) {
   return Visitor->isStructLatticeConstant(F, STy);
 }
diff --git a/llvm/test/Transforms/SCCP/range-attribute.ll b/llvm/test/Transforms/SCCP/range-attribute.ll
index ea1e88a7939b35..ae66af91bb8e35 100644
--- a/llvm/test/Transforms/SCCP/range-attribute.ll
+++ b/llvm/test/Transforms/SCCP/range-attribute.ll
@@ -6,12 +6,10 @@ declare i32 @get_i32()
 
 define void @range_attribute(i32 range(i32 0, 10) %v) {
 ; CHECK-LABEL: @range_attribute(
-; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT:    call void @use(i1 [[C1]])
-; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V:%.*]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C2]])
-; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    call void @use(i1 false)
 ; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
 ; CHECK-NEXT:    call void @use(i1 [[C4]])
 ; CHECK-NEXT:    ret void
@@ -29,7 +27,7 @@ define void @range_attribute(i32 range(i32 0, 10) %v) {
 
 define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
 ; CHECK-LABEL: @range_attribute_single(
-; CHECK-NEXT:    ret i32 [[V:%.*]]
+; CHECK-NEXT:    ret i32 0
 ;
   ret i32 %v
 }
@@ -37,12 +35,10 @@ define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
 define void @call_range_attribute() {
 ; CHECK-LABEL: @call_range_attribute(
 ; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
-; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C2]])
-; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    call void @use(i1 false)
 ; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
 ; CHECK-NEXT:    call void @use(i1 [[C4]])
 ; CHECK-NEXT:    ret void
@@ -65,12 +61,10 @@ declare range(i32 0, 10) i32 @get_i32_in_range()
 define void @call_range_result() {
 ; CHECK-LABEL: @call_range_result(
 ; CHECK-NEXT:    [[V:%.*]] = call i32 @get_i32_in_range()
-; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C2]])
-; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    call void @use(i1 false)
 ; CHECK-NEXT:    [[C4:%.*]] = icmp ugt i32 [[V]], 8
 ; CHECK-NEXT:    call void @use(i1 [[C4]])
 ; CHECK-NEXT:    ret void
@@ -89,8 +83,7 @@ define void @call_range_result() {
 
 define internal i1 @ip_cmp_range_attribute(i32 %v) {
 ; CHECK-LABEL: @ip_cmp_range_attribute(
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 undef
 ;
   %c = icmp ult i32 %v, 10
   ret i1 %c
@@ -99,7 +92,7 @@ define internal i1 @ip_cmp_range_attribute(i32 %v) {
 define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
 ; CHECK-LABEL: @ip_range_attribute(
 ; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]])
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %c = call i1 @ip_cmp_range_attribute(i32 %v)
   ret i1 %c
@@ -107,8 +100,7 @@ define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
 
 define internal i1 @ip_cmp_range_call(i32 %v) {
 ; CHECK-LABEL: @ip_cmp_range_call(
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 undef
 ;
   %c = icmp ult i32 %v, 10
   ret i1 %c
@@ -118,7 +110,7 @@ define i1 @ip_range_call() {
 ; CHECK-LABEL: @ip_range_call(
 ; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
 ; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]])
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %v = call range(i32 0, 10) i32 @get_i32()
   %c = call i1 @ip_cmp_range_call(i32 %v)
@@ -127,8 +119,7 @@ define i1 @ip_range_call() {
 
 define internal i1 @ip_cmp_range_result(i32 %v) {
 ; CHECK-LABEL: @ip_cmp_range_result(
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 undef
 ;
   %c = icmp ult i32 %v, 10
   ret i1 %c
@@ -138,7 +129,7 @@ define i1 @ip_range_result() {
 ; CHECK-LABEL: @ip_range_result(
 ; CHECK-NEXT:    [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
 ; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]])
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %v = call range(i32 0, 10) i32 @get_i32()
   %c = call i1 @ip_cmp_range_result(i32 %v)



More information about the llvm-commits mailing list