[llvm] [SeparateConstOffsetFromGEP] Stop distributing sext/zext over lossy trunc (PR #221381)

Fujun Han via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 25 20:06:31 PDT 2026


================
@@ -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 and
+    // disjoint or 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.
----------------
Peter9606 wrote:

Thanks! The constant exception is for when trunc's operand is already a Constant: there's no variable remainder to split, so tracing through is just folding ext(trunc C).

That shows up in @trunk_explicit (NVPTX/split-gep.ll): the index is trunc i64 1 to i32, and canonicalizeArrayIndicesToIndexSize then sign-extends it to the pointer index type, so find() sees sext(trunc C). The check stays that narrow so we keep this fold without reopening the known-bits approach.

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


More information about the llvm-commits mailing list