[llvm] [DAGCombiner] Reconstruct borrow chain from icmp pattern for USUBO_CARRY (PR #193707)
Paweł Bylica via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 29 07:54:35 PDT 2026
https://github.com/chfast updated https://github.com/llvm/llvm-project/pull/193707
>From c6f02687a11bd3c622f3082b987a9478f6c3ac68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 2 Apr 2026 09:23:48 +0200
Subject: [PATCH 1/5] [X86][AArch64][test] Add borrow chain tests for
subtract-with-carry (NFC)
Add test cases to cgp-usubo.ll (AArch64) and update subcarry.ll (X86)
for the borrow chain pattern:
or(icmp ult A, B, and(icmp eq A, B, carry_in))
Positive tests: 2-limb, 3-limb, commuted eq operands.
Negative tests: i128 (non-simple type), different operands, signed cmp.
The X86 subcarry_ult_2x64_2 function currently produces suboptimal
setb/sete/and/or instead of cmp+sbb.
---
llvm/test/CodeGen/AArch64/cgp-usubo.ll | 129 ++++++++++++++++++++++++-
llvm/test/CodeGen/X86/subcarry.ll | 76 +++++++++++++++
2 files changed, 200 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/cgp-usubo.ll b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
index d307107fc07ee..b91616805d402 100644
--- a/llvm/test/CodeGen/AArch64/cgp-usubo.ll
+++ b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
@@ -120,6 +120,125 @@ define i1 @usubo_eq_constant1_op1_i32(i32 %x, ptr %p) nounwind {
ret i1 %ov
}
+; Borrow chain: or(icmp ult X, Y, and(icmp eq X, Y, carry_in)) -> cmp + sbcs.
+; See https://github.com/llvm/llvm-project/issues/106118.
+
+define i1 @subcarry_ult_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
+; CHECK-LABEL: subcarry_ult_2x64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x0, x2
+; CHECK-NEXT: cset w8, lo
+; CHECK-NEXT: cmp x1, x3
+; CHECK-NEXT: csel w8, wzr, w8, ne
+; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: ret
+ %b0 = icmp ult i64 %x0, %y0
+ %b1 = icmp ult i64 %x1, %y1
+ %e1 = icmp eq i64 %x1, %y1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
+
+; Commuted eq operands: icmp eq Y, X instead of icmp eq X, Y.
+define i1 @subcarry_ult_2x64_commuted_eq(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
+; CHECK-LABEL: subcarry_ult_2x64_commuted_eq:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x0, x2
+; CHECK-NEXT: ccmp x3, x1, #0, lo
+; CHECK-NEXT: ccmp x1, x3, #0, ne
+; CHECK-NEXT: cset w0, lo
+; CHECK-NEXT: ret
+ %b0 = icmp ult i64 %x0, %y0
+ %b1 = icmp ult i64 %x1, %y1
+ %e1 = icmp eq i64 %y1, %x1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
+
+define i1 @subcarry_ult_3x64(i64 %x0, i64 %x1, i64 %x2, i64 %y0, i64 %y1, i64 %y2) nounwind {
+; CHECK-LABEL: subcarry_ult_3x64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x0, x3
+; CHECK-NEXT: cset w8, lo
+; CHECK-NEXT: cmp x1, x4
+; CHECK-NEXT: csel w8, wzr, w8, ne
+; CHECK-NEXT: csinc w8, w8, wzr, hs
+; CHECK-NEXT: cmp x2, x5
+; CHECK-NEXT: csel w8, wzr, w8, ne
+; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: ret
+ %b0 = icmp ult i64 %x0, %y0
+ %b1 = icmp ult i64 %x1, %y1
+ %e1 = icmp eq i64 %x1, %y1
+ %bp1 = and i1 %b0, %e1
+ %br1 = or i1 %b1, %bp1
+ %b2 = icmp ult i64 %x2, %y2
+ %e2 = icmp eq i64 %x2, %y2
+ %bp2 = and i1 %br1, %e2
+ %br2 = or i1 %b2, %bp2
+ ret i1 %br2
+}
+
+; Unsigned less than of two 2x128 integers combined into a borrow chain.
+define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
+; CHECK-LABEL: subcarry_ult_2x128:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x2, x6
+; CHECK-NEXT: cset w8, eq
+; CHECK-NEXT: cmp x3, x7
+; CHECK-NEXT: csel w8, wzr, w8, ne
+; CHECK-NEXT: cmp x0, x4
+; CHECK-NEXT: sbcs xzr, x1, x5
+; CHECK-NEXT: csel w8, wzr, w8, hs
+; CHECK-NEXT: cmp x2, x6
+; CHECK-NEXT: sbcs xzr, x3, x7
+; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: ret
+ %b0 = icmp ult i128 %x0, %y0
+ %b1 = icmp ult i128 %x1, %y1
+ %e1 = icmp eq i128 %x1, %y1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
+
+; Negative test: icmp eq compares different operands than icmp ult.
+define i1 @no_subcarry_different_ops(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
+; CHECK-LABEL: no_subcarry_different_ops:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x0, x2
+; CHECK-NEXT: ccmp x1, x0, #0, lo
+; CHECK-NEXT: ccmp x1, x3, #0, ne
+; CHECK-NEXT: cset w0, lo
+; CHECK-NEXT: ret
+ %b0 = icmp ult i64 %x0, %y0
+ %b1 = icmp ult i64 %x1, %y1
+ %e1 = icmp eq i64 %x1, %x0
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
+
+; Negative test: icmp slt instead of icmp ult.
+define i1 @no_subcarry_signed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
+; CHECK-LABEL: no_subcarry_signed:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmp x0, x2
+; CHECK-NEXT: cset w8, lo
+; CHECK-NEXT: cmp x1, x3
+; CHECK-NEXT: csel w8, wzr, w8, ne
+; CHECK-NEXT: csinc w0, w8, wzr, ge
+; CHECK-NEXT: ret
+ %b0 = icmp ult i64 %x0, %y0
+ %b1 = icmp slt i64 %x1, %y1
+ %e1 = icmp eq i64 %x1, %y1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
+
; Verify insertion point for multi-BB.
declare void @call(i1)
@@ -127,12 +246,12 @@ declare void @call(i1)
define i1 @usubo_ult_sub_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwind {
; CHECK-LABEL: usubo_ult_sub_dominates_i64:
; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: tbz w3, #0, .LBB7_2
+; CHECK-NEXT: tbz w3, #0, .LBB13_2
; CHECK-NEXT: // %bb.1: // %t
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: cset w3, lo
; CHECK-NEXT: str x8, [x2]
-; CHECK-NEXT: .LBB7_2: // %common.ret
+; CHECK-NEXT: .LBB13_2: // %common.ret
; CHECK-NEXT: and w0, w3, #0x1
; CHECK-NEXT: ret
entry:
@@ -158,7 +277,7 @@ define i1 @usubo_ult_cmp_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwin
; CHECK-NEXT: stp x20, x19, [sp, #32] // 16-byte Folded Spill
; CHECK-NEXT: mov w19, w3
; CHECK-NEXT: stp x22, x21, [sp, #16] // 16-byte Folded Spill
-; CHECK-NEXT: tbz w3, #0, .LBB8_3
+; CHECK-NEXT: tbz w3, #0, .LBB14_3
; CHECK-NEXT: // %bb.1: // %t
; CHECK-NEXT: cmp x0, x1
; CHECK-NEXT: mov x22, x0
@@ -168,11 +287,11 @@ define i1 @usubo_ult_cmp_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwin
; CHECK-NEXT: mov w0, w21
; CHECK-NEXT: bl call
; CHECK-NEXT: subs x8, x22, x23
-; CHECK-NEXT: b.hs .LBB8_3
+; CHECK-NEXT: b.hs .LBB14_3
; CHECK-NEXT: // %bb.2: // %end
; CHECK-NEXT: mov w19, w21
; CHECK-NEXT: str x8, [x20]
-; CHECK-NEXT: .LBB8_3: // %common.ret
+; CHECK-NEXT: .LBB14_3: // %common.ret
; CHECK-NEXT: and w0, w19, #0x1
; CHECK-NEXT: ldp x20, x19, [sp, #32] // 16-byte Folded Reload
; CHECK-NEXT: ldp x22, x21, [sp, #16] // 16-byte Folded Reload
diff --git a/llvm/test/CodeGen/X86/subcarry.ll b/llvm/test/CodeGen/X86/subcarry.ll
index 60bedad53ac9a..7c5b9d361df90 100644
--- a/llvm/test/CodeGen/X86/subcarry.ll
+++ b/llvm/test/CodeGen/X86/subcarry.ll
@@ -1437,5 +1437,81 @@ entry:
%2 = and i1 %1, %0
ret i1 %2
}
+
+; Unsigned less than of two 2x128 integers combined into a borrow chain.
+define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
+; X64-LABEL: subcarry_ult_2x128:
+; X64: # %bb.0:
+; X64-NEXT: movq {{[0-9]+}}(%rsp), %rax
+; X64-NEXT: movq {{[0-9]+}}(%rsp), %r10
+; X64-NEXT: cmpq %r8, %rdi
+; X64-NEXT: sbbq %r9, %rsi
+; X64-NEXT: setb %sil
+; X64-NEXT: cmpq %r10, %rdx
+; X64-NEXT: movq %rcx, %rdi
+; X64-NEXT: sbbq %rax, %rdi
+; X64-NEXT: setb %dil
+; X64-NEXT: xorq %rax, %rcx
+; X64-NEXT: xorq %r10, %rdx
+; X64-NEXT: orq %rcx, %rdx
+; X64-NEXT: sete %al
+; X64-NEXT: andb %sil, %al
+; X64-NEXT: orb %dil, %al
+; X64-NEXT: retq
+;
+; X86-LABEL: subcarry_ult_2x128:
+; X86: # %bb.0:
+; X86-NEXT: pushl %ebp
+; X86-NEXT: movl %esp, %ebp
+; X86-NEXT: pushl %ebx
+; X86-NEXT: pushl %edi
+; X86-NEXT: pushl %esi
+; X86-NEXT: andl $-16, %esp
+; X86-NEXT: subl $16, %esp
+; X86-NEXT: movl 24(%ebp), %esi
+; X86-NEXT: movl 28(%ebp), %ebx
+; X86-NEXT: movl 8(%ebp), %edx
+; X86-NEXT: cmpl 40(%ebp), %edx
+; X86-NEXT: movl 12(%ebp), %edx
+; X86-NEXT: sbbl 44(%ebp), %edx
+; X86-NEXT: movl 16(%ebp), %edx
+; X86-NEXT: sbbl 48(%ebp), %edx
+; X86-NEXT: movl 20(%ebp), %edx
+; X86-NEXT: sbbl 52(%ebp), %edx
+; X86-NEXT: setb {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Folded Spill
+; X86-NEXT: cmpl 56(%ebp), %esi
+; X86-NEXT: movl %ebx, %edx
+; X86-NEXT: sbbl 60(%ebp), %edx
+; X86-NEXT: movl 32(%ebp), %edx
+; X86-NEXT: movl %edx, %edi
+; X86-NEXT: sbbl 64(%ebp), %edi
+; X86-NEXT: movl 68(%ebp), %edi
+; X86-NEXT: movl 36(%ebp), %ecx
+; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: sbbl %edi, %eax
+; X86-NEXT: setb %ah
+; X86-NEXT: xorl %edi, %ecx
+; X86-NEXT: xorl 60(%ebp), %ebx
+; X86-NEXT: orl %ecx, %ebx
+; X86-NEXT: xorl 64(%ebp), %edx
+; X86-NEXT: xorl 56(%ebp), %esi
+; X86-NEXT: orl %edx, %esi
+; X86-NEXT: orl %ebx, %esi
+; X86-NEXT: sete %al
+; X86-NEXT: andb {{[-0-9]+}}(%e{{[sb]}}p), %al # 1-byte Folded Reload
+; X86-NEXT: orb %ah, %al
+; X86-NEXT: leal -12(%ebp), %esp
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: popl %ebx
+; X86-NEXT: popl %ebp
+; X86-NEXT: retl
+ %b0 = icmp ult i128 %x0, %y0
+ %b1 = icmp ult i128 %x1, %y1
+ %e1 = icmp eq i128 %x1, %y1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
>From def24ea75e7bce9e3c10173b98e465a6af7081c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 23 Apr 2026 11:57:11 +0200
Subject: [PATCH 2/5] [DAGCombiner] Reconstruct borrow chain from icmp pattern
for USUBO_CARRY
DAG-level alternative to #189018 (CGP): match the canonical icmp form
carry_out = or(icmp ult A, B, and(icmp eq A, B, carry_in))
in visitOR and rewrite to USUBO_CARRY so the backend can chain the
borrow through sbb/sbcs.
Gated on USUBO_CARRY being legal/custom at the type the integer
legalizes to, so targets without hardware carry-flag support are
unaffected. For oversize integers (e.g. i128 on x86_64/aarch64) type
legalization then expands one USUBO_CARRY into a chain of
register-width USUBO_CARRYs, which gives strictly better code than the
CGP-level reconstruction.
Fixes llvm#106118.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 31 +++++++++++
llvm/test/CodeGen/AArch64/cgp-usubo.ll | 28 +++-------
llvm/test/CodeGen/X86/subcarry.ll | 52 +++++--------------
3 files changed, 51 insertions(+), 60 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 20ff48789152b..bf0dbf15c2610 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -3907,6 +3907,34 @@ static SDValue combineCarryDiamond(SelectionDAG &DAG, const TargetLowering &TLI,
return Merged.getValue(1);
}
+// Reconstruct a subtract-with-borrow chain from its canonicalized icmp form:
+// carry_out = or(icmp ult A, B, and(icmp eq A, B, carry_in))
+// InstCombine collapses chained usub.with.overflow intrinsics into this
+// shape, losing the direct USUBO_CARRY that the backend can chain through
+// sbb/sbcs. Fixes llvm#106118.
+static SDValue combineOrOfSetCCToUSUBOCarry(SDNode *N, SelectionDAG &DAG,
+ const TargetLowering &TLI) {
+ SDValue A, B, CarryIn;
+ if (!sd_match(N, m_Or(m_SetCC(m_Value(A), m_Value(B),
+ m_SpecificCondCode(ISD::SETULT)),
+ m_And(m_c_SetCC(m_Deferred(A), m_Deferred(B),
+ m_SpecificCondCode(ISD::SETEQ)),
+ m_Value(CarryIn)))))
+ return SDValue();
+
+ // Require USUBO_CARRY on the post-legalization type; for oversize integers
+ // (e.g. i128) type legalization then expands one USUBO_CARRY into a chain
+ // of register-width USUBO_CARRYs, which is exactly what we want.
+ EVT IntVT = A.getValueType();
+ if (!TLI.isOperationLegalOrCustom(
+ ISD::USUBO_CARRY, TLI.getTypeToTransformTo(*DAG.getContext(), IntVT)))
+ return SDValue();
+
+ SDLoc DL(N);
+ SDVTList VTs = DAG.getVTList(IntVT, N->getValueType(0));
+ return DAG.getNode(ISD::USUBO_CARRY, DL, VTs, A, B, CarryIn).getValue(1);
+}
+
SDValue DAGCombiner::visitUADDO_CARRYLike(SDValue N0, SDValue N1,
SDValue CarryIn, SDNode *N) {
// fold (uaddo_carry (xor a, -1), b, c) -> (usubo_carry b, a, !c) and flip
@@ -8744,6 +8772,9 @@ SDValue DAGCombiner::visitOR(SDNode *N) {
if (SDValue Combined = combineCarryDiamond(DAG, TLI, N0, N1, N))
return Combined;
+ if (SDValue Combined = combineOrOfSetCCToUSUBOCarry(N, DAG, TLI))
+ return Combined;
+
// Recognize halfword bswaps as (bswap + rotl 16) or (bswap + shl 16)
if (SDValue BSwap = MatchBSwapHWord(N, N0, N1))
return BSwap;
diff --git a/llvm/test/CodeGen/AArch64/cgp-usubo.ll b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
index b91616805d402..be8aa3eb82075 100644
--- a/llvm/test/CodeGen/AArch64/cgp-usubo.ll
+++ b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
@@ -127,10 +127,8 @@ define i1 @subcarry_ult_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
; CHECK-LABEL: subcarry_ult_2x64:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x0, x2
-; CHECK-NEXT: cset w8, lo
-; CHECK-NEXT: cmp x1, x3
-; CHECK-NEXT: csel w8, wzr, w8, ne
-; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: sbcs xzr, x1, x3
+; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%b0 = icmp ult i64 %x0, %y0
%b1 = icmp ult i64 %x1, %y1
@@ -145,8 +143,7 @@ define i1 @subcarry_ult_2x64_commuted_eq(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nou
; CHECK-LABEL: subcarry_ult_2x64_commuted_eq:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x0, x2
-; CHECK-NEXT: ccmp x3, x1, #0, lo
-; CHECK-NEXT: ccmp x1, x3, #0, ne
+; CHECK-NEXT: sbcs xzr, x1, x3
; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%b0 = icmp ult i64 %x0, %y0
@@ -161,13 +158,9 @@ define i1 @subcarry_ult_3x64(i64 %x0, i64 %x1, i64 %x2, i64 %y0, i64 %y1, i64 %y
; CHECK-LABEL: subcarry_ult_3x64:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x0, x3
-; CHECK-NEXT: cset w8, lo
-; CHECK-NEXT: cmp x1, x4
-; CHECK-NEXT: csel w8, wzr, w8, ne
-; CHECK-NEXT: csinc w8, w8, wzr, hs
-; CHECK-NEXT: cmp x2, x5
-; CHECK-NEXT: csel w8, wzr, w8, ne
-; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: sbcs xzr, x1, x4
+; CHECK-NEXT: sbcs xzr, x2, x5
+; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%b0 = icmp ult i64 %x0, %y0
%b1 = icmp ult i64 %x1, %y1
@@ -185,16 +178,11 @@ define i1 @subcarry_ult_3x64(i64 %x0, i64 %x1, i64 %x2, i64 %y0, i64 %y1, i64 %y
define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
; CHECK-LABEL: subcarry_ult_2x128:
; CHECK: // %bb.0:
-; CHECK-NEXT: cmp x2, x6
-; CHECK-NEXT: cset w8, eq
-; CHECK-NEXT: cmp x3, x7
-; CHECK-NEXT: csel w8, wzr, w8, ne
; CHECK-NEXT: cmp x0, x4
; CHECK-NEXT: sbcs xzr, x1, x5
-; CHECK-NEXT: csel w8, wzr, w8, hs
-; CHECK-NEXT: cmp x2, x6
+; CHECK-NEXT: sbcs xzr, x2, x6
; CHECK-NEXT: sbcs xzr, x3, x7
-; CHECK-NEXT: csinc w0, w8, wzr, hs
+; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%b0 = icmp ult i128 %x0, %y0
%b1 = icmp ult i128 %x1, %y1
diff --git a/llvm/test/CodeGen/X86/subcarry.ll b/llvm/test/CodeGen/X86/subcarry.ll
index 7c5b9d361df90..8c359074f99da 100644
--- a/llvm/test/CodeGen/X86/subcarry.ll
+++ b/llvm/test/CodeGen/X86/subcarry.ll
@@ -1360,41 +1360,23 @@ define i1 @subcarry_ult_2x64_2(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
; X64-LABEL: subcarry_ult_2x64_2:
; X64: # %bb.0: # %entry
; X64-NEXT: cmpq %rdx, %rdi
-; X64-NEXT: setb %dl
-; X64-NEXT: cmpq %rcx, %rsi
-; X64-NEXT: setb %cl
-; X64-NEXT: sete %al
-; X64-NEXT: andb %dl, %al
-; X64-NEXT: orb %cl, %al
+; X64-NEXT: sbbq %rcx, %rsi
+; X64-NEXT: setb %al
; X64-NEXT: retq
;
; X86-LABEL: subcarry_ult_2x64_2:
; X86: # %bb.0: # %entry
-; X86-NEXT: pushl %ebx
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %ebx
-; X86-NEXT: cmpl {{[0-9]+}}(%esp), %edi
-; X86-NEXT: sbbl {{[0-9]+}}(%esp), %ebx
-; X86-NEXT: setb %bl
-; X86-NEXT: cmpl %ecx, %eax
-; X86-NEXT: movl %edx, %edi
-; X86-NEXT: sbbl %esi, %edi
-; X86-NEXT: setb %bh
-; X86-NEXT: xorl %esi, %edx
-; X86-NEXT: xorl %ecx, %eax
-; X86-NEXT: orl %edx, %eax
-; X86-NEXT: sete %al
-; X86-NEXT: andb %bl, %al
-; X86-NEXT: orb %bh, %al
+; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
+; X86-NEXT: cmpl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: sbbl {{[0-9]+}}(%esp), %esi
+; X86-NEXT: sbbl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: sbbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: setb %al
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
-; X86-NEXT: popl %ebx
; X86-NEXT: retl
entry:
%0 = icmp ult i64 %x0, %y0
@@ -1442,21 +1424,11 @@ entry:
define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
; X64-LABEL: subcarry_ult_2x128:
; X64: # %bb.0:
-; X64-NEXT: movq {{[0-9]+}}(%rsp), %rax
-; X64-NEXT: movq {{[0-9]+}}(%rsp), %r10
; X64-NEXT: cmpq %r8, %rdi
; X64-NEXT: sbbq %r9, %rsi
-; X64-NEXT: setb %sil
-; X64-NEXT: cmpq %r10, %rdx
-; X64-NEXT: movq %rcx, %rdi
-; X64-NEXT: sbbq %rax, %rdi
-; X64-NEXT: setb %dil
-; X64-NEXT: xorq %rax, %rcx
-; X64-NEXT: xorq %r10, %rdx
-; X64-NEXT: orq %rcx, %rdx
-; X64-NEXT: sete %al
-; X64-NEXT: andb %sil, %al
-; X64-NEXT: orb %dil, %al
+; X64-NEXT: sbbq {{[0-9]+}}(%rsp), %rdx
+; X64-NEXT: sbbq {{[0-9]+}}(%rsp), %rcx
+; X64-NEXT: setb %al
; X64-NEXT: retq
;
; X86-LABEL: subcarry_ult_2x128:
>From 76f0d9f664a2fb90825dafe597e667959c2a8b45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Wed, 29 Apr 2026 11:45:43 +0200
Subject: [PATCH 3/5] [DAGCombiner] Use getLegalTypeToTransformTo for
USUBO_CARRY legality check
getTypeToTransformTo performs only a single step in the type-legalization
ladder and can return an intermediate illegal type. For example:
- i128 on x86-32: getTypeToTransformTo returns i64, which is not legal
for USUBO_CARRY on x86-32 (the loop in X86ISelLowering skips i64 for
non-64-bit targets), so the combine was silently skipped.
- i256 on x86-64: getTypeToTransformTo returns i128, which is also not
directly legal; only i64 is.
getLegalTypeToTransformTo walks until a fixed point (legal type):
- i128 on x86-32: i128 -> i64 -> i32 (legal, USUBO_CARRY Custom)
- i256 on x86-64: i256 -> i128 -> i64 (legal, USUBO_CARRY Custom)
- i256 on x86-32: i256 -> i128 -> i64 -> i32 (legal)
After the fix, combineOrOfSetCCToUSUBOCarry fires for i128 on x86-32 and
for i256 on all targets, producing a single optimal SBB carry chain instead
of separate comparisons joined by xorl/sete/orb bit-manipulation.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 6 +-
llvm/test/CodeGen/X86/subcarry.ll | 118 ++++++++++++------
2 files changed, 81 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index bf0dbf15c2610..b180256857c2e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -3922,12 +3922,10 @@ static SDValue combineOrOfSetCCToUSUBOCarry(SDNode *N, SelectionDAG &DAG,
m_Value(CarryIn)))))
return SDValue();
- // Require USUBO_CARRY on the post-legalization type; for oversize integers
- // (e.g. i128) type legalization then expands one USUBO_CARRY into a chain
- // of register-width USUBO_CARRYs, which is exactly what we want.
EVT IntVT = A.getValueType();
if (!TLI.isOperationLegalOrCustom(
- ISD::USUBO_CARRY, TLI.getTypeToTransformTo(*DAG.getContext(), IntVT)))
+ ISD::USUBO_CARRY,
+ TLI.getLegalTypeToTransformTo(*DAG.getContext(), IntVT)))
return SDValue();
SDLoc DL(N);
diff --git a/llvm/test/CodeGen/X86/subcarry.ll b/llvm/test/CodeGen/X86/subcarry.ll
index 8c359074f99da..c9b6a008eaf6b 100644
--- a/llvm/test/CodeGen/X86/subcarry.ll
+++ b/llvm/test/CodeGen/X86/subcarry.ll
@@ -1435,47 +1435,26 @@ define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
; X86: # %bb.0:
; X86-NEXT: pushl %ebp
; X86-NEXT: movl %esp, %ebp
-; X86-NEXT: pushl %ebx
-; X86-NEXT: pushl %edi
-; X86-NEXT: pushl %esi
; X86-NEXT: andl $-16, %esp
; X86-NEXT: subl $16, %esp
-; X86-NEXT: movl 24(%ebp), %esi
-; X86-NEXT: movl 28(%ebp), %ebx
-; X86-NEXT: movl 8(%ebp), %edx
-; X86-NEXT: cmpl 40(%ebp), %edx
-; X86-NEXT: movl 12(%ebp), %edx
-; X86-NEXT: sbbl 44(%ebp), %edx
-; X86-NEXT: movl 16(%ebp), %edx
-; X86-NEXT: sbbl 48(%ebp), %edx
-; X86-NEXT: movl 20(%ebp), %edx
-; X86-NEXT: sbbl 52(%ebp), %edx
-; X86-NEXT: setb {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Folded Spill
-; X86-NEXT: cmpl 56(%ebp), %esi
-; X86-NEXT: movl %ebx, %edx
-; X86-NEXT: sbbl 60(%ebp), %edx
-; X86-NEXT: movl 32(%ebp), %edx
-; X86-NEXT: movl %edx, %edi
-; X86-NEXT: sbbl 64(%ebp), %edi
-; X86-NEXT: movl 68(%ebp), %edi
-; X86-NEXT: movl 36(%ebp), %ecx
-; X86-NEXT: movl %ecx, %eax
-; X86-NEXT: sbbl %edi, %eax
-; X86-NEXT: setb %ah
-; X86-NEXT: xorl %edi, %ecx
-; X86-NEXT: xorl 60(%ebp), %ebx
-; X86-NEXT: orl %ecx, %ebx
-; X86-NEXT: xorl 64(%ebp), %edx
-; X86-NEXT: xorl 56(%ebp), %esi
-; X86-NEXT: orl %edx, %esi
-; X86-NEXT: orl %ebx, %esi
-; X86-NEXT: sete %al
-; X86-NEXT: andb {{[-0-9]+}}(%e{{[sb]}}p), %al # 1-byte Folded Reload
-; X86-NEXT: orb %ah, %al
-; X86-NEXT: leal -12(%ebp), %esp
-; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
-; X86-NEXT: popl %ebx
+; X86-NEXT: movl 8(%ebp), %eax
+; X86-NEXT: cmpl 40(%ebp), %eax
+; X86-NEXT: movl 12(%ebp), %eax
+; X86-NEXT: sbbl 44(%ebp), %eax
+; X86-NEXT: movl 16(%ebp), %eax
+; X86-NEXT: sbbl 48(%ebp), %eax
+; X86-NEXT: movl 20(%ebp), %eax
+; X86-NEXT: sbbl 52(%ebp), %eax
+; X86-NEXT: movl 24(%ebp), %eax
+; X86-NEXT: sbbl 56(%ebp), %eax
+; X86-NEXT: movl 28(%ebp), %eax
+; X86-NEXT: sbbl 60(%ebp), %eax
+; X86-NEXT: movl 32(%ebp), %eax
+; X86-NEXT: sbbl 64(%ebp), %eax
+; X86-NEXT: movl 36(%ebp), %eax
+; X86-NEXT: sbbl 68(%ebp), %eax
+; X86-NEXT: setb %al
+; X86-NEXT: movl %ebp, %esp
; X86-NEXT: popl %ebp
; X86-NEXT: retl
%b0 = icmp ult i128 %x0, %y0
@@ -1485,5 +1464,66 @@ define i1 @subcarry_ult_2x128(i128 %x0, i128 %x1, i128 %y0, i128 %y1) nounwind {
%br = or i1 %b1, %bp
ret i1 %br
}
+
+; i256 borrow chain — exercises the non-simple EVT path (i256->i128->i64/i32).
+define i1 @subcarry_ult_2x256(i256 %x0, i256 %x1, i256 %y0, i256 %y1) nounwind {
+; X64-LABEL: subcarry_ult_2x256:
+; X64: # %bb.0:
+; X64-NEXT: movq 16(%rsp), %rax
+; X64-NEXT: movq 8(%rsp), %r10
+; X64-NEXT: cmpq 24(%rsp), %rdi
+; X64-NEXT: sbbq 32(%rsp), %rsi
+; X64-NEXT: sbbq 40(%rsp), %rdx
+; X64-NEXT: sbbq 48(%rsp), %rcx
+; X64-NEXT: sbbq 56(%rsp), %r8
+; X64-NEXT: sbbq 64(%rsp), %r9
+; X64-NEXT: sbbq 72(%rsp), %r10
+; X64-NEXT: sbbq 80(%rsp), %rax
+; X64-NEXT: setb %al
+; X64-NEXT: retq
+;
+; X86-LABEL: subcarry_ult_2x256:
+; X86: # %bb.0:
+; X86-NEXT: movl 4(%esp), %eax
+; X86-NEXT: cmpl 68(%esp), %eax
+; X86-NEXT: movl 8(%esp), %eax
+; X86-NEXT: sbbl 72(%esp), %eax
+; X86-NEXT: movl 12(%esp), %eax
+; X86-NEXT: sbbl 76(%esp), %eax
+; X86-NEXT: movl 16(%esp), %eax
+; X86-NEXT: sbbl 80(%esp), %eax
+; X86-NEXT: movl 20(%esp), %eax
+; X86-NEXT: sbbl 84(%esp), %eax
+; X86-NEXT: movl 24(%esp), %eax
+; X86-NEXT: sbbl 88(%esp), %eax
+; X86-NEXT: movl 28(%esp), %eax
+; X86-NEXT: sbbl 92(%esp), %eax
+; X86-NEXT: movl 32(%esp), %eax
+; X86-NEXT: sbbl 96(%esp), %eax
+; X86-NEXT: movl 36(%esp), %eax
+; X86-NEXT: sbbl 100(%esp), %eax
+; X86-NEXT: movl 40(%esp), %eax
+; X86-NEXT: sbbl 104(%esp), %eax
+; X86-NEXT: movl 44(%esp), %eax
+; X86-NEXT: sbbl 108(%esp), %eax
+; X86-NEXT: movl 48(%esp), %eax
+; X86-NEXT: sbbl 112(%esp), %eax
+; X86-NEXT: movl 52(%esp), %eax
+; X86-NEXT: sbbl 116(%esp), %eax
+; X86-NEXT: movl 56(%esp), %eax
+; X86-NEXT: sbbl 120(%esp), %eax
+; X86-NEXT: movl 60(%esp), %eax
+; X86-NEXT: sbbl 124(%esp), %eax
+; X86-NEXT: movl 64(%esp), %eax
+; X86-NEXT: sbbl 128(%esp), %eax
+; X86-NEXT: setb %al
+; X86-NEXT: retl
+ %b0 = icmp ult i256 %x0, %y0
+ %b1 = icmp ult i256 %x1, %y1
+ %e1 = icmp eq i256 %x1, %y1
+ %bp = and i1 %b0, %e1
+ %br = or i1 %b1, %bp
+ ret i1 %br
+}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
>From dd0b4c773fbc89386af51ae40910347f5b2ac0f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Wed, 29 Apr 2026 15:24:14 +0200
Subject: [PATCH 4/5] [DAGCombiner] Add vector negative test for borrow chain
combine (NFC)
USUBO_CARRY is not defined for vector types, so the borrow chain pattern
is not recognized for vectors.
---
llvm/test/CodeGen/AArch64/cgp-usubo.ll | 29 +++++++++++++++++++++-----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/cgp-usubo.ll b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
index be8aa3eb82075..f990920e2793a 100644
--- a/llvm/test/CodeGen/AArch64/cgp-usubo.ll
+++ b/llvm/test/CodeGen/AArch64/cgp-usubo.ll
@@ -227,6 +227,25 @@ define i1 @no_subcarry_signed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
ret i1 %br
}
+; Negative test: vector types are not a borrow chain.
+define <4 x i1> @no_subcarry_vector(<4 x i32> %x0, <4 x i32> %x1, <4 x i32> %y0, <4 x i32> %y1) nounwind {
+; CHECK-LABEL: no_subcarry_vector:
+; CHECK: // %bb.0:
+; CHECK-NEXT: cmeq v4.4s, v1.4s, v3.4s
+; CHECK-NEXT: cmhi v0.4s, v2.4s, v0.4s
+; CHECK-NEXT: cmhi v1.4s, v3.4s, v1.4s
+; CHECK-NEXT: and v0.16b, v0.16b, v4.16b
+; CHECK-NEXT: orr v0.16b, v1.16b, v0.16b
+; CHECK-NEXT: xtn v0.4h, v0.4s
+; CHECK-NEXT: ret
+ %b0 = icmp ult <4 x i32> %x0, %y0
+ %b1 = icmp ult <4 x i32> %x1, %y1
+ %e1 = icmp eq <4 x i32> %x1, %y1
+ %bp = and <4 x i1> %b0, %e1
+ %br = or <4 x i1> %b1, %bp
+ ret <4 x i1> %br
+}
+
; Verify insertion point for multi-BB.
declare void @call(i1)
@@ -234,12 +253,12 @@ declare void @call(i1)
define i1 @usubo_ult_sub_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwind {
; CHECK-LABEL: usubo_ult_sub_dominates_i64:
; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: tbz w3, #0, .LBB13_2
+; CHECK-NEXT: tbz w3, #0, .LBB14_2
; CHECK-NEXT: // %bb.1: // %t
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: cset w3, lo
; CHECK-NEXT: str x8, [x2]
-; CHECK-NEXT: .LBB13_2: // %common.ret
+; CHECK-NEXT: .LBB14_2: // %common.ret
; CHECK-NEXT: and w0, w3, #0x1
; CHECK-NEXT: ret
entry:
@@ -265,7 +284,7 @@ define i1 @usubo_ult_cmp_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwin
; CHECK-NEXT: stp x20, x19, [sp, #32] // 16-byte Folded Spill
; CHECK-NEXT: mov w19, w3
; CHECK-NEXT: stp x22, x21, [sp, #16] // 16-byte Folded Spill
-; CHECK-NEXT: tbz w3, #0, .LBB14_3
+; CHECK-NEXT: tbz w3, #0, .LBB15_3
; CHECK-NEXT: // %bb.1: // %t
; CHECK-NEXT: cmp x0, x1
; CHECK-NEXT: mov x22, x0
@@ -275,11 +294,11 @@ define i1 @usubo_ult_cmp_dominates_i64(i64 %x, i64 %y, ptr %p, i1 %cond) nounwin
; CHECK-NEXT: mov w0, w21
; CHECK-NEXT: bl call
; CHECK-NEXT: subs x8, x22, x23
-; CHECK-NEXT: b.hs .LBB14_3
+; CHECK-NEXT: b.hs .LBB15_3
; CHECK-NEXT: // %bb.2: // %end
; CHECK-NEXT: mov w19, w21
; CHECK-NEXT: str x8, [x20]
-; CHECK-NEXT: .LBB14_3: // %common.ret
+; CHECK-NEXT: .LBB15_3: // %common.ret
; CHECK-NEXT: and w0, w19, #0x1
; CHECK-NEXT: ldp x20, x19, [sp, #32] // 16-byte Folded Reload
; CHECK-NEXT: ldp x22, x21, [sp, #16] // 16-byte Folded Reload
>From cea52ed7b07901162c90900d2de3c3dc8dbe9bf9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Wed, 29 Apr 2026 15:29:18 +0200
Subject: [PATCH 5/5] [DAGCombiner] Guard borrow chain combine against vector
types
USUBO_CARRY is a scalar operation; the borrow chain pattern makes no
sense for vectors (no cross-element carry). Without the guard,
getLegalTypeToTransformTo could scalarize a vector type to a legal
scalar (e.g. v4i32->i32 on i686) causing isOperationLegalOrCustom to
incorrectly return true, then crash when trying to split USUBO_CARRY.
Add an AVX512 vector negative test to subcarry.ll.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 8 +--
llvm/test/CodeGen/X86/subcarry.ll | 50 +++++++++++++++++++
2 files changed, 55 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index b180256857c2e..e80a271d3370e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -3923,9 +3923,11 @@ static SDValue combineOrOfSetCCToUSUBOCarry(SDNode *N, SelectionDAG &DAG,
return SDValue();
EVT IntVT = A.getValueType();
- if (!TLI.isOperationLegalOrCustom(
- ISD::USUBO_CARRY,
- TLI.getLegalTypeToTransformTo(*DAG.getContext(), IntVT)))
+ // Skip vectors: USUBO_CARRY on a vector type has no legalization path and
+ // would crash.
+ if (IntVT.isVector() || !TLI.isOperationLegalOrCustom(
+ ISD::USUBO_CARRY, TLI.getLegalTypeToTransformTo(
+ *DAG.getContext(), IntVT)))
return SDValue();
SDLoc DL(N);
diff --git a/llvm/test/CodeGen/X86/subcarry.ll b/llvm/test/CodeGen/X86/subcarry.ll
index c9b6a008eaf6b..c618cc9efbfb6 100644
--- a/llvm/test/CodeGen/X86/subcarry.ll
+++ b/llvm/test/CodeGen/X86/subcarry.ll
@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown | FileCheck %s --check-prefixes=CHECK,X64
; RUN: llc < %s -mtriple=i686-unknown | FileCheck %s --check-prefixes=CHECK,X86
+; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+avx512f,+avx512vl,+avx512bw | FileCheck %s --check-prefixes=AVX512
define i128 @sub128(i128 %a, i128 %b) nounwind {
; X64-LABEL: sub128:
@@ -1525,5 +1526,54 @@ define i1 @subcarry_ult_2x256(i256 %x0, i256 %x1, i256 %y0, i256 %y1) nounwind {
%br = or i1 %b1, %bp
ret i1 %br
}
+; Negative test: vector types are not a borrow chain.
+define <4 x i1> @no_subcarry_vector(<4 x i32> %x0, <4 x i32> %x1, <4 x i32> %y0, <4 x i32> %y1) nounwind {
+; AVX512-LABEL: no_subcarry_vector:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpcmpltud %xmm2, %xmm0, %k1
+; AVX512-NEXT: vpcmpltud %xmm3, %xmm1, %k0
+; AVX512-NEXT: vpcmpeqd %xmm3, %xmm1, %k1 {%k1}
+; AVX512-NEXT: korw %k1, %k0, %k1
+; AVX512-NEXT: vpcmpeqd %xmm0, %xmm0, %xmm0
+; AVX512-NEXT: vmovdqa32 %xmm0, %xmm0 {%k1} {z}
+; AVX512-NEXT: retq
+;
+; X86-LABEL: no_subcarry_vector:
+; X86: # %bb.0:
+; X86-NEXT: movl 16(%esp), %ecx
+; X86-NEXT: movl 20(%esp), %eax
+; X86-NEXT: cmpl 52(%esp), %eax
+; X86-NEXT: movl 36(%esp), %eax
+; X86-NEXT: sbbl 68(%esp), %eax
+; X86-NEXT: setb %al
+; X86-NEXT: addb %al, %al
+; X86-NEXT: cmpl 48(%esp), %ecx
+; X86-NEXT: movl 32(%esp), %ecx
+; X86-NEXT: sbbl 64(%esp), %ecx
+; X86-NEXT: movl 12(%esp), %ecx
+; X86-NEXT: adcb $0, %al
+; X86-NEXT: shlb $2, %al
+; X86-NEXT: cmpl 44(%esp), %ecx
+; X86-NEXT: movl 28(%esp), %ecx
+; X86-NEXT: sbbl 60(%esp), %ecx
+; X86-NEXT: movl 8(%esp), %ecx
+; X86-NEXT: setb %dl
+; X86-NEXT: addb %dl, %dl
+; X86-NEXT: cmpl 40(%esp), %ecx
+; X86-NEXT: movl 24(%esp), %ecx
+; X86-NEXT: sbbl 56(%esp), %ecx
+; X86-NEXT: adcb %al, %dl
+; X86-NEXT: movl 4(%esp), %eax
+; X86-NEXT: andb $15, %dl
+; X86-NEXT: movb %dl, (%eax)
+; X86-NEXT: retl $4
+ %b0 = icmp ult <4 x i32> %x0, %y0
+ %b1 = icmp ult <4 x i32> %x1, %y1
+ %e1 = icmp eq <4 x i32> %x1, %y1
+ %bp = and <4 x i1> %b0, %e1
+ %br = or <4 x i1> %b1, %bp
+ ret <4 x i1> %br
+}
+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
More information about the llvm-commits
mailing list