Skip to content

Conversation

andjo403
Copy link
Contributor

@andjo403 andjo403 commented Sep 9, 2025

@andjo403 andjo403 requested review from nikic and dtcxzyw September 9, 2025 05:20
@llvmbot llvmbot added llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Sep 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 9, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: Andreas Jonson (andjo403)

Changes

proof: https://alive2.llvm.org/ce/z/8emkHY


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/LazyValueInfo.cpp (+18)
  • (modified) llvm/test/Transforms/CorrelatedValuePropagation/range.ll (+144)
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index c7b0ca97a8e43..cbb9ed3b12934 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1493,6 +1493,24 @@ LazyValueInfoImpl::getEdgeValueLocal(Value *Val, BasicBlock *BBFrom,
             //   br %Condition, label %then, label %else
             APInt ConditionVal(1, isTrueDest ? 1 : 0);
             Result = constantFoldUser(Usr, Condition, ConditionVal, DL);
+          } else if (isa<CastInst>(Usr) || isa<FreezeInst>(Usr)) {
+            ValueLatticeElement OpLatticeVal =
+                *getValueFromCondition(Usr->getOperand(0), Condition,
+                                       isTrueDest, /*UseBlockValue*/ false);
+
+            if (isa<FreezeInst>(Usr) || !OpLatticeVal.isConstantRange())
+              return OpLatticeVal;
+
+            const unsigned ResultBitWidth =
+                Usr->getType()->getScalarSizeInBits();
+            if (auto *Trunc = dyn_cast<TruncInst>(Usr))
+              return ValueLatticeElement::getRange(
+                  OpLatticeVal.getConstantRange().truncate(
+                      ResultBitWidth, Trunc->getNoWrapKind()));
+
+            return ValueLatticeElement::getRange(
+                OpLatticeVal.getConstantRange().castOp(
+                    cast<CastInst>(Usr)->getOpcode(), ResultBitWidth));
           } else {
             // If one of Val's operand has an inferred value, we may be able to
             // infer the value of Val.
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
index 8d787fd5586ef..fd6722cfc1671 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
@@ -1235,6 +1235,150 @@ define i1 @neg_icmp_eq_range_call() {
   ret i1 %cmp
 }
 
+define i16 @return_range_for_edge_value_zext(i8 %a) {
+; CHECK-LABEL: define range(i16 0, 98) i16 @return_range_for_edge_value_zext(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = zext i8 [[A]] to i16
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 98
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i16 [[B]]
+;
+entry:
+  %b = zext i8 %a to i16
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ult i8 %a, 98
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i16 %b
+}
+
+define i16 @return_range_for_edge_value_sext(i8 %a) {
+; CHECK-LABEL: define range(i16 0, 98) i16 @return_range_for_edge_value_sext(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = sext i8 [[A]] to i16
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 98
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i16 [[B]]
+;
+entry:
+  %b = sext i8 %a to i16
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ult i8 %a, 98
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i16 %b
+}
+
+define i8 @return_range_for_edge_value_trunc(i16 %a) {
+; CHECK-LABEL: define range(i8 0, 98) i8 @return_range_for_edge_value_trunc(
+; CHECK-SAME: i16 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = trunc i16 [[A]] to i8
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i16 [[A]], 98
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i8 [[B]]
+;
+entry:
+  %b = trunc i16 %a to i8
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ult i16 %a, 98
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i8 %b
+}
+
+define i8 @neg_return_range_for_edge_value_trunc(i16 %a) {
+; CHECK-LABEL: define i8 @neg_return_range_for_edge_value_trunc(
+; CHECK-SAME: i16 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = trunc i16 [[A]] to i8
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i16 [[A]], 200
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i8 [[B]]
+;
+entry:
+  %b = trunc i16 %a to i8
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ugt i16 %a, 200
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i8 %b
+}
+
+define i8 @return_range_for_edge_value_trunc_nuw(i16 %a) {
+; CHECK-LABEL: define range(i8 -55, 0) i8 @return_range_for_edge_value_trunc_nuw(
+; CHECK-SAME: i16 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = trunc nuw i16 [[A]] to i8
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i16 [[A]], 200
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i8 [[B]]
+;
+entry:
+  %b = trunc nuw i16 %a to i8
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ugt i16 %a, 200
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i8 %b
+}
+
+define i8 @return_range_for_edge_value_freeze(i8 %a) {
+; CHECK-LABEL: define range(i8 0, 98) i8 @return_range_for_edge_value_freeze(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[B:%.*]] = freeze i8 [[A]]
+; CHECK-NEXT:    br label %[[DISPATCH:.*]]
+; CHECK:       [[DISPATCH]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 98
+; CHECK-NEXT:    br i1 [[CMP]], label %[[TARGET:.*]], label %[[DISPATCH]]
+; CHECK:       [[TARGET]]:
+; CHECK-NEXT:    ret i8 [[B]]
+;
+entry:
+  %b = freeze i8 %a
+  br label %dispatch
+
+dispatch:
+  %cmp = icmp ult i8 %a, 98
+  br i1 %cmp, label %target, label %dispatch
+
+target:
+  ret i8 %b
+}
+
 declare i16 @llvm.ctlz.i16(i16, i1)
 declare i16 @llvm.cttz.i16(i16, i1)
 declare i16 @llvm.ctpop.i16(i16)

@andjo403
Copy link
Contributor Author

andjo403 commented Sep 9, 2025

I needs to look in to why there is failures in dtcxzyw/llvm-opt-benchmark#2768

@andjo403 andjo403 force-pushed the NonConstEdgeValueCastFreeze branch from 98aa2b2 to 3c4ac99 Compare September 9, 2025 06:01
@andjo403 andjo403 force-pushed the NonConstEdgeValueCastFreeze branch from 3c4ac99 to 7cce3e7 Compare September 9, 2025 06:20
@andjo403
Copy link
Contributor Author

andjo403 commented Sep 9, 2025

limited the cast to trunc, zext and sext as that is what I have tested

br label %dispatch

dispatch:
%cmp = icmp ult i8 %a, 98
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InstCombine canonicalizes all uses of %a to %b. Can you put freeze-related changes into a separate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch there was no changes for freeze when I did a benchmark run I only added it as it was listed in isOperationFoldable. but is dropped now

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes w/o freeze look good.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andjo403 andjo403 changed the title [LVI] Support no constant range of cast and freeze value in getEdgeValueLocal. [LVI] Support no constant range of cast value in getEdgeValueLocal. Sep 9, 2025
@andjo403 andjo403 merged commit 2701a22 into llvm:main Sep 10, 2025
9 checks passed
@andjo403 andjo403 deleted the NonConstEdgeValueCastFreeze branch September 10, 2025 16:19
dtcxzyw added a commit that referenced this pull request Oct 1, 2025
Closes #161367.

In #157614, we ignored cases
where OpLatticeVal might be a constant or notconstant. Directly
returning the result causes a type mismatch. I apologize for the
oversight in the previous code review.

This patch applies the cast op to constants. For notconstant value
lattices, I'd leave it as a todo (it is similar to the constant case,
except for trunc without nsw/nuw).
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 1, 2025
… (#161410)

Closes llvm/llvm-project#161367.

In llvm/llvm-project#157614, we ignored cases
where OpLatticeVal might be a constant or notconstant. Directly
returning the result causes a type mismatch. I apologize for the
oversight in the previous code review.

This patch applies the cast op to constants. For notconstant value
lattices, I'd leave it as a todo (it is similar to the constant case,
except for trunc without nsw/nuw).
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
Closes llvm#161367.

In llvm#157614, we ignored cases
where OpLatticeVal might be a constant or notconstant. Directly
returning the result causes a type mismatch. I apologize for the
oversight in the previous code review.

This patch applies the cast op to constants. For notconstant value
lattices, I'd leave it as a todo (it is similar to the constant case,
except for trunc without nsw/nuw).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants