[llvm] [DAG] FoldConstantArithmetic - add support for ISD::MULO/ADDO/SUBO constant folding (PR #228877)
Prajwal KP via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 23:27:35 PDT 2026
https://github.com/Prajwal-kp-18 updated https://github.com/llvm/llvm-project/pull/228877
>From cd103d590147cf214e2b39daba2f5af0202cdab7 Mon Sep 17 00:00:00 2001
From: Prajwal <prajwal.kp.1817 at gmail.com>
Date: Sat, 3 Oct 2026 10:43:24 +0530
Subject: [PATCH] [DAG] FoldConstantArithmetic - add support for
ISD::MULO/ADDO/SUBO constant folding
---
llvm/include/llvm/CodeGen/SelectionDAG.h | 7 ++
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 28 +++--
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 59 +++++++++
llvm/test/CodeGen/AArch64/pr88966.ll | 6 +-
.../CodeGen/AMDGPU/i128-add-carry-chain.ll | 27 +---
llvm/test/CodeGen/X86/addcarry.ll | 20 +--
llvm/test/CodeGen/X86/combine-addo.ll | 99 +++++++++++++++
llvm/test/CodeGen/X86/combine-mulo.ll | 92 ++++++++++++++
llvm/test/CodeGen/X86/combine-subo.ll | 115 +++++++++++++++++-
.../CodeGen/X86/smul_fix_sat_constants.ll | 9 +-
10 files changed, 400 insertions(+), 62 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index a345c90ae9c0a..3b6763ce88366 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2081,6 +2081,13 @@ class SelectionDAG {
EVT VT, ArrayRef<SDValue> Ops,
SDNodeFlags Flags = SDNodeFlags());
+ /// Constant fold a node with multiple results, returning a MERGE_VALUES of
+ /// the folded results on success.
+ LLVM_ABI SDValue FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
+ SDVTList VTList,
+ ArrayRef<SDValue> Ops,
+ SDNodeFlags Flags = SDNodeFlags());
+
/// Fold floating-point operations when all operands are constants and/or
/// undefined.
LLVM_ABI SDValue foldConstantFPMath(unsigned Opcode, const SDLoc &DL, EVT VT,
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c8a984a9dc56e..1ee738dc1a299 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -3632,6 +3632,11 @@ SDValue DAGCombiner::visitADDO(SDNode *N) {
return CombineTo(N, DAG.getNode(ISD::ADD, DL, VT, N0, N1),
DAG.getUNDEF(CarryVT));
+ // fold operation with constant operands.
+ if (SDValue C = DAG.FoldConstantArithmetic(N->getOpcode(), DL, N->getVTList(),
+ {N0, N1}))
+ return C;
+
// canonicalize constant to RHS.
if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
!DAG.isConstantIntBuildVectorOrConstantInt(N1))
@@ -4805,6 +4810,11 @@ SDValue DAGCombiner::visitSUBO(SDNode *N) {
return CombineTo(N, DAG.getNode(ISD::SUB, DL, VT, N0, N1),
DAG.getUNDEF(CarryVT));
+ // fold operation with constant operands.
+ if (SDValue C = DAG.FoldConstantArithmetic(N->getOpcode(), DL, N->getVTList(),
+ {N0, N1}))
+ return C;
+
// fold (subo x, x) -> 0 + no borrow
if (N0 == N1)
return CombineTo(N, DAG.getConstant(0, DL, VT),
@@ -6169,20 +6179,12 @@ SDValue DAGCombiner::visitMULO(SDNode *N) {
EVT CarryVT = N->getValueType(1);
SDLoc DL(N);
- ConstantSDNode *N0C = isConstOrConstSplat(N0);
- ConstantSDNode *N1C = isConstOrConstSplat(N1);
-
// fold operation with constant operands.
- // TODO: Move this to FoldConstantArithmetic when it supports nodes with
- // multiple results.
- if (N0C && N1C && !N0C->isOpaque() && !N1C->isOpaque()) {
- bool Overflow;
- APInt Result =
- IsSigned ? N0C->getAPIntValue().smul_ov(N1C->getAPIntValue(), Overflow)
- : N0C->getAPIntValue().umul_ov(N1C->getAPIntValue(), Overflow);
- return CombineTo(N, DAG.getConstant(Result, DL, VT),
- DAG.getBoolConstant(Overflow, DL, CarryVT, CarryVT));
- }
+ if (SDValue C = DAG.FoldConstantArithmetic(N->getOpcode(), DL, N->getVTList(),
+ {N0, N1}))
+ return C;
+
+ ConstantSDNode *N1C = isConstOrConstSplat(N1);
// canonicalize constant to RHS.
if (DAG.isConstantIntBuildVectorOrConstantInt(N0) &&
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 77922d8fb1344..0b51b150483bf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -8273,6 +8273,62 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
return V;
}
+SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
+ SDVTList VTList,
+ ArrayRef<SDValue> Ops,
+ SDNodeFlags Flags) {
+ if (VTList.NumVTs == 1)
+ return FoldConstantArithmetic(Opcode, DL, VTList.VTs[0], Ops, Flags);
+
+ switch (Opcode) {
+ case ISD::SADDO:
+ case ISD::UADDO:
+ case ISD::SSUBO:
+ case ISD::USUBO:
+ case ISD::SMULO:
+ case ISD::UMULO: {
+ // TODO: Support non-splat vector constants.
+ ConstantSDNode *C1 = isConstOrConstSplat(Ops[0]);
+ ConstantSDNode *C2 = isConstOrConstSplat(Ops[1]);
+ if (!C1 || !C2 || C1->isOpaque() || C2->isOpaque())
+ return SDValue();
+
+ const APInt &V1 = C1->getAPIntValue(), &V2 = C2->getAPIntValue();
+ bool Overflow;
+ APInt Result;
+ switch (Opcode) {
+ default:
+ llvm_unreachable("Unexpected overflow opcode");
+ case ISD::SADDO:
+ Result = V1.sadd_ov(V2, Overflow);
+ break;
+ case ISD::UADDO:
+ Result = V1.uadd_ov(V2, Overflow);
+ break;
+ case ISD::SSUBO:
+ Result = V1.ssub_ov(V2, Overflow);
+ break;
+ case ISD::USUBO:
+ Result = V1.usub_ov(V2, Overflow);
+ break;
+ case ISD::SMULO:
+ Result = V1.smul_ov(V2, Overflow);
+ break;
+ case ISD::UMULO:
+ Result = V1.umul_ov(V2, Overflow);
+ break;
+ }
+
+ EVT VT = VTList.VTs[0];
+ return getNode(ISD::MERGE_VALUES, DL, VTList,
+ {getConstant(Result, DL, VT),
+ getBoolConstant(Overflow, DL, VTList.VTs[1], VT)},
+ Flags);
+ }
+ }
+ return SDValue();
+}
+
SDValue SelectionDAG::foldConstantFPMath(unsigned Opcode, const SDLoc &DL,
EVT VT, ArrayRef<SDValue> Ops) {
// TODO: Add support for unary/ternary fp opcodes.
@@ -12044,6 +12100,9 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, SDVTList VTList,
"Operand is DELETED_NODE!");
#endif
+ if (SDValue V = FoldConstantArithmetic(Opcode, DL, VTList, Ops, Flags))
+ return V;
+
switch (Opcode) {
case ISD::SADDO:
case ISD::UADDO:
diff --git a/llvm/test/CodeGen/AArch64/pr88966.ll b/llvm/test/CodeGen/AArch64/pr88966.ll
index 0743773d171d7..7ade3654a2e93 100644
--- a/llvm/test/CodeGen/AArch64/pr88966.ll
+++ b/llvm/test/CodeGen/AArch64/pr88966.ll
@@ -4,12 +4,10 @@
define i32 @f(ptr %arg, i41 %arg1, ptr %arg2) {
; CHECK-LABEL: f:
; CHECK: // %bb.0: // %bb
-; CHECK-NEXT: and w9, w1, #0x1
-; CHECK-NEXT: mov w10, #1 // =0x1
+; CHECK-NEXT: and x9, x1, #0x1
; CHECK-NEXT: mov x8, x0
-; CHECK-NEXT: cmp w9, #1
; CHECK-NEXT: mov w0, wzr
-; CHECK-NEXT: adc x9, xzr, x10
+; CHECK-NEXT: add x9, x9, #1
; CHECK-NEXT: str x9, [x2]
; CHECK-NEXT: str xzr, [x8]
; CHECK-NEXT: ret
diff --git a/llvm/test/CodeGen/AMDGPU/i128-add-carry-chain.ll b/llvm/test/CodeGen/AMDGPU/i128-add-carry-chain.ll
index 2a8b9e38b9057..ea8477acbd629 100644
--- a/llvm/test/CodeGen/AMDGPU/i128-add-carry-chain.ll
+++ b/llvm/test/CodeGen/AMDGPU/i128-add-carry-chain.ll
@@ -28,20 +28,11 @@ define i128 @i128_add_uniform_carry_to_divergent(i128 %x) #0 {
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: s_swappc_b64 s[30:31], s[16:17]
; GFX9-NEXT: s_mov_b64 s[4:5], 1
-; GFX9-NEXT: s_mov_b64 s[6:7], 0
-; GFX9-NEXT: s_add_u32 s4, s6, s4
-; GFX9-NEXT: s_cselect_b64 s[8:9], -1, 0
-; GFX9-NEXT: s_cmp_lg_u64 s[8:9], 0
-; GFX9-NEXT: s_addc_u32 s5, s7, s5
-; GFX9-NEXT: s_cselect_b64 s[8:9], -1, 0
-; GFX9-NEXT: v_mov_b32_e32 v0, s6
-; GFX9-NEXT: v_addc_co_u32_e64 v2, s[8:9], v40, v0, s[8:9]
-; GFX9-NEXT: v_mov_b32_e32 v0, s7
-; GFX9-NEXT: v_addc_co_u32_e64 v3, s[6:7], v41, v0, s[8:9]
-; GFX9-NEXT: v_lshrrev_b64 v[4:5], s34, v[2:3]
; GFX9-NEXT: s_lshr_b64 s[6:7], s[4:5], s34
+; GFX9-NEXT: v_lshrrev_b64 v[4:5], s34, v[40:41]
; GFX9-NEXT: v_mov_b32_e32 v0, s4
; GFX9-NEXT: v_mov_b32_e32 v1, s6
+; GFX9-NEXT: v_mov_b32_e32 v2, v40
; GFX9-NEXT: v_mov_b32_e32 v3, v4
; GFX9-NEXT: v_readlane_b32 s30, v42, 1
; GFX9-NEXT: v_readlane_b32 s31, v42, 2
@@ -91,23 +82,13 @@ define i128 @i128_add_uniform_carry_to_divergent(i128 %x) #0 {
; GFX12-NEXT: s_wait_kmcnt 0x0
; GFX12-NEXT: s_swappc_b64 s[30:31], s[0:1]
; GFX12-NEXT: s_mov_b64 s[0:1], 1
-; GFX12-NEXT: s_mov_b64 s[2:3], 0
; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-NEXT: s_add_co_u32 s0, s2, s0
-; GFX12-NEXT: s_cselect_b32 s4, -1, 0
-; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-NEXT: s_cmp_lg_u32 s4, 0
-; GFX12-NEXT: s_add_co_ci_u32 s1, s3, s1
-; GFX12-NEXT: s_cselect_b32 s4, -1, 0
-; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-NEXT: v_add_co_ci_u32_e64 v2, s2, v40, s2, s4
-; GFX12-NEXT: s_wait_alu depctr_va_sdst(0)
-; GFX12-NEXT: v_add_co_ci_u32_e64 v3, s2, v41, s3, s2
-; GFX12-NEXT: v_lshrrev_b64 v[3:4], s34, v[2:3]
; GFX12-NEXT: s_lshr_b64 s[2:3], s[0:1], s34
+; GFX12-NEXT: v_lshrrev_b64 v[3:4], s34, v[40:41]
; GFX12-NEXT: v_mov_b32_e32 v0, s0
; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
; GFX12-NEXT: v_mov_b32_e32 v1, s2
+; GFX12-NEXT: v_mov_b32_e32 v2, v40
; GFX12-NEXT: v_readlane_b32 s30, v42, 1
; GFX12-NEXT: v_readlane_b32 s31, v42, 2
; GFX12-NEXT: v_readlane_b32 s34, v42, 0
diff --git a/llvm/test/CodeGen/X86/addcarry.ll b/llvm/test/CodeGen/X86/addcarry.ll
index 2031a17a23be9..7de8ba36140da 100644
--- a/llvm/test/CodeGen/X86/addcarry.ll
+++ b/llvm/test/CodeGen/X86/addcarry.ll
@@ -3154,20 +3154,10 @@ define { i64, i64 } @addcarry_commutative_2(i64 %x0, i64 %x1, i64 %y0, i64 %y1)
}
define i1 @pr84831(i64 %arg) {
-; X64-LABEL: pr84831:
-; X64: # %bb.0:
-; X64-NEXT: testq %rdi, %rdi
-; X64-NEXT: setne %al
-; X64-NEXT: xorl %ecx, %ecx
-; X64-NEXT: addb $-1, %al
-; X64-NEXT: adcq $1, %rcx
-; X64-NEXT: setb %al
-; X64-NEXT: retq
-;
-; X86-LABEL: pr84831:
-; X86: # %bb.0:
-; X86-NEXT: xorl %eax, %eax
-; X86-NEXT: retl
+; CHECK-LABEL: pr84831:
+; CHECK: # %bb.0:
+; CHECK-NEXT: xorl %eax, %eax
+; CHECK-NEXT: ret{{[l|q]}}
%a = icmp ult i64 0, %arg
%add1 = add i64 0, 1
%carryout1 = icmp ult i64 %add1, 0
@@ -3243,5 +3233,3 @@ define void @pr169691(ptr %p0, i64 %implicit, i1 zeroext %carry) nounwind {
store i64 %uaddo1b.0, ptr %p1, align 8
ret void
}
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; CHECK: {{.*}}
diff --git a/llvm/test/CodeGen/X86/combine-addo.ll b/llvm/test/CodeGen/X86/combine-addo.ll
index ba748b6e653cf..7619a338a6369 100644
--- a/llvm/test/CodeGen/X86/combine-addo.ll
+++ b/llvm/test/CodeGen/X86/combine-addo.ll
@@ -143,3 +143,102 @@ define <4 x i32> @combine_vec_uadd_no_overflow(<4 x i32> %a0, <4 x i32> %a1, <4
%6 = select <4 x i1> %5, <4 x i32> %a2, <4 x i32> %4
ret <4 x i32> %4
}
+
+; fold (sadd c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_sadd_constant_overflow() {
+; CHECK-LABEL: combine_sadd_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2147483648, %eax # imm = 0x80000000
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 2147483647, i32 1)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_sadd_constant_no_overflow() {
+; CHECK-LABEL: combine_sadd_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 2147483646, i32 1)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_sadd_constant_overflow() {
+; SSE-LABEL: combine_vec_sadd_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [2147483648,2147483648,2147483648,2147483648]
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_sadd_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [2147483648,2147483648,2147483648,2147483648]
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.sadd.with.overflow.v4i32(<4 x i32> splat (i32 2147483647), <4 x i32> splat (i32 1))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; fold (uadd c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_uadd_constant_overflow() {
+; CHECK-LABEL: combine_uadd_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $1, %eax
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -1, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_uadd_constant_no_overflow() {
+; CHECK-LABEL: combine_uadd_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-1, %eax
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 -3, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_uadd_constant_overflow() {
+; SSE-LABEL: combine_vec_uadd_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [1,1,1,1]
+; SSE-NEXT: movaps %xmm0, %xmm1
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_uadd_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [1,1,1,1]
+; AVX-NEXT: vmovaps %xmm0, %xmm1
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.uadd.with.overflow.v4i32(<4 x i32> splat (i32 -1), <4 x i32> splat (i32 2))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; Opaque constants (as created by constant hoisting) should not be folded.
+define { i64, i1 } @combine_sadd_constant_opaque() {
+; CHECK-LABEL: combine_sadd_constant_opaque:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movabsq $9223372036854775792, %rax # imm = 0x7FFFFFFFFFFFFFF0
+; CHECK-NEXT: addq $100, %rax
+; CHECK-NEXT: seto %dl
+; CHECK-NEXT: retq
+ %c = bitcast i64 9223372036854775792 to i64
+ %x = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %c, i64 100)
+ ret { i64, i1 } %x
+}
+
+define { i64, i1 } @combine_uadd_constant_opaque() {
+; CHECK-LABEL: combine_uadd_constant_opaque:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movabsq $-1311768467463790320, %rax # imm = 0xEDCBA98765432110
+; CHECK-NEXT: addq $-3, %rax
+; CHECK-NEXT: setb %dl
+; CHECK-NEXT: retq
+ %c = bitcast i64 -1311768467463790320 to i64
+ %x = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %c, i64 -3)
+ ret { i64, i1 } %x
+}
diff --git a/llvm/test/CodeGen/X86/combine-mulo.ll b/llvm/test/CodeGen/X86/combine-mulo.ll
index 4a27c4da6f35c..82b50cca01378 100644
--- a/llvm/test/CodeGen/X86/combine-mulo.ll
+++ b/llvm/test/CodeGen/X86/combine-mulo.ll
@@ -152,3 +152,95 @@ define { i64, i1 } @combine_umul_constant_opaque() {
%x = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 3, i64 %c)
ret { i64, i1 } %x
}
+
+; fold (smul c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_smul_constant_overflow() {
+; CHECK-LABEL: combine_smul_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2, %eax
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 2147483647, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_smul_constant_no_overflow() {
+; CHECK-LABEL: combine_smul_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $2147483646, %eax # imm = 0x7FFFFFFE
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 1073741823, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_smul_constant_overflow() {
+; SSE-LABEL: combine_vec_smul_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_smul_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.smul.with.overflow.v4i32(<4 x i32> splat (i32 2147483647), <4 x i32> splat (i32 2))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; fold (umul c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_umul_constant_overflow() {
+; CHECK-LABEL: combine_umul_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2, %eax
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 -1, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_umul_constant_no_overflow() {
+; CHECK-LABEL: combine_umul_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2, %eax
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 2147483647, i32 2)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_umul_constant_overflow() {
+; SSE-LABEL: combine_vec_umul_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_umul_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.umul.with.overflow.v4i32(<4 x i32> splat (i32 -1), <4 x i32> splat (i32 2))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; Non-splat constants are not folded by the combine, and are instead folded
+; when the node is expanded during legalization.
+define { <4 x i32>, <4 x i1> } @combine_vec_umul_constant_overflow_nonsplat() {
+; SSE-LABEL: combine_vec_umul_constant_overflow_nonsplat:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967292]
+; SSE-NEXT: pcmpeqd %xmm1, %xmm1
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_umul_constant_overflow_nonsplat:
+; AVX: # %bb.0:
+; AVX-NEXT: vmovaps {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967292]
+; AVX-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.umul.with.overflow.v4i32(<4 x i32> <i32 -1, i32 -1, i32 -1, i32 -2>, <4 x i32> splat (i32 2))
+ ret { <4 x i32>, <4 x i1> } %x
+}
diff --git a/llvm/test/CodeGen/X86/combine-subo.ll b/llvm/test/CodeGen/X86/combine-subo.ll
index 5e4bba6e0fd35..e4be84f6ff0d4 100644
--- a/llvm/test/CodeGen/X86/combine-subo.ll
+++ b/llvm/test/CodeGen/X86/combine-subo.ll
@@ -201,14 +201,14 @@ define { i8, i1 } @ssub_always_overflow(i8 %x) nounwind {
define { <4 x i8>, <4 x i1> } @always_usub_const_vector() nounwind {
; SSE-LABEL: always_usub_const_vector:
; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
; SSE-NEXT: pcmpeqd %xmm0, %xmm0
-; SSE-NEXT: pcmpeqd %xmm1, %xmm1
; SSE-NEXT: retq
;
; AVX-LABEL: always_usub_const_vector:
; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
; AVX-NEXT: vpcmpeqd %xmm0, %xmm0, %xmm0
-; AVX-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1
; AVX-NEXT: retq
%x = call { <4 x i8>, <4 x i1> } @llvm.usub.with.overflow.v4i8(<4 x i8> <i8 0, i8 0, i8 0, i8 0>, <4 x i8> <i8 1, i8 1, i8 1, i8 1>)
ret { <4 x i8>, <4 x i1> } %x
@@ -229,3 +229,114 @@ define { <4 x i8>, <4 x i1> } @never_usub_const_vector() nounwind {
%x = call { <4 x i8>, <4 x i1> } @llvm.usub.with.overflow.v4i8(<4 x i8> <i8 255, i8 255, i8 255, i8 255>, <4 x i8> <i8 128, i8 0, i8 255, i8 1>)
ret { <4 x i8>, <4 x i1> } %x
}
+
+; fold (ssub c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_ssub_constant_overflow() {
+; CHECK-LABEL: combine_ssub_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483648, i32 1)
+ ret { i32, i1 } %x
+}
+
+; INT_MIN on the RHS is not canonicalized to saddo.
+define { i32, i1 } @combine_ssub_constant_overflow_minsigned() {
+; CHECK-LABEL: combine_ssub_constant_overflow_minsigned:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2147483648, %eax # imm = 0x80000000
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 0, i32 -2147483648)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_ssub_constant_no_overflow() {
+; CHECK-LABEL: combine_ssub_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2147483648, %eax # imm = 0x80000000
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -2147483647, i32 1)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_ssub_constant_overflow() {
+; SSE-LABEL: combine_vec_ssub_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [2147483647,2147483647,2147483647,2147483647]
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_ssub_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [2147483647,2147483647,2147483647,2147483647]
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.ssub.with.overflow.v4i32(<4 x i32> splat (i32 -2147483648), <4 x i32> splat (i32 1))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; fold (usub c1, c2) -> c3 + overflow
+define { i32, i1 } @combine_usub_constant_overflow() {
+; CHECK-LABEL: combine_usub_constant_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-2, %eax
+; CHECK-NEXT: movb $1, %dl
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 1, i32 3)
+ ret { i32, i1 } %x
+}
+
+define { i32, i1 } @combine_usub_constant_no_overflow() {
+; CHECK-LABEL: combine_usub_constant_no_overflow:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $2, %eax
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: retq
+ %x = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 3, i32 1)
+ ret { i32, i1 } %x
+}
+
+define { <4 x i32>, <4 x i1> } @combine_vec_usub_constant_overflow() {
+; SSE-LABEL: combine_vec_usub_constant_overflow:
+; SSE: # %bb.0:
+; SSE-NEXT: movaps {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; SSE-NEXT: movaps {{.*#+}} xmm1 = [1,1,1,1]
+; SSE-NEXT: retq
+;
+; AVX-LABEL: combine_vec_usub_constant_overflow:
+; AVX: # %bb.0:
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm0 = [4294967294,4294967294,4294967294,4294967294]
+; AVX-NEXT: vbroadcastss {{.*#+}} xmm1 = [1,1,1,1]
+; AVX-NEXT: retq
+ %x = call { <4 x i32>, <4 x i1> } @llvm.usub.with.overflow.v4i32(<4 x i32> splat (i32 1), <4 x i32> splat (i32 3))
+ ret { <4 x i32>, <4 x i1> } %x
+}
+
+; Opaque constants (as created by constant hoisting) should not be folded.
+define { i64, i1 } @combine_ssub_constant_opaque() {
+; CHECK-LABEL: combine_ssub_constant_opaque:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movabsq $-9223372036854775792, %rax # imm = 0x8000000000000010
+; CHECK-NEXT: addq $-100, %rax
+; CHECK-NEXT: seto %dl
+; CHECK-NEXT: retq
+ %c = bitcast i64 -9223372036854775792 to i64
+ %x = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %c, i64 100)
+ ret { i64, i1 } %x
+}
+
+define { i64, i1 } @combine_usub_constant_opaque() {
+; CHECK-LABEL: combine_usub_constant_opaque:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movabsq $1311768467463790320, %rcx # imm = 0x123456789ABCDEF0
+; CHECK-NEXT: movl $3, %eax
+; CHECK-NEXT: subq %rcx, %rax
+; CHECK-NEXT: setb %dl
+; CHECK-NEXT: retq
+ %c = bitcast i64 1311768467463790320 to i64
+ %x = call { i64, i1 } @llvm.usub.with.overflow.i64(i64 3, i64 %c)
+ ret { i64, i1 } %x
+}
diff --git a/llvm/test/CodeGen/X86/smul_fix_sat_constants.ll b/llvm/test/CodeGen/X86/smul_fix_sat_constants.ll
index 5017838a4f265..15756c4fca051 100644
--- a/llvm/test/CodeGen/X86/smul_fix_sat_constants.ll
+++ b/llvm/test/CodeGen/X86/smul_fix_sat_constants.ll
@@ -29,10 +29,11 @@ define i64 @func() nounwind {
define i64 @func2() nounwind {
; X64-LABEL: func2:
; X64: # %bb.0:
-; X64-NEXT: movl $3, %eax
-; X64-NEXT: imulq $2, %rax, %rcx
-; X64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF
-; X64-NEXT: cmovaeq %rcx, %rax
+; X64-NEXT: xorl %eax, %eax
+; X64-NEXT: testb %al, %al
+; X64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF
+; X64-NEXT: movl $6, %eax
+; X64-NEXT: cmovneq %rcx, %rax
; X64-NEXT: retq
%tmp = call i64 @llvm.smul.fix.sat.i64(i64 3, i64 2, i32 0)
ret i64 %tmp
More information about the llvm-commits
mailing list