[llvm] [SeparateConstOffsetFromGEP] Stop distributing sext/zext over lossy trunc (PR #221381)
Fujun Han via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 9 04:23:11 PDT 2026
https://github.com/Peter9606 updated https://github.com/llvm/llvm-project/pull/221381
>From b9f7a6b9f6ba3325a5c93cd2750a5e8450cb146f Mon Sep 17 00:00:00 2001
From: Fujun Han <fujun.han at iluvatar.com>
Date: Sat, 5 Sep 2026 10:06:57 +0800
Subject: [PATCH 1/2] [SeparateConstOffsetFromGEP] Precommit tests for
sext/zext of trunc (NFC)
The nuw/nsw flags on an add hold at the width of the add, which says
nothing about wrapping at the width of a truncation applied to its
result. These tests capture the current behavior of distributing a
sext/zext above a truncation into the operands of the add/sub/or; the
lossy cases are miscompiled today.
@sext_of_lossy_operand is the review counter-example: the sum survives
the truncation, but the remaining operand does not, so hoisting the
constant is still wrong.
Assisted-by: Cursor (Claude Fable 5)
Signed-off-by: Fujun Han <fujun.han at iluvatar.com>
Co-authored-by: Cursor <cursoragent at cursor.com>
---
.../ext-of-trunc-add-wrap.ll | 283 ++++++++++++++++++
1 file changed, 283 insertions(+)
create mode 100644 llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
new file mode 100644
index 0000000000000..27f4c6cd4112e
--- /dev/null
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
@@ -0,0 +1,283 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='separate-const-offset-from-gep<lower-gep>' < %s | FileCheck %s
+
+; The nuw/nsw flags on an add hold at the width of the add, which says nothing
+; about wrapping at the width of a truncation applied to its result. A
+; sext/zext above such a truncation must therefore not be distributed into the
+; operands of the add.
+
+; zext i64 (trunc i8 (add nuw i32 A, B)) wraps modulo 256, so the constant 1
+; must stay inside the truncation. For A = 251 and B = 5 the index is 0, while
+; distributing the casts would give 251 + 5 = 256.
+define ptr @zext_of_lossy_trunc(ptr %p, i8 %a, i64 %iv) {
+; CHECK-LABEL: define ptr @zext_of_lossy_trunc(
+; CHECK-SAME: ptr [[P:%.*]], i8 [[A:%.*]], i64 [[IV:%.*]]) {
+; CHECK-NEXT: [[AZ:%.*]] = zext i8 [[A]] to i32
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AZ]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i8 [[TMP1]] to i64
+; CHECK-NEXT: [[T:%.*]] = trunc i64 [[IV]] to i8
+; CHECK-NEXT: [[TZ:%.*]] = zext i8 [[T]] to i32
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[TZ]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = zext nneg i8 [[SUM8]] to i64
+; CHECK-NEXT: [[SUM2:%.*]] = add i64 [[TMP2]], [[IDX]]
+; CHECK-NEXT: [[TMP7:%.*]] = shl i64 [[SUM2]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP7]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 4
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %iv.next = add nuw nsw i64 %iv, 1
+ %t = trunc i64 %iv.next to i8
+ %az = zext i8 %a to i32
+ %tz = zext i8 %t to i32
+ %sum = add nuw nsw i32 %az, %tz
+ %sum8 = trunc i32 %sum to i8
+ %idx = zext nneg i8 %sum8 to i64
+ %q = getelementptr inbounds i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; Same shape, but masking keeps both the sum and the remaining operand below
+; 256, so the truncation loses nothing and the constant can still be hoisted.
+define ptr @zext_of_lossless_trunc(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @zext_of_lossless_trunc(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 15
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = zext nneg i8 [[SUM8]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %a = and i32 %x, 15
+ %sum = add nuw nsw i32 %a, 3
+ %sum8 = trunc i32 %sum to i8
+ %idx = zext nneg i8 %sum8 to i64
+ %q = getelementptr inbounds i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; sext additionally needs the sign bit of the narrow value to be clear. Here
+; the sum reaches 130, so sext i8 of the truncation is negative and the
+; constant must stay inside the truncation.
+define ptr @sext_of_lossy_trunc(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @sext_of_lossy_trunc(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 127
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %a = and i32 %x, 127
+ %sum = add nuw nsw i32 %a, 3
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; With the sign bits of the sum and of the remaining operand both clear, sext
+; and zext agree on every involved value, so the constant can be hoisted out
+; of a sext'ed truncation as well.
+define ptr @sext_of_lossless_trunc(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @sext_of_lossless_trunc(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 63
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %a = and i32 %x, 63
+ %sum = add nuw nsw i32 %a, 3
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; The trunc of the sum being lossless is not enough: here %sum is 124 or 127,
+; so trunc+sext reproduces it, but the remaining operand %a is 224 or 227 and
+; wraps at i8. Distributing the sext would compute sext(trunc(%a)) - 100 =
+; -132 instead of 124. Counter-example from review of llvm#221381.
+define ptr @sext_of_lossy_operand(ptr %p, i1 %c) {
+; CHECK-LABEL: define ptr @sext_of_lossy_operand(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = select i1 [[C]], i32 224, i32 227
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 -400
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %a = select i1 %c, i32 224, i32 227
+ %sum = add nsw i32 %a, -100
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; Same as @sext_of_lossless_trunc but with the constant as operand 0 of the
+; add.
+define ptr @sext_of_lossless_trunc_const_lhs(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @sext_of_lossless_trunc_const_lhs(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 63
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[UGLYGEP2]]
+;
+ %a = and i32 %x, 63
+ %sum = add nuw nsw i32 3, %a
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; A truncated constant has no variable remainder; the pending extension
+; applies to the constant itself, so extracting it is a plain constant fold.
+; 130 wraps to -126 at i8 and the offset must use the wrapped value.
+define ptr @sext_of_trunc_of_constant(ptr %p, i64 %i) {
+; CHECK-LABEL: define ptr @sext_of_trunc_of_constant(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[I]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP1]]
+; CHECK-NEXT: [[UGLYGEP1:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 -50400
+; CHECK-NEXT: ret ptr [[UGLYGEP1]]
+;
+ %t = trunc i64 130 to i8
+ %idx = sext i8 %t to i64
+ %q = getelementptr [100 x i32], ptr %p, i64 %idx, i64 %i
+ ret ptr %q
+}
+
+; Same with zext: trunc i8 130 is 0x82, which zext reads back as 130.
+define ptr @zext_of_trunc_of_constant(ptr %p, i64 %i) {
+; CHECK-LABEL: define ptr @zext_of_trunc_of_constant(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[I]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP1]]
+; CHECK-NEXT: [[UGLYGEP1:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 52000
+; CHECK-NEXT: ret ptr [[UGLYGEP1]]
+;
+ %t = trunc i64 130 to i8
+ %idx = zext i8 %t to i64
+ %q = getelementptr [100 x i32], ptr %p, i64 %idx, i64 %i
+ ret ptr %q
+}
+
+; canTraceInto already refuses to trace a sub under zext (the constant would
+; need to be zero-extended before negation), so this stays untransformed.
+define ptr @zext_of_trunc_sub(ptr %p, i32 %a) {
+; CHECK-LABEL: define ptr @zext_of_trunc_sub(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[A:%.*]]) {
+; CHECK-NEXT: [[SUM:%.*]] = sub nuw i32 [[A]], 250
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
+; CHECK-NEXT: [[IDX:%.*]] = zext i8 [[SUM8]] to i64
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P]], i64 [[IDX]]
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %sum = sub nuw i32 %a, 250
+ %sum8 = trunc i32 %sum to i8
+ %idx = zext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; A sub under a sext'ed truncation wraps the same way as an add. The
+; extracted -250 becomes 6 after trunc+sext, so for %x = 127 the original
+; index is sext(trunc(127 - 250)) = -123 while the rebuilt form computes
+; sext(trunc(127)) + 6 = 133.
+define ptr @sext_of_trunc_sub(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @sext_of_trunc_sub(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 255
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 24
+; CHECK-NEXT: ret ptr [[UGLYGEP2]]
+;
+ %a = and i32 %x, 255
+ %sum = sub nsw i32 %a, 250
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; An or with disjoint constant bits acts as an add and must follow the same
+; rule under a truncation with a pending extension.
+define ptr @zext_of_trunc_or_disjoint(ptr %p, i32 %x) {
+; CHECK-LABEL: define ptr @zext_of_trunc_or_disjoint(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 384
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = zext i8 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[UGLYGEP2]]
+;
+ %a = and i32 %x, 384
+ %sum = or disjoint i32 %a, 3
+ %sum8 = trunc i32 %sum to i8
+ %idx = zext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; The constant sits one add deeper: %sum = %a + (%b + 3).
+define ptr @sext_of_trunc_nested_const(ptr %p, i32 %x, i32 %y) {
+; CHECK-LABEL: define ptr @sext_of_trunc_nested_const(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 15
+; CHECK-NEXT: [[B:%.*]] = and i32 [[Y]], 15
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[B]] to i8
+; CHECK-NEXT: [[TMP4:%.*]] = sext i8 [[TMP3]] to i64
+; CHECK-NEXT: [[SUM2:%.*]] = add i64 [[TMP2]], [[TMP4]]
+; CHECK-NEXT: [[TMP5:%.*]] = shl i64 [[SUM2]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP5]]
+; CHECK-NEXT: [[UGLYGEP3:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: ret ptr [[UGLYGEP3]]
+;
+ %a = and i32 %x, 15
+ %b = and i32 %y, 15
+ %inner = add nuw nsw i32 %b, 3
+ %sum = add nuw nsw i32 %a, %inner
+ %sum8 = trunc i32 %sum to i8
+ %idx = sext i8 %sum8 to i64
+ %q = getelementptr i32, ptr %p, i64 %idx
+ ret ptr %q
+}
+
+; A truncation with no extension above it still distributes over the add,
+; because truncation is exact in modular arithmetic.
+define ptr @bare_trunc(ptr %p, i128 %i) {
+; CHECK-LABEL: define ptr @bare_trunc(
+; CHECK-SAME: ptr [[P:%.*]], i128 [[I:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i128 [[I]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = shl i64 [[TMP1]], 2
+; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP2]]
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 4
+; CHECK-NEXT: ret ptr [[Q]]
+;
+ %idx = add i128 %i, 1
+ %idx.conv = trunc i128 %idx to i64
+ %q = getelementptr i32, ptr %p, i64 %idx.conv
+ ret ptr %q
+}
>From e129472eaf82e2d618e5eacb05685e6b7f03ba91 Mon Sep 17 00:00:00 2001
From: Fujun Han <fujun.han at iluvatar.com>
Date: Wed, 9 Sep 2026 19:22:53 +0800
Subject: [PATCH 2/2] [SeparateConstOffsetFromGEP] Stop distributing sext/zext
over lossy trunc
find() traces through a trunc while keeping a pending sext/zext, and
canTraceInto() then justifies distributing the extension into the
operands of an add/sub by checking its nuw/nsw flags. Those flags
hold at the width of the add but say nothing about wrapping at the
truncation width, so
zext i64 (trunc i8 (add nuw i32 (zext i8 251), (zext i8 5)))
which is 0 was rebuilt as 251 + 5 = 256.
With a pending sext/zext, stop tracing through a trunc unless the
truncated value is entirely constant: in that case there is no
variable remainder and the pending casts apply to the constant
itself, so extracting it is a plain constant fold (this keeps e.g.
@trunk_explicit in NVPTX/split-gep.ll folding).
Known-bits reasoning could keep some of the lossless cases optimizing,
but proving the round trip for the truncated value alone is not
enough: in the review counter-example, add nsw (select i1 %c, i32 224,
i32 227), -100 is 124 or 127 and truncates to i8 losslessly, yet the
remaining operand 224/227 wraps at i8, so hoisting -100 would rebuild
the index as sext(trunc(%a)) - 100 = -132 instead of 124. Leave that
as a possible follow-up and only fix the miscompile here.
Assisted-by: Cursor (Claude Fable 5)
Signed-off-by: Fujun Han <fujun.han at iluvatar.com>
Co-authored-by: Cursor <cursoragent at cursor.com>
---
.../Scalar/SeparateConstOffsetFromGEP.cpp | 19 ++++-
.../ext-of-trunc-add-wrap.ll | 73 ++++++++-----------
2 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
index 4870b8c888279..f4f6e4176ccf2 100644
--- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
+++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
@@ -741,9 +741,22 @@ APInt ConstantOffsetExtractor::find(Value *V, GetElementPtrInst *GEP,
else if (BO->getOpcode() == Instruction::Xor)
ConstantOffset = extractDisjointBitsFromXor(BO);
} else if (isa<TruncInst>(V)) {
- ConstantOffset =
- find(U->getOperand(0), GEP, Idx, SignExtended, ZeroExtended)
- .trunc(BitWidth);
+ // With no pending extension, truncation distributes over add/sub in
+ // modular arithmetic, so any constant found in the wider operand stays
+ // valid after truncating it.
+ //
+ // With a pending sext/zext, distributing the extension into the operands
+ // of the truncated expression is unsound: the nuw/nsw flags checked by
+ // canTraceInto hold at the width of the add and say nothing about
+ // wrapping at the truncation width, e.g.
+ // zext i64 (trunc i8 (add nuw i32 (zext i8 251), (zext i8 5)))
+ // is 0 but would be rebuilt as 251 + 5 = 256. Only a fully constant
+ // truncated value remains exact, because then there is no remainder and
+ // the pending casts apply to the constant itself.
+ Value *TruncOp = U->getOperand(0);
+ if ((!SignExtended && !ZeroExtended) || isa<ConstantInt>(TruncOp))
+ ConstantOffset =
+ find(TruncOp, GEP, Idx, SignExtended, ZeroExtended).trunc(BitWidth);
} else if (isa<SExtInst>(V)) {
ConstantOffset =
find(U->getOperand(0), GEP, Idx, /* SignExtended */ true, ZeroExtended)
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
index 27f4c6cd4112e..17a203b460cef 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/ext-of-trunc-add-wrap.ll
@@ -12,17 +12,14 @@
define ptr @zext_of_lossy_trunc(ptr %p, i8 %a, i64 %iv) {
; CHECK-LABEL: define ptr @zext_of_lossy_trunc(
; CHECK-SAME: ptr [[P:%.*]], i8 [[A:%.*]], i64 [[IV:%.*]]) {
+; CHECK-NEXT: [[IV_NEXT:%.*]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[T:%.*]] = trunc i64 [[IV_NEXT]] to i8
; CHECK-NEXT: [[AZ:%.*]] = zext i8 [[A]] to i32
-; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AZ]] to i8
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i8 [[TMP1]] to i64
-; CHECK-NEXT: [[T:%.*]] = trunc i64 [[IV]] to i8
; CHECK-NEXT: [[TZ:%.*]] = zext i8 [[T]] to i32
-; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[TZ]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[AZ]], [[TZ]]
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[IDX:%.*]] = zext nneg i8 [[SUM8]] to i64
-; CHECK-NEXT: [[SUM2:%.*]] = add i64 [[TMP2]], [[IDX]]
-; CHECK-NEXT: [[TMP7:%.*]] = shl i64 [[SUM2]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP7]]
-; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 4
+; CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX]]
; CHECK-NEXT: ret ptr [[Q]]
;
%iv.next = add nuw nsw i64 %iv, 1
@@ -42,11 +39,10 @@ define ptr @zext_of_lossless_trunc(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @zext_of_lossless_trunc(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 15
-; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[A]], 3
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[IDX:%.*]] = zext nneg i8 [[SUM8]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX]]
; CHECK-NEXT: ret ptr [[Q]]
;
%a = and i32 %x, 15
@@ -64,11 +60,10 @@ define ptr @sext_of_lossy_trunc(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @sext_of_lossy_trunc(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 127
-; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[A]], 3
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P]], i64 [[IDX]]
; CHECK-NEXT: ret ptr [[Q]]
;
%a = and i32 %x, 127
@@ -86,11 +81,10 @@ define ptr @sext_of_lossless_trunc(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @sext_of_lossless_trunc(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 63
-; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[A]], 3
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P]], i64 [[IDX]]
; CHECK-NEXT: ret ptr [[Q]]
;
%a = and i32 %x, 63
@@ -109,11 +103,10 @@ define ptr @sext_of_lossy_operand(ptr %p, i1 %c) {
; CHECK-LABEL: define ptr @sext_of_lossy_operand(
; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = select i1 [[C]], i32 224, i32 227
-; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nsw i32 [[A]], -100
+; CHECK-NEXT: [[SUM8:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[IDX:%.*]] = sext i8 [[SUM8]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[IDX]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[Q:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 -400
+; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P]], i64 [[IDX]]
; CHECK-NEXT: ret ptr [[Q]]
;
%a = select i1 %c, i32 224, i32 227
@@ -130,11 +123,10 @@ define ptr @sext_of_lossless_trunc_const_lhs(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @sext_of_lossless_trunc_const_lhs(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 63
-; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 3, [[A]]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP2]]
; CHECK-NEXT: ret ptr [[UGLYGEP2]]
;
%a = and i32 %x, 63
@@ -203,11 +195,10 @@ define ptr @sext_of_trunc_sub(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @sext_of_trunc_sub(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 255
-; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = sub nsw i32 [[A]], 250
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 24
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP2]]
; CHECK-NEXT: ret ptr [[UGLYGEP2]]
;
%a = and i32 %x, 255
@@ -224,11 +215,10 @@ define ptr @zext_of_trunc_or_disjoint(ptr %p, i32 %x) {
; CHECK-LABEL: define ptr @zext_of_trunc_or_disjoint(
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 384
-; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
+; CHECK-NEXT: [[SUM:%.*]] = or disjoint i32 [[A]], 3
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[TMP2:%.*]] = zext i8 [[TMP1]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[TMP2]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]]
-; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[UGLYGEP2:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP2]]
; CHECK-NEXT: ret ptr [[UGLYGEP2]]
;
%a = and i32 %x, 384
@@ -245,14 +235,11 @@ define ptr @sext_of_trunc_nested_const(ptr %p, i32 %x, i32 %y) {
; CHECK-SAME: ptr [[P:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = and i32 [[X]], 15
; CHECK-NEXT: [[B:%.*]] = and i32 [[Y]], 15
-; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[A]] to i8
-; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP1]] to i64
-; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[B]] to i8
+; CHECK-NEXT: [[INNER:%.*]] = add nuw nsw i32 [[B]], 3
+; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[A]], [[INNER]]
+; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[SUM]] to i8
; CHECK-NEXT: [[TMP4:%.*]] = sext i8 [[TMP3]] to i64
-; CHECK-NEXT: [[SUM2:%.*]] = add i64 [[TMP2]], [[TMP4]]
-; CHECK-NEXT: [[TMP5:%.*]] = shl i64 [[SUM2]], 2
-; CHECK-NEXT: [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP5]]
-; CHECK-NEXT: [[UGLYGEP3:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT: [[UGLYGEP3:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP4]]
; CHECK-NEXT: ret ptr [[UGLYGEP3]]
;
%a = and i32 %x, 15
More information about the llvm-commits
mailing list