[llvm] [InstCombine] Preserve no-wrap flags in BitwiseOp(trunc(A), trunc(B)) fold. (PR #219567)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 13:00:05 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Andreas Jonson (andjo403)

<details>
<summary>Changes</summary>

proof: https://alive2.llvm.org/ce/z/BP5VSQ

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


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+12-1) 
- (modified) llvm/test/Transforms/InstCombine/and-xor-or.ll (+195) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 9e9ebfe5c5e90..054161d4481ff 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1911,7 +1911,18 @@ Instruction *InstCombinerImpl::foldCastedBitwiseLogic(BinaryOperator &I) {
   if (shouldOptimizeCast(Cast0) && shouldOptimizeCast(Cast1)) {
     Value *NewOp = Builder.CreateBinOp(LogicOpc, Cast0Src, Cast1Src,
                                        I.getName());
-    return CastInst::Create(CastOpcode, NewOp, DestTy);
+    auto *NewCast = CastInst::Create(CastOpcode, NewOp, DestTy);
+    if (auto *NewTrunc = dyn_cast<TruncInst>(NewCast)) {
+      auto *Trunc0 = cast<TruncInst>(Cast0);
+      auto *Trunc1 = cast<TruncInst>(Cast1);
+      NewTrunc->setHasNoUnsignedWrap(
+          LogicOpc == Instruction::And
+              ? Trunc0->hasNoUnsignedWrap() || Trunc1->hasNoUnsignedWrap()
+              : Trunc0->hasNoUnsignedWrap() && Trunc1->hasNoUnsignedWrap());
+      NewTrunc->setHasNoSignedWrap(Trunc0->hasNoSignedWrap() &&
+                                   Trunc1->hasNoSignedWrap());
+    }
+    return NewCast;
   }
 
   return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/and-xor-or.ll b/llvm/test/Transforms/InstCombine/and-xor-or.ll
index 3bb1056c72025..1750aea8e1eaa 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-or.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-or.ll
@@ -4247,6 +4247,201 @@ define i32 @trunc_trunc_xor_uses(i65 %x, i65 %y) {
   ret i32 %r
 }
 
+define i8 @trunc_nuw_trunc_nuw_xor(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nuw_trunc_nuw_xor
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = xor i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nuw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = xor i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nsw_trunc_nsw_xor(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nsw_trunc_nsw_xor
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = xor i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nsw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nsw i16 %y to i8
+  %r = xor i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nsw_trunc_nuw_xor(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nsw_trunc_nuw_xor
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = xor i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = xor i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nuw_trunc_xor(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nuw_trunc_xor
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = xor i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = xor i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nsw_trunc_xor(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nsw_trunc_xor
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = xor i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = xor i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nuw_trunc_nuw_or(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nuw_trunc_nuw_or
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = or i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nuw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = or i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nsw_trunc_nsw_or(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nsw_trunc_nsw_or
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = or i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nsw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nsw i16 %y to i8
+  %r = or i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nsw_trunc_nuw_or(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nsw_trunc_nuw_or
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = or i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = or i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nuw_trunc_or(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nuw_trunc_or
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = or i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = or i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nsw_trunc_or(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nsw_trunc_or
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = or i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = or i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nuw_trunc_nuw_and(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nuw_trunc_nuw_and
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = and i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nuw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = and i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nsw_trunc_nsw_and(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nsw_trunc_nsw_and
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = and i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nsw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nsw i16 %y to i8
+  %r = and i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nsw_trunc_nuw_and(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nsw_trunc_nuw_and
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = and i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nuw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc nuw i16 %y to i8
+  %r = and i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @trunc_nuw_trunc_and(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@trunc_nuw_trunc_and
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = and i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc nuw i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nuw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = and i8 %sx, %sy
+  ret i8 %r
+}
+
+define i8 @neg_trunc_nsw_trunc_and(i16 %x, i16 %y) {
+; CHECK-LABEL: define {{[^@]+}}@neg_trunc_nsw_trunc_and
+; CHECK-SAME: (i16 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R1:%.*]] = and i16 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = trunc i16 [[R1]] to i8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %sx = trunc nsw i16 %x to i8
+  %sy = trunc i16 %y to i8
+  %r = and i8 %sx, %sy
+  ret i8 %r
+}
+
 define i16 @and_zext_zext(i8 %x, i4 %y) {
 ; CHECK-LABEL: define {{[^@]+}}@and_zext_zext
 ; CHECK-SAME: (i8 [[X:%.*]], i4 [[Y:%.*]]) {

``````````

</details>


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


More information about the llvm-commits mailing list