[llvm] [ValueTracking] Handle range attributes (PR #85143)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 17 15:25:53 PDT 2024


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

>From 096fcfaec02da054ab172f8417a1018462d868d8 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 16 Mar 2024 15:39:25 +0100
Subject: [PATCH 1/7] Add helper functions to get range attribute

---
 llvm/include/llvm/IR/Argument.h           |  7 +++++++
 llvm/include/llvm/IR/InstrTypes.h         |  7 ++++++-
 llvm/lib/Analysis/InstructionSimplify.cpp | 13 ++++---------
 llvm/lib/IR/Function.cpp                  |  8 ++++++++
 llvm/lib/IR/Instructions.cpp              |  8 ++++++++
 5 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h
index f0c0ce75d2b7e1..98d6f34272579e 100644
--- a/llvm/include/llvm/IR/Argument.h
+++ b/llvm/include/llvm/IR/Argument.h
@@ -16,9 +16,12 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/Value.h"
+#include <optional>
 
 namespace llvm {
 
+class ConstantRange;
+
 /// This class represents an incoming formal argument to a Function. A formal
 /// argument, since it is ``formal'', does not contain an actual value but
 /// instead represents the type, argument number, and attributes of an argument
@@ -67,6 +70,10 @@ class Argument final : public Value {
   /// disallowed floating-point values. Otherwise, fcNone is returned.
   FPClassTest getNoFPClass() const;
 
+  /// If this argument has range attribute, return the value range of the
+  /// argument. Otherwise, std::nullopt is returned.
+  std::optional<ConstantRange> getRange() const;
+
   /// Return true if this argument has the byval attribute.
   bool hasByValAttr() const;
 
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index fed21b992e3d10..61f0fa14fbe34e 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -43,6 +43,7 @@ namespace llvm {
 class StringRef;
 class Type;
 class Value;
+class ConstantRange;
 
 namespace Intrinsic {
 typedef unsigned ID;
@@ -1917,7 +1918,7 @@ class CallBase : public Instruction {
 
     // Look at the callee, if available.
     if (const Function *F = getCalledFunction())
-      return F->getAttributes().getRetAttr(Kind);
+      return F->getRetAttribute(Kind);
     return Attribute();
   }
 
@@ -2154,6 +2155,10 @@ class CallBase : public Instruction {
   /// parameter.
   FPClassTest getParamNoFPClass(unsigned i) const;
 
+  /// If this return value has range attribute, return the value range of the
+  /// argument. Otherwise, std::nullopt is returned.
+  std::optional<ConstantRange> getRange() const;
+
   /// Return true if the return value is known to be not null.
   /// This may be because it has the nonnull attribute, or because at least
   /// one byte is dereferenceable and the pointer is in addrspace(0).
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ce651783caf16b..383c4259c81ffa 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3736,15 +3736,10 @@ static std::optional<ConstantRange> getRange(Value *V,
     if (MDNode *MD = IIQ.getMetadata(I, LLVMContext::MD_range))
       return getConstantRangeFromMetadata(*MD);
 
-  Attribute Range;
-  if (const Argument *A = dyn_cast<Argument>(V)) {
-    Range = A->getAttribute(llvm::Attribute::Range);
-  } else if (const CallBase *CB = dyn_cast<CallBase>(V)) {
-    Range = CB->getRetAttr(llvm::Attribute::Range);
-  }
-
-  if (Range.isValid())
-    return Range.getRange();
+  if (const Argument *A = dyn_cast<Argument>(V))
+    return A->getRange();
+  else if (const CallBase *CB = dyn_cast<CallBase>(V))
+    return CB->getRange();
 
   return std::nullopt;
 }
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index d22e1c12311189..eb126f182eadcb 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -24,6 +24,7 @@
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/GlobalValue.h"
@@ -256,6 +257,13 @@ FPClassTest Argument::getNoFPClass() const {
   return getParent()->getParamNoFPClass(getArgNo());
 }
 
+std::optional<ConstantRange> Argument::getRange() const {
+  const Attribute RangeAttr = getAttribute(llvm::Attribute::Range);
+  if (RangeAttr.isValid())
+    return RangeAttr.getRange();
+  return std::nullopt;
+}
+
 bool Argument::hasNestAttr() const {
   if (!getType()->isPointerTy()) return false;
   return hasAttribute(Attribute::Nest);
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index c55d6cff84ed55..494d50f89e374c 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -19,6 +19,7 @@
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -395,6 +396,13 @@ FPClassTest CallBase::getParamNoFPClass(unsigned i) const {
   return Mask;
 }
 
+std::optional<ConstantRange> CallBase::getRange() const {
+  const Attribute RangeAttr = getRetAttr(llvm::Attribute::Range);
+  if (RangeAttr.isValid())
+    return RangeAttr.getRange();
+  return std::nullopt;
+}
+
 bool CallBase::isReturnNonNull() const {
   if (hasRetAttr(Attribute::NonNull))
     return true;

>From af880340580f58801e1dabcc01feed716fe75084 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 12:03:32 +0100
Subject: [PATCH 2/7] pre-commit tests for range attributes in valuetracking

---
 .../Transforms/InstSimplify/icmp-constant.ll  | 264 ++++++++++++++++++
 .../InstSimplify/shift-knownbits.ll           | 134 +++++++++
 2 files changed, 398 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/icmp-constant.ll b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
index 04261f6f40b7c3..33bb9ea127cc69 100644
--- a/llvm/test/Transforms/InstSimplify/icmp-constant.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
@@ -1140,3 +1140,267 @@ define <2 x i1> @heterogeneous_constvector(<2 x i8> %x) {
   %c = icmp ult <2 x i8> %x, <i8 undef, i8 poison>
   ret <2 x i1> %c
 }
+
+define i1 @icmp_eq_non_zero_range_attr(i8 range(i8 1, 0) %i) {
+; CHECK-LABEL: @icmp_eq_non_zero_range_attr(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_non_zero_range_attr(i8 range(i8 -1, 1) %i) {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_attr(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+declare range(i8 1, 0) i8 @returns_non_zero_range_helper()
+declare range(i8 -1, 1) i8 @returns_contain_zero_range_helper()
+
+define i1 @icmp_eq_non_zero_range_return() {
+; CHECK-LABEL: @icmp_eq_non_zero_range_return(
+; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_non_zero_range_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call i8 @returns_non_zero_range_helper()
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_non_zero_range_return() {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_return(
+; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_contain_zero_range_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call i8 @returns_contain_zero_range_helper()
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+declare i8 @returns_i8_helper()
+
+define i1 @icmp_eq_non_zero_range_call() {
+; CHECK-LABEL: @icmp_eq_non_zero_range_call(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 1, 0) i8 @returns_i8_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call range(i8 1, 0) i8 @returns_i8_helper()
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_non_zero_range_call() {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_call(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 -1, 1) i8 @returns_i8_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call range(i8 -1, 1) i8 @returns_i8_helper()
+  %cmp = icmp eq i8 %i, 0
+  ret i1 %cmp
+}
+
+define <2 x i1> @icmp_eq_non_zero_range_attr_vec(<2 x i8> range(i8 1, 0) %i) {
+; CHECK-LABEL: @icmp_eq_non_zero_range_attr_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_non_zero_range_attr_vec(<2 x i8> range(i8 -1, 1) %i) {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_attr_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+declare range(i8 1, 0) <2 x i8> @returns_non_zero_range_helper_vec()
+declare range(i8 -1, 1) <2 x i8> @returns_contain_zero_range_helper_vec()
+
+define <2 x i1> @icmp_eq_non_zero_range_return_vec() {
+; CHECK-LABEL: @icmp_eq_non_zero_range_return_vec(
+; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_non_zero_range_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call <2 x i8> @returns_non_zero_range_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_non_zero_range_return_vec() {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_return_vec(
+; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_contain_zero_range_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call <2 x i8> @returns_contain_zero_range_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+declare <2 x i8> @returns_i8_helper_vec()
+
+define <2 x i1> @icmp_eq_non_zero_range_call_vec() {
+; CHECK-LABEL: @icmp_eq_non_zero_range_call_vec(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 1, 0) <2 x i8> @returns_i8_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call range(i8 1, 0) <2 x i8> @returns_i8_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_non_zero_range_call_vec() {
+; CHECK-LABEL: @neg_icmp_eq_non_zero_range_call_vec(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 -1, 1) <2 x i8> @returns_i8_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call range(i8 -1, 1) <2 x i8> @returns_i8_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
+  ret <2 x i1> %cmp
+}
+
+define i1 @icmp_eq_constant_range_attr(i8 range(i8 0, 10) %i) {
+; CHECK-LABEL: @icmp_eq_constant_range_attr(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_constant_range_attr(i8 range(i8 0, 11) %i) {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_attr(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+declare range(i8 1, 0) i8 @returns_non_ten_range_helper()
+declare range(i8 -1, 1) i8 @returns_contain_ten_range_helper()
+
+define i1 @icmp_eq_constant_range_return() {
+; CHECK-LABEL: @icmp_eq_constant_range_return(
+; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_non_ten_range_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call i8 @returns_non_ten_range_helper()
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_constant_range_return() {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_return(
+; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_contain_ten_range_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call i8 @returns_contain_ten_range_helper()
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+define i1 @icmp_eq_constant_range_call() {
+; CHECK-LABEL: @icmp_eq_constant_range_call(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 10) i8 @returns_i8_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call range(i8 0, 10) i8 @returns_i8_helper()
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+define i1 @neg_icmp_eq_constant_range_call() {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_call(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 11) i8 @returns_i8_helper()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %i = call range(i8 0, 11) i8 @returns_i8_helper()
+  %cmp = icmp eq i8 %i, 10
+  ret i1 %cmp
+}
+
+define <2 x i1> @icmp_eq_constant_range_attr_vec(<2 x i8> range(i8 0, 10) %i) {
+; CHECK-LABEL: @icmp_eq_constant_range_attr_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_constant_range_attr_vec(<2 x i8> range(i8 0, 11) %i) {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_attr_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
+
+declare range(i8 0, 10) <2 x i8> @returns_non_ten_range_helper_vec()
+declare range(i8 0, 11) <2 x i8> @returns_contain_ten_range_helper_vec()
+
+define <2 x i1> @icmp_eq_constant_range_return_vec() {
+; CHECK-LABEL: @icmp_eq_constant_range_return_vec(
+; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_non_ten_range_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call <2 x i8> @returns_non_ten_range_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_constant_range_return_vec() {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_return_vec(
+; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_contain_ten_range_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call <2 x i8> @returns_contain_ten_range_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @icmp_eq_constant_range_call_vec() {
+; CHECK-LABEL: @icmp_eq_constant_range_call_vec(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 10) <2 x i8> @returns_i8_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call range(i8 0, 10) <2 x i8> @returns_i8_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @neg_icmp_eq_constant_range_call_vec() {
+; CHECK-LABEL: @neg_icmp_eq_constant_range_call_vec(
+; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 11) <2 x i8> @returns_i8_helper_vec()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %i = call range(i8 0, 11) <2 x i8> @returns_i8_helper_vec()
+  %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
+  ret <2 x i1> %cmp
+}
diff --git a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
index c2256f4b14af83..c05327458ea335 100644
--- a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
+++ b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
@@ -14,6 +14,140 @@ define i32 @shl_amount_is_known_bogus(i32 %a, i32 %b) {
   ret i32 %shl
 }
 
+define i32 @shl_amount_is_known_bogus_range_attr(i32 %a, i32 range(i32 32, 64) %b) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_attr(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+define i32 @neg_shl_amount_is_known_bogus_range_attr(i32 %a, i32 range(i32 0, 32) %b) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_attr(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+declare range(i32 32, 64) i32 @returns_out_of_range_helper()
+declare range(i32 0, 32) i32 @returns_in_range_helper()
+
+define i32 @shl_amount_is_known_bogus_range_return(i32 %a) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_return(
+; CHECK-NEXT:    [[B:%.*]] = call i32 @returns_out_of_range_helper()
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %b = call i32 @returns_out_of_range_helper()
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+define i32 @neg_shl_amount_is_known_bogus_range_return(i32 %a) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_return(
+; CHECK-NEXT:    [[B:%.*]] = call i32 @returns_in_range_helper()
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %b = call i32 @returns_in_range_helper()
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+declare i32 @returns_i32_helper()
+
+define i32 @shl_amount_is_known_bogus_range_call(i32 %a) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_call(
+; CHECK-NEXT:    [[B:%.*]] = call range(i32 32, 64) i32 @returns_i32_helper()
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %b = call range(i32 32, 64) i32 @returns_i32_helper()
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+define i32 @neg_shl_amount_is_known_bogus_range_call(i32 %a) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_call(
+; CHECK-NEXT:    [[B:%.*]] = call range(i32 0, 32) i32 @returns_i32_helper()
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[SHL]]
+;
+  %b = call range(i32 0, 32) i32 @returns_i32_helper()
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
+define <2 x i32> @shl_amount_is_known_bogus_range_attr_vec(<2 x i32> %a, <2 x i32> range(i32 32, 64) %b) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_attr_vec(
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
+define <2 x i32> @neg_shl_amount_is_known_bogus_range_attr_vec(<2 x i32> %a, <2 x i32> range(i32 0, 32) %b) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_attr_vec(
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
+declare range(i32 32, 64) <2 x i32> @returns_out_of_range_helper_vec()
+declare range(i32 0, 32) <2 x i32> @returns_in_range_helper_vec()
+
+define <2 x i32> @shl_amount_is_known_bogus_range_return_vec(<2 x i32> %a) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_return_vec(
+; CHECK-NEXT:    [[B:%.*]] = call <2 x i32> @returns_out_of_range_helper_vec()
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %b = call <2 x i32> @returns_out_of_range_helper_vec()
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
+define <2 x i32> @neg_shl_amount_is_known_bogus_range_return_vec(<2 x i32> %a) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_return_vec(
+; CHECK-NEXT:    [[B:%.*]] = call <2 x i32> @returns_in_range_helper_vec()
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %b = call <2 x i32> @returns_in_range_helper_vec()
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
+declare <2 x i32> @returns_i32_helper_vec()
+
+define <2 x i32> @shl_amount_is_known_bogus_range_call_vec(<2 x i32> %a) {
+; CHECK-LABEL: @shl_amount_is_known_bogus_range_call_vec(
+; CHECK-NEXT:    [[B:%.*]] = call range(i32 32, 64) <2 x i32> @returns_i32_helper_vec()
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %b = call range(i32 32, 64) <2 x i32> @returns_i32_helper_vec()
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
+define <2 x i32> @neg_shl_amount_is_known_bogus_range_call_vec(<2 x i32> %a) {
+; CHECK-LABEL: @neg_shl_amount_is_known_bogus_range_call_vec(
+; CHECK-NEXT:    [[B:%.*]] = call range(i32 0, 32) <2 x i32> @returns_i32_helper_vec()
+; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+;
+  %b = call range(i32 0, 32) <2 x i32> @returns_i32_helper_vec()
+  %shl = shl <2 x i32> %a, %b
+  ret <2 x i32> %shl
+}
+
 ; Check some weird types and the other shift ops.
 
 define i31 @lshr_amount_is_known_bogus(i31 %a, i31 %b) {

>From 45303ed3a03c261a8ea201af07ef831ca839e52f Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 12:13:45 +0100
Subject: [PATCH 3/7] handle range attribute in isKnownNonZero

---
 llvm/lib/Analysis/ValueTracking.cpp            | 12 ++++++++++++
 .../Transforms/InstSimplify/icmp-constant.ll   | 18 ++++++------------
 2 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8a4a2c4f92a0dc..66cc846def1859 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2893,6 +2893,18 @@ bool isKnownNonZero(const Value *V, const APInt &DemandedElts, unsigned Depth,
     }
   }
 
+  std::optional<ConstantRange> Range;
+  if (const CallBase *CB = dyn_cast<CallBase>(V))
+    Range = CB->getRange();
+  else if (const Argument *A = dyn_cast<Argument>(V))
+    Range = A->getRange();
+
+  if (Range) {
+    const APInt ZeroValue(Range->getBitWidth(), 0);
+    if (!Range->contains(ZeroValue))
+      return true;
+  }
+
   if (!isa<Constant>(V) && isKnownNonZeroFromAssume(V, Q))
     return true;
 
diff --git a/llvm/test/Transforms/InstSimplify/icmp-constant.ll b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
index 33bb9ea127cc69..fc68d78f851d62 100644
--- a/llvm/test/Transforms/InstSimplify/icmp-constant.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
@@ -1143,8 +1143,7 @@ define <2 x i1> @heterogeneous_constvector(<2 x i8> %x) {
 
 define i1 @icmp_eq_non_zero_range_attr(i8 range(i8 1, 0) %i) {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_attr(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp = icmp eq i8 %i, 0
   ret i1 %cmp
@@ -1165,8 +1164,7 @@ declare range(i8 -1, 1) i8 @returns_contain_zero_range_helper()
 define i1 @icmp_eq_non_zero_range_return() {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_return(
 ; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_non_zero_range_helper()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %i = call i8 @returns_non_zero_range_helper()
   %cmp = icmp eq i8 %i, 0
@@ -1189,8 +1187,7 @@ declare i8 @returns_i8_helper()
 define i1 @icmp_eq_non_zero_range_call() {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_call(
 ; CHECK-NEXT:    [[I:%.*]] = call range(i8 1, 0) i8 @returns_i8_helper()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %i = call range(i8 1, 0) i8 @returns_i8_helper()
   %cmp = icmp eq i8 %i, 0
@@ -1210,8 +1207,7 @@ define i1 @neg_icmp_eq_non_zero_range_call() {
 
 define <2 x i1> @icmp_eq_non_zero_range_attr_vec(<2 x i8> range(i8 1, 0) %i) {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_attr_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
   ret <2 x i1> %cmp
@@ -1232,8 +1228,7 @@ declare range(i8 -1, 1) <2 x i8> @returns_contain_zero_range_helper_vec()
 define <2 x i1> @icmp_eq_non_zero_range_return_vec() {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_return_vec(
 ; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_non_zero_range_helper_vec()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %i = call <2 x i8> @returns_non_zero_range_helper_vec()
   %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>
@@ -1256,8 +1251,7 @@ declare <2 x i8> @returns_i8_helper_vec()
 define <2 x i1> @icmp_eq_non_zero_range_call_vec() {
 ; CHECK-LABEL: @icmp_eq_non_zero_range_call_vec(
 ; CHECK-NEXT:    [[I:%.*]] = call range(i8 1, 0) <2 x i8> @returns_i8_helper_vec()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %i = call range(i8 1, 0) <2 x i8> @returns_i8_helper_vec()
   %cmp = icmp eq <2 x i8> %i, <i8 0, i8 0>

>From a9bc8866e571f03af04864c471ec7fbfca0fab98 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 12:29:22 +0100
Subject: [PATCH 4/7] handle range attribute in computeConstantRange

---
 llvm/lib/Analysis/ValueTracking.cpp            | 11 +++++++++--
 .../Transforms/InstSimplify/icmp-constant.ll   | 18 ++++++------------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 66cc846def1859..f852e3a01eec83 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9126,12 +9126,19 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
     // TODO: Return ConstantRange.
     setLimitForFPToI(cast<Instruction>(V), Lower, Upper);
     CR = ConstantRange::getNonEmpty(Lower, Upper);
-  }
+  } else if (const Argument *A = dyn_cast<Argument>(V))
+    if (std::optional<ConstantRange> Range = A->getRange())
+      CR = *Range;
 
-  if (auto *I = dyn_cast<Instruction>(V))
+  if (auto *I = dyn_cast<Instruction>(V)) {
     if (auto *Range = IIQ.getMetadata(I, LLVMContext::MD_range))
       CR = CR.intersectWith(getConstantRangeFromMetadata(*Range));
 
+    if (const CallBase *CB = dyn_cast<CallBase>(V))
+      if (std::optional<ConstantRange> Range = CB->getRange())
+        CR = CR.intersectWith(*Range);
+  }
+
   if (CtxI && AC) {
     // Try to restrict the range based on information from assumptions.
     for (auto &AssumeVH : AC->assumptionsFor(V)) {
diff --git a/llvm/test/Transforms/InstSimplify/icmp-constant.ll b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
index fc68d78f851d62..12051b6bffdeb1 100644
--- a/llvm/test/Transforms/InstSimplify/icmp-constant.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp-constant.ll
@@ -1271,8 +1271,7 @@ define <2 x i1> @neg_icmp_eq_non_zero_range_call_vec() {
 
 define i1 @icmp_eq_constant_range_attr(i8 range(i8 0, 10) %i) {
 ; CHECK-LABEL: @icmp_eq_constant_range_attr(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I:%.*]], 10
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp = icmp eq i8 %i, 10
   ret i1 %cmp
@@ -1304,8 +1303,7 @@ define i1 @icmp_eq_constant_range_return() {
 define i1 @neg_icmp_eq_constant_range_return() {
 ; CHECK-LABEL: @neg_icmp_eq_constant_range_return(
 ; CHECK-NEXT:    [[I:%.*]] = call i8 @returns_contain_ten_range_helper()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %i = call i8 @returns_contain_ten_range_helper()
   %cmp = icmp eq i8 %i, 10
@@ -1315,8 +1313,7 @@ define i1 @neg_icmp_eq_constant_range_return() {
 define i1 @icmp_eq_constant_range_call() {
 ; CHECK-LABEL: @icmp_eq_constant_range_call(
 ; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 10) i8 @returns_i8_helper()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[I]], 10
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %i = call range(i8 0, 10) i8 @returns_i8_helper()
   %cmp = icmp eq i8 %i, 10
@@ -1336,8 +1333,7 @@ define i1 @neg_icmp_eq_constant_range_call() {
 
 define <2 x i1> @icmp_eq_constant_range_attr_vec(<2 x i8> range(i8 0, 10) %i) {
 ; CHECK-LABEL: @icmp_eq_constant_range_attr_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I:%.*]], <i8 10, i8 10>
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
   ret <2 x i1> %cmp
@@ -1358,8 +1354,7 @@ declare range(i8 0, 11) <2 x i8> @returns_contain_ten_range_helper_vec()
 define <2 x i1> @icmp_eq_constant_range_return_vec() {
 ; CHECK-LABEL: @icmp_eq_constant_range_return_vec(
 ; CHECK-NEXT:    [[I:%.*]] = call <2 x i8> @returns_non_ten_range_helper_vec()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %i = call <2 x i8> @returns_non_ten_range_helper_vec()
   %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>
@@ -1380,8 +1375,7 @@ define <2 x i1> @neg_icmp_eq_constant_range_return_vec() {
 define <2 x i1> @icmp_eq_constant_range_call_vec() {
 ; CHECK-LABEL: @icmp_eq_constant_range_call_vec(
 ; CHECK-NEXT:    [[I:%.*]] = call range(i8 0, 10) <2 x i8> @returns_i8_helper_vec()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[I]], <i8 10, i8 10>
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %i = call range(i8 0, 10) <2 x i8> @returns_i8_helper_vec()
   %cmp = icmp eq <2 x i8> %i, <i8 10, i8 10>

>From 3027ab262630e2071182f7d7598576cde16b8ffd Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 12:43:46 +0100
Subject: [PATCH 5/7] handle range attribute in computeKnownBits

---
 llvm/lib/Analysis/ValueTracking.cpp            | 15 +++++++++++++--
 .../Transforms/InstSimplify/shift-knownbits.ll | 18 ++++++------------
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f852e3a01eec83..9816bcd08d39c5 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1467,14 +1467,20 @@ static void computeKnownBitsFromOperator(const Operator *I,
     break;
   }
   case Instruction::Call:
-  case Instruction::Invoke:
+  case Instruction::Invoke: {
     // If range metadata is attached to this call, set known bits from that,
     // and then intersect with known bits based on other properties of the
     // function.
     if (MDNode *MD =
             Q.IIQ.getMetadata(cast<Instruction>(I), LLVMContext::MD_range))
       computeKnownBitsFromRangeMetadata(*MD, Known);
-    if (const Value *RV = cast<CallBase>(I)->getReturnedArgOperand()) {
+
+    const CallBase *CB = cast<CallBase>(I);
+
+    if (std::optional<ConstantRange> Range = CB->getRange())
+      Known = Range->toKnownBits();
+
+    if (const Value *RV = CB->getReturnedArgOperand()) {
       if (RV->getType() == I->getType()) {
         computeKnownBits(RV, Known2, Depth + 1, Q);
         Known = Known.unionWith(Known2);
@@ -1646,6 +1652,7 @@ static void computeKnownBitsFromOperator(const Operator *I,
       }
     }
     break;
+  }
   case Instruction::ShuffleVector: {
     auto *Shuf = dyn_cast<ShuffleVectorInst>(I);
     // FIXME: Do we need to handle ConstantExpr involving shufflevectors?
@@ -1900,6 +1907,10 @@ void computeKnownBits(const Value *V, const APInt &DemandedElts,
   // assumptions.  Confirm that we've handled them all.
   assert(!isa<ConstantData>(V) && "Unhandled constant data!");
 
+  if (const Argument *A = dyn_cast<Argument>(V))
+    if (std::optional<ConstantRange> Range = A->getRange())
+      Known = Range->toKnownBits();
+
   // All recursive calls that increase depth must come after this.
   if (Depth == MaxAnalysisRecursionDepth)
     return;
diff --git a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
index c05327458ea335..901520f62acffd 100644
--- a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
+++ b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
@@ -16,8 +16,7 @@ define i32 @shl_amount_is_known_bogus(i32 %a, i32 %b) {
 
 define i32 @shl_amount_is_known_bogus_range_attr(i32 %a, i32 range(i32 32, 64) %b) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_attr(
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    ret i32 [[SHL]]
+; CHECK-NEXT:    ret i32 poison
 ;
   %shl = shl i32 %a, %b
   ret i32 %shl
@@ -38,8 +37,7 @@ declare range(i32 0, 32) i32 @returns_in_range_helper()
 define i32 @shl_amount_is_known_bogus_range_return(i32 %a) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_return(
 ; CHECK-NEXT:    [[B:%.*]] = call i32 @returns_out_of_range_helper()
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
-; CHECK-NEXT:    ret i32 [[SHL]]
+; CHECK-NEXT:    ret i32 poison
 ;
   %b = call i32 @returns_out_of_range_helper()
   %shl = shl i32 %a, %b
@@ -62,8 +60,7 @@ declare i32 @returns_i32_helper()
 define i32 @shl_amount_is_known_bogus_range_call(i32 %a) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_call(
 ; CHECK-NEXT:    [[B:%.*]] = call range(i32 32, 64) i32 @returns_i32_helper()
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
-; CHECK-NEXT:    ret i32 [[SHL]]
+; CHECK-NEXT:    ret i32 poison
 ;
   %b = call range(i32 32, 64) i32 @returns_i32_helper()
   %shl = shl i32 %a, %b
@@ -83,8 +80,7 @@ define i32 @neg_shl_amount_is_known_bogus_range_call(i32 %a) {
 
 define <2 x i32> @shl_amount_is_known_bogus_range_attr_vec(<2 x i32> %a, <2 x i32> range(i32 32, 64) %b) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_attr_vec(
-; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+; CHECK-NEXT:    ret <2 x i32> poison
 ;
   %shl = shl <2 x i32> %a, %b
   ret <2 x i32> %shl
@@ -105,8 +101,7 @@ declare range(i32 0, 32) <2 x i32> @returns_in_range_helper_vec()
 define <2 x i32> @shl_amount_is_known_bogus_range_return_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_return_vec(
 ; CHECK-NEXT:    [[B:%.*]] = call <2 x i32> @returns_out_of_range_helper_vec()
-; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
-; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+; CHECK-NEXT:    ret <2 x i32> poison
 ;
   %b = call <2 x i32> @returns_out_of_range_helper_vec()
   %shl = shl <2 x i32> %a, %b
@@ -129,8 +124,7 @@ declare <2 x i32> @returns_i32_helper_vec()
 define <2 x i32> @shl_amount_is_known_bogus_range_call_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @shl_amount_is_known_bogus_range_call_vec(
 ; CHECK-NEXT:    [[B:%.*]] = call range(i32 32, 64) <2 x i32> @returns_i32_helper_vec()
-; CHECK-NEXT:    [[SHL:%.*]] = shl <2 x i32> [[A:%.*]], [[B]]
-; CHECK-NEXT:    ret <2 x i32> [[SHL]]
+; CHECK-NEXT:    ret <2 x i32> poison
 ;
   %b = call range(i32 32, 64) <2 x i32> @returns_i32_helper_vec()
   %shl = shl <2 x i32> %a, %b

>From 2caa99e271eb855da0d5e57c80b45020b9232ae4 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 23:08:04 +0100
Subject: [PATCH 6/7] fixup! pre-commit tests for range attributes in
 valuetracking

---
 llvm/test/Transforms/InstSimplify/shift-knownbits.ll | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
index 901520f62acffd..7fab61a5561586 100644
--- a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
+++ b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
@@ -142,6 +142,16 @@ define <2 x i32> @neg_shl_amount_is_known_bogus_range_call_vec(<2 x i32> %a) {
   ret <2 x i32> %shl
 }
 
+define i32 @shl_amount_is_not_known_bogus_range_call_and_range_metadata(i32 %a) {
+; CHECK-LABEL: @shl_amount_is_not_known_bogus_range_call_and_range_metadata(
+; CHECK-NEXT:    [[B:%.*]] = call range(i32 0, 32) i32 @returns_i32_helper(), !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    ret i32 poison
+;
+  %b = call range(i32 0, 32) i32 @returns_i32_helper(), !range !{ i32 32, i32 64 }
+  %shl = shl i32 %a, %b
+  ret i32 %shl
+}
+
 ; Check some weird types and the other shift ops.
 
 define i31 @lshr_amount_is_known_bogus(i31 %a, i31 %b) {

>From 9c50cc961186a813090e804116015ba006c2314f Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 17 Mar 2024 23:25:11 +0100
Subject: [PATCH 7/7] fixup! handle range attribute in computeKnownBits

---
 llvm/lib/Analysis/ValueTracking.cpp                  | 2 +-
 llvm/test/Transforms/InstSimplify/shift-knownbits.ll | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 9816bcd08d39c5..b726725e2b6cae 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1478,7 +1478,7 @@ static void computeKnownBitsFromOperator(const Operator *I,
     const CallBase *CB = cast<CallBase>(I);
 
     if (std::optional<ConstantRange> Range = CB->getRange())
-      Known = Range->toKnownBits();
+      Known = Known.unionWith(Range->toKnownBits());
 
     if (const Value *RV = CB->getReturnedArgOperand()) {
       if (RV->getType() == I->getType()) {
diff --git a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
index 7fab61a5561586..6bf03779379ec7 100644
--- a/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
+++ b/llvm/test/Transforms/InstSimplify/shift-knownbits.ll
@@ -145,7 +145,8 @@ define <2 x i32> @neg_shl_amount_is_known_bogus_range_call_vec(<2 x i32> %a) {
 define i32 @shl_amount_is_not_known_bogus_range_call_and_range_metadata(i32 %a) {
 ; CHECK-LABEL: @shl_amount_is_not_known_bogus_range_call_and_range_metadata(
 ; CHECK-NEXT:    [[B:%.*]] = call range(i32 0, 32) i32 @returns_i32_helper(), !range [[RNG0:![0-9]+]]
-; CHECK-NEXT:    ret i32 poison
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[SHL]]
 ;
   %b = call range(i32 0, 32) i32 @returns_i32_helper(), !range !{ i32 32, i32 64 }
   %shl = shl i32 %a, %b



More information about the llvm-commits mailing list