[llvm] [X86] Merge BT with a matching BTR/BTS/BTC (PR #193612)
Paweł Bylica via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 2 06:05:44 PDT 2026
https://github.com/chfast updated https://github.com/llvm/llvm-project/pull/193612
>From 6118734066cced6035a0fa8e806b1bc4b25474e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 19:17:06 +0200
Subject: [PATCH 01/13] [X86][test] Pre-commit test for combineBT with
mismatched bit positions
Pre-commit test for the upcoming BT-fuse-with-BTR/BTS/BTC combine: when
Src is shared between BT and a sibling bit-modify but the bit positions
differ, the combine must NOT fuse them. CHECKs are stable across the
upcoming impl change (the fold doesn't fire here either way); landing
the test first locks in the boundary.
---
.../CodeGen/X86/bt-merge-mismatched-bitpos.ll | 67 +++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
diff --git a/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll b/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
new file mode 100644
index 0000000000000..4936167ed6349
--- /dev/null
+++ b/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
@@ -0,0 +1,67 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s
+
+; combineBT only fuses BT with BTR/BTS/BTC when the bit positions match
+; (modulo trunc/zext/any-ext/and-with-mask). When the bit positions differ,
+; both instructions must remain in the output.
+
+define i1 @no_fuse_diff_pos_xor(ptr %word, i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_xor:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: btcl %edx, %ecx
+; CHECK-NEXT: btl %esi, %eax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movl %ecx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i32 1, %a
+ %bitb = shl i32 1, %b
+ %ld = load i32, ptr %word
+ %res = xor i32 %ld, %bitb
+ %test = and i32 %ld, %bita
+ %cmp = icmp ne i32 %test, 0
+ store i32 %res, ptr %word
+ ret i1 %cmp
+}
+
+define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_or:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btsq %rdx, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i64 1, %a
+ %bitb = shl i64 1, %b
+ %ld = load i64, ptr %word
+ %res = or i64 %ld, %bitb
+ %test = and i64 %ld, %bita
+ %cmp = icmp ne i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
+
+define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_and:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btrq %rdx, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setae %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i64 1, %a
+ %bitb = shl i64 1, %b
+ %notb = xor i64 %bitb, -1
+ %ld = load i64, ptr %word
+ %res = and i64 %ld, %notb
+ %test = and i64 %ld, %bita
+ %cmp = icmp eq i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
>From 418f0292f50b81e368939c7290335eeb09d841e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 19:17:07 +0200
Subject: [PATCH 02/13] [X86][test] Pre-commit test for combineBT
BT/BTR-BTS-BTC fuse
Pre-commit test for the upcoming BT-fuse-with-BTR/BTS/BTC combine. The
CHECKs here show the redundant bt instruction the upcoming combine will
remove (paired with btc/bts/btr); the next commit updates them.
Three peek-through paths are covered:
- (and %p, BW-1) only on the modify side
- (and %p, BW-1) only on the BT side
- ZERO_EXTEND wrapper on the bit position
---
llvm/test/CodeGen/X86/bt-merge-fuse.ll | 72 ++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/bt-merge-fuse.ll
diff --git a/llvm/test/CodeGen/X86/bt-merge-fuse.ll b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
new file mode 100644
index 0000000000000..ea23221940ee7
--- /dev/null
+++ b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
@@ -0,0 +1,72 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s
+
+; Pin the peek-through paths in combineBTToBitOpFlag: the bit positions of
+; BT and the bit-modify can differ by an (and x, BW-1) mask or by trunc/zext
+; wrappers, and the combine should still fuse them into a single BTR/BTS/BTC.
+
+; (and x, 31) on the modify side, bare on the BT side.
+define i1 @fuse_and_mask_on_modify(ptr %word, i32 %position) nounwind {
+; CHECK-LABEL: fuse_and_mask_on_modify:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: btcl %esi, %ecx
+; CHECK-NEXT: btl %esi, %eax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movl %ecx, (%rdi)
+; CHECK-NEXT: retq
+ %ofs = and i32 %position, 31
+ %bit = shl i32 1, %ofs
+ %ld = load i32, ptr %word
+ %res = xor i32 %ld, %bit
+ %test = and i32 %ld, %bit
+ %cmp = icmp ne i32 %test, 0
+ store i32 %res, ptr %word
+ ret i1 %cmp
+}
+
+; (and x, 63) on the BT side, bare on the modify side.
+define i1 @fuse_and_mask_on_bt(ptr %word, i64 %position) nounwind {
+; CHECK-LABEL: fuse_and_mask_on_bt:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btsq %rsi, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %bit = shl i64 1, %position
+ %ld = load i64, ptr %word
+ %res = or i64 %ld, %bit
+ %ofs = and i64 %position, 63
+ %tbit = shl i64 1, %ofs
+ %test = and i64 %ld, %tbit
+ %cmp = icmp ne i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
+
+; ZERO_EXTEND wrapper on the bit position.
+define i1 @fuse_zext_pos(ptr %word, i32 %position) nounwind {
+; CHECK-LABEL: fuse_zext_pos:
+; CHECK: # %bb.0:
+; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btrq %rsi, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setae %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %zext = zext i32 %position to i64
+ %bit = shl i64 1, %zext
+ %mask = xor i64 %bit, -1
+ %ld = load i64, ptr %word
+ %res = and i64 %ld, %mask
+ %test = and i64 %ld, %bit
+ %cmp = icmp eq i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
>From b0adbc11e51e2f7d634dc42c1142b988be4c91aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 23 Apr 2026 00:22:07 +0200
Subject: [PATCH 03/13] [X86] Merge BT with a matching BTR/BTS/BTC
BTR/BTS/BTC set CF from the pre-operation bit value, so a subsequent BT
on the same source and bit index produces redundant EFLAGS. We were
emitting both, e.g. for `(and ld, ~bit) | (and ld, bit) != 0` we got:
btrl %esi, %ecx
btl %esi, %eax
setae %al
Introduce three flag-producing DAG nodes X86ISD::BTR/BTS/BTC that model
the register-register BTR/BTS/BTC as `(res, EFLAGS) = op src, bitno`
(the atomic locked variants live under X86ISD::LBTR/LBTS/LBTC already),
pattern-match them to the existing BTR/BTS/BTC encodings, and add a DAG
combine in combineBT that fuses an X86ISD::BT with a sibling
AND(Src, rotl -2, X) / OR(Src, shl 1, X) / XOR(Src, shl 1, X) on the
same source into a single flag-producing node. The bit-position operand
can differ from BT's by trunc/zext/and-with-mask that preserves the low
log2(BW) bits (BT already masks those implicitly), so peek through
those wrappers when matching.
After the fix the example lowers to:
btrl %esi, %ecx
setae %al
Fixes llvm#165291.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 108 +++++++
llvm/lib/Target/X86/X86InstrCompiler.td | 17 ++
llvm/lib/Target/X86/X86InstrFragments.td | 8 +
llvm/test/CodeGen/X86/bittest-big-integer.ll | 306 +++++++------------
llvm/test/CodeGen/X86/bt-merge-fuse.ll | 12 +-
5 files changed, 250 insertions(+), 201 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6c5e682a41542..b48fe3883ce02 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57150,6 +57150,111 @@ static SDValue combineAndnp(SDNode *N, SelectionDAG &DAG,
return SDValue();
}
+// Strip TRUNCATE/ZERO_EXTEND/ANY_EXTEND wrappers and `and x, C` where C
+// preserves the low log2(BW) bits; these are transparent to BT/BTR/BTS/BTC,
+// which implicitly mask the bit index to log2(BW) bits.
+static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
+ APInt LowBits =
+ APInt::getLowBitsSet(V.getScalarValueSizeInBits(), Log2_32(BW));
+ for (;;) {
+ unsigned Op = V.getOpcode();
+ if (Op == ISD::TRUNCATE || Op == ISD::ZERO_EXTEND ||
+ Op == ISD::ANY_EXTEND) {
+ V = V.getOperand(0);
+ LowBits = LowBits.zextOrTrunc(V.getScalarValueSizeInBits());
+ continue;
+ }
+ if (Op == ISD::AND) {
+ auto *C = dyn_cast<ConstantSDNode>(V.getOperand(1));
+ if (C && LowBits.isSubsetOf(C->getAPIntValue())) {
+ V = V.getOperand(0);
+ continue;
+ }
+ }
+ return V;
+ }
+}
+
+// Try to merge a (X86ISD::BT Src, BitNo) with a sibling bit-modifying op on
+// Src (AND(Src, rotl -2, X), OR(Src, shl 1, X), XOR(Src, shl 1, X)) into a
+// single flag-producing X86ISD::{BTR,BTS,BTC} node. Both BT and BTR/BTS/BTC
+// set CF from the pre-op bit value, so one instruction subsumes the other.
+// Fixes llvm#165291.
+static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
+ SDValue Src = N->getOperand(0);
+ SDValue BitNo = N->getOperand(1);
+ EVT VT = Src.getValueType();
+ SDLoc DL(N);
+
+ // BT is only emitted for legal integer widths (16/32/64); match those.
+ if (VT != MVT::i16 && VT != MVT::i32 && VT != MVT::i64)
+ return SDValue();
+
+ unsigned BW = VT.getScalarSizeInBits();
+ SDValue PeeledBitNo = peekThroughBitPosExtTrunc(BitNo, BW);
+
+ for (SDNode *User : Src->users()) {
+ if (User == N)
+ continue;
+ unsigned UOpc = User->getOpcode();
+ if (UOpc != ISD::AND && UOpc != ISD::OR && UOpc != ISD::XOR)
+ continue;
+ if (User->getValueType(0) != VT)
+ continue;
+
+ // Identify which operand of User is Src; the other is the mask.
+ SDValue UOp0 = User->getOperand(0);
+ SDValue UOp1 = User->getOperand(1);
+ SDValue Mask;
+ if (UOp0 == SDValue(Src.getNode(), Src.getResNo()))
+ Mask = UOp1;
+ else if (UOp1 == SDValue(Src.getNode(), Src.getResNo()))
+ Mask = UOp0;
+ else
+ continue;
+ // We will replace the mask's consumer (User); require the mask to have no
+ // other live uses so we can drop it.
+ if (!Mask.hasOneUse())
+ continue;
+
+ unsigned FlagOp = 0;
+ SDValue ShAmt;
+ if (UOpc == ISD::AND && Mask.getOpcode() == ISD::ROTL) {
+ // (and Src, (rotl -2, X)): clears bit X.
+ if (auto *C = dyn_cast<ConstantSDNode>(Mask.getOperand(0)))
+ if (C->getAPIntValue() == APInt::getAllOnes(BW) - 1) {
+ FlagOp = X86ISD::BTR;
+ ShAmt = Mask.getOperand(1);
+ }
+ } else if ((UOpc == ISD::OR || UOpc == ISD::XOR) &&
+ Mask.getOpcode() == ISD::SHL) {
+ // (or/xor Src, (shl 1, X)): sets/flips bit X.
+ if (auto *C = dyn_cast<ConstantSDNode>(Mask.getOperand(0)))
+ if (C->getAPIntValue() == 1) {
+ FlagOp = UOpc == ISD::OR ? X86ISD::BTS : X86ISD::BTC;
+ ShAmt = Mask.getOperand(1);
+ }
+ }
+ if (!FlagOp)
+ continue;
+
+ // The BT and the bit-op must address the same bit. They can differ only
+ // by truncation/extension or an AND that preserves the low log2(BW) bits.
+ if (peekThroughBitPosExtTrunc(ShAmt, BW) != PeeledBitNo)
+ continue;
+
+ // BTR/BTS/BTC *rr take the bit index in a register of the same width as
+ // the source. Extend or truncate to VT to match the instruction signature.
+ SDValue BN = DAG.getZExtOrTrunc(BitNo, DL, VT);
+ SDValue New = DAG.getNode(FlagOp, DL, DAG.getVTList(VT, MVT::i32), Src, BN);
+ // Reroute the value output through User's consumers.
+ DAG.ReplaceAllUsesOfValueWith(SDValue(User, 0), New.getValue(0));
+ // Return the flags output so combineBT installs it as N's replacement.
+ return New.getValue(1);
+ }
+ return SDValue();
+}
+
static SDValue combineBT(SDNode *N, SelectionDAG &DAG,
TargetLowering::DAGCombinerInfo &DCI) {
SDValue N1 = N->getOperand(1);
@@ -57163,6 +57268,9 @@ static SDValue combineBT(SDNode *N, SelectionDAG &DAG,
return SDValue(N, 0);
}
+ if (SDValue V = combineBTToBitOpFlag(N, DAG))
+ return V;
+
return SDValue();
}
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 6ab6f870f1bb8..0437227921174 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -2087,6 +2087,23 @@ defm : OneBitPats<GR16, i16, BTR16rr, BTS16rr, BTC16rr, shiftMask16>;
defm : OneBitPats<GR32, i32, BTR32rr, BTS32rr, BTC32rr, shiftMask32>;
defm : OneBitPats<GR64, i64, BTR64rr, BTS64rr, BTC64rr, shiftMask64>;
+// Flag-producing variants: reuse the BTR/BTS/BTC encodings so CF can replace
+// a separate X86ISD::BT. Emitted by a DAG combine in combineBT.
+multiclass OneBitFlagPats<RegisterClass rc, Instruction btr, Instruction bts,
+ Instruction btc, SDNode btr_flag, SDNode bts_flag,
+ SDNode btc_flag> {
+ def : Pat<(btr_flag rc:$src1, rc:$src2), (btr rc:$src1, rc:$src2)>;
+ def : Pat<(bts_flag rc:$src1, rc:$src2), (bts rc:$src1, rc:$src2)>;
+ def : Pat<(btc_flag rc:$src1, rc:$src2), (btc rc:$src1, rc:$src2)>;
+}
+
+defm : OneBitFlagPats<GR16, BTR16rr, BTS16rr, BTC16rr,
+ X86btr_flag, X86bts_flag, X86btc_flag>;
+defm : OneBitFlagPats<GR32, BTR32rr, BTS32rr, BTC32rr,
+ X86btr_flag, X86bts_flag, X86btc_flag>;
+defm : OneBitFlagPats<GR64, BTR64rr, BTS64rr, BTC64rr,
+ X86btr_flag, X86bts_flag, X86btc_flag>;
+
//===----------------------------------------------------------------------===//
// EFLAGS-defining Patterns
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index 69f19ce08d314..f40a615f4c8e0 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -165,6 +165,14 @@ let IsStrictFP = true in {
// X86 bit-test instructions.
def X86bt : SDNode<"X86ISD::BT", SDTX86CmpTest>;
+// X86 bit-test-and-modify instructions: res, EFLAGS = op src, bitno.
+// CF is set from the pre-operation bit value; BT on the same operands is
+// therefore redundant after one of these nodes. The atomic (locked) variants
+// live under X86ISD::LBTR/LBTS/LBTC.
+def X86btr_flag : SDNode<"X86ISD::BTR", SDTBinaryArithWithFlags>;
+def X86bts_flag : SDNode<"X86ISD::BTS", SDTBinaryArithWithFlags>;
+def X86btc_flag : SDNode<"X86ISD::BTC", SDTBinaryArithWithFlags>;
+
// Conditional compare instructions
def X86ccmp : SDNode<"X86ISD::CCMP", SDTX86Ccmp>;
def X86ctest : SDNode<"X86ISD::CTEST", SDTX86Ccmp>;
diff --git a/llvm/test/CodeGen/X86/bittest-big-integer.ll b/llvm/test/CodeGen/X86/bittest-big-integer.ll
index 96ccc7b0f7527..6767cc45a2c5e 100644
--- a/llvm/test/CodeGen/X86/bittest-big-integer.ll
+++ b/llvm/test/CodeGen/X86/bittest-big-integer.ll
@@ -38,24 +38,18 @@ define i1 @test_eq_i32(ptr %word, i32 %position) nounwind {
define i1 @complement_ne_i32(ptr %word, i32 %position) nounwind {
; X86-LABEL: complement_ne_i32:
; X86: # %bb.0:
-; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl (%ecx), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: btcl %eax, %esi
-; X86-NEXT: btl %eax, %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: btcl %eax, %edx
; X86-NEXT: setb %al
-; X86-NEXT: movl %esi, (%ecx)
-; X86-NEXT: popl %esi
+; X86-NEXT: movl %edx, (%ecx)
; X86-NEXT: retl
;
; X64-LABEL: complement_ne_i32:
; X64: # %bb.0:
-; X64-NEXT: movl (%rdi), %eax
-; X64-NEXT: movl %eax, %ecx
+; X64-NEXT: movl (%rdi), %ecx
; X64-NEXT: btcl %esi, %ecx
-; X64-NEXT: btl %esi, %eax
; X64-NEXT: setb %al
; X64-NEXT: movl %ecx, (%rdi)
; X64-NEXT: retq
@@ -72,24 +66,18 @@ define i1 @complement_ne_i32(ptr %word, i32 %position) nounwind {
define i1 @reset_eq_i32(ptr %word, i32 %position) nounwind {
; X86-LABEL: reset_eq_i32:
; X86: # %bb.0:
-; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl (%ecx), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: btrl %eax, %esi
-; X86-NEXT: btl %eax, %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: btrl %eax, %edx
; X86-NEXT: setae %al
-; X86-NEXT: movl %esi, (%ecx)
-; X86-NEXT: popl %esi
+; X86-NEXT: movl %edx, (%ecx)
; X86-NEXT: retl
;
; X64-LABEL: reset_eq_i32:
; X64: # %bb.0:
-; X64-NEXT: movl (%rdi), %eax
-; X64-NEXT: movl %eax, %ecx
+; X64-NEXT: movl (%rdi), %ecx
; X64-NEXT: btrl %esi, %ecx
-; X64-NEXT: btl %esi, %eax
; X64-NEXT: setae %al
; X64-NEXT: movl %ecx, (%rdi)
; X64-NEXT: retq
@@ -107,24 +95,18 @@ define i1 @reset_eq_i32(ptr %word, i32 %position) nounwind {
define i1 @set_ne_i32(ptr %word, i32 %position) nounwind {
; X86-LABEL: set_ne_i32:
; X86: # %bb.0:
-; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl (%ecx), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: btsl %eax, %esi
-; X86-NEXT: btl %eax, %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: btsl %eax, %edx
; X86-NEXT: setb %al
-; X86-NEXT: movl %esi, (%ecx)
-; X86-NEXT: popl %esi
+; X86-NEXT: movl %edx, (%ecx)
; X86-NEXT: retl
;
; X64-LABEL: set_ne_i32:
; X64: # %bb.0:
-; X64-NEXT: movl (%rdi), %eax
-; X64-NEXT: movl %eax, %ecx
+; X64-NEXT: movl (%rdi), %ecx
; X64-NEXT: btsl %esi, %ecx
-; X64-NEXT: btl %esi, %eax
; X64-NEXT: setb %al
; X64-NEXT: movl %ecx, (%rdi)
; X64-NEXT: retq
@@ -145,14 +127,12 @@ define i1 @init_eq_i32(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movzbl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: shll %cl, %eax
-; X86-NEXT: movl (%edx), %esi
-; X86-NEXT: movl %esi, %edi
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %esi
+; X86-NEXT: shll %cl, %esi
+; X86-NEXT: movl (%edx), %edi
; X86-NEXT: btrl %ecx, %edi
-; X86-NEXT: orl %eax, %edi
-; X86-NEXT: btl %ecx, %esi
; X86-NEXT: setae %al
+; X86-NEXT: orl %esi, %edi
; X86-NEXT: movl %edi, (%edx)
; X86-NEXT: popl %esi
; X86-NEXT: popl %edi
@@ -162,24 +142,20 @@ define i1 @init_eq_i32(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; SSE: # %bb.0:
; SSE-NEXT: movl %esi, %ecx
; SSE-NEXT: shll %cl, %edx
-; SSE-NEXT: movl (%rdi), %eax
-; SSE-NEXT: movl %eax, %esi
+; SSE-NEXT: movl (%rdi), %esi
; SSE-NEXT: btrl %ecx, %esi
-; SSE-NEXT: orl %edx, %esi
-; SSE-NEXT: btl %ecx, %eax
; SSE-NEXT: setae %al
+; SSE-NEXT: orl %edx, %esi
; SSE-NEXT: movl %esi, (%rdi)
; SSE-NEXT: retq
;
; AVX-LABEL: init_eq_i32:
; AVX: # %bb.0:
-; AVX-NEXT: shlxl %esi, %edx, %eax
-; AVX-NEXT: movl (%rdi), %ecx
-; AVX-NEXT: movl %ecx, %edx
+; AVX-NEXT: shlxl %esi, %edx, %ecx
+; AVX-NEXT: movl (%rdi), %edx
; AVX-NEXT: btrl %esi, %edx
-; AVX-NEXT: orl %eax, %edx
-; AVX-NEXT: btl %esi, %ecx
; AVX-NEXT: setae %al
+; AVX-NEXT: orl %ecx, %edx
; AVX-NEXT: movl %edx, (%rdi)
; AVX-NEXT: retq
%ofs = and i32 %position, 31
@@ -232,29 +208,24 @@ define i1 @test_ne_i64(ptr %word, i32 %position) nounwind {
define i1 @complement_ne_i64(ptr %word, i32 %position) nounwind {
; X86-LABEL: complement_ne_i64:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $32, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $32, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btcl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btcl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: complement_ne_i64:
; X64: # %bb.0:
; X64-NEXT: # kill: def $esi killed $esi def $rsi
-; X64-NEXT: movq (%rdi), %rax
-; X64-NEXT: movq %rax, %rcx
+; X64-NEXT: movq (%rdi), %rcx
; X64-NEXT: btcq %rsi, %rcx
-; X64-NEXT: btq %rsi, %rax
; X64-NEXT: setb %al
; X64-NEXT: movq %rcx, (%rdi)
; X64-NEXT: retq
@@ -272,29 +243,24 @@ define i1 @complement_ne_i64(ptr %word, i32 %position) nounwind {
define i1 @reset_eq_i64(ptr %word, i32 %position) nounwind {
; X86-LABEL: reset_eq_i64:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $32, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $32, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btrl %eax, %esi
; X86-NEXT: setae %al
-; X86-NEXT: btrl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: reset_eq_i64:
; X64: # %bb.0:
; X64-NEXT: # kill: def $esi killed $esi def $rsi
-; X64-NEXT: movq (%rdi), %rax
-; X64-NEXT: movq %rax, %rcx
+; X64-NEXT: movq (%rdi), %rcx
; X64-NEXT: btrq %rsi, %rcx
-; X64-NEXT: btq %rsi, %rax
; X64-NEXT: setae %al
; X64-NEXT: movq %rcx, (%rdi)
; X64-NEXT: retq
@@ -313,29 +279,24 @@ define i1 @reset_eq_i64(ptr %word, i32 %position) nounwind {
define i1 @set_ne_i64(ptr %word, i32 %position) nounwind {
; X86-LABEL: set_ne_i64:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $32, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $32, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btsl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btsl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: set_ne_i64:
; X64: # %bb.0:
; X64-NEXT: # kill: def $esi killed $esi def $rsi
-; X64-NEXT: movq (%rdi), %rax
-; X64-NEXT: movq %rax, %rcx
+; X64-NEXT: movq (%rdi), %rcx
; X64-NEXT: btsq %rsi, %rcx
-; X64-NEXT: btq %rsi, %rax
; X64-NEXT: setb %al
; X64-NEXT: movq %rcx, (%rdi)
; X64-NEXT: retq
@@ -362,9 +323,8 @@ define i1 @init_eq_i64(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; X86-NEXT: andl $32, %esi
; X86-NEXT: shrl $3, %esi
; X86-NEXT: movl (%edx,%esi), %edi
-; X86-NEXT: btl %ecx, %edi
-; X86-NEXT: setae %al
; X86-NEXT: btrl %ecx, %edi
+; X86-NEXT: setae %al
; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ebx
; X86-NEXT: # kill: def $cl killed $cl killed $ecx
; X86-NEXT: shll %cl, %ebx
@@ -378,14 +338,12 @@ define i1 @init_eq_i64(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; SSE-LABEL: init_eq_i64:
; SSE: # %bb.0:
; SSE-NEXT: movl %esi, %ecx
-; SSE-NEXT: movl %edx, %eax
-; SSE-NEXT: shlq %cl, %rax
-; SSE-NEXT: movq (%rdi), %rdx
-; SSE-NEXT: movq %rdx, %rsi
+; SSE-NEXT: movl %edx, %edx
+; SSE-NEXT: shlq %cl, %rdx
+; SSE-NEXT: movq (%rdi), %rsi
; SSE-NEXT: btrq %rcx, %rsi
-; SSE-NEXT: orq %rax, %rsi
-; SSE-NEXT: btq %rcx, %rdx
; SSE-NEXT: setae %al
+; SSE-NEXT: orq %rdx, %rsi
; SSE-NEXT: movq %rsi, (%rdi)
; SSE-NEXT: retq
;
@@ -393,13 +351,11 @@ define i1 @init_eq_i64(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; AVX: # %bb.0:
; AVX-NEXT: # kill: def $esi killed $esi def $rsi
; AVX-NEXT: movl %edx, %eax
-; AVX-NEXT: shlxq %rsi, %rax, %rax
-; AVX-NEXT: movq (%rdi), %rcx
-; AVX-NEXT: movq %rcx, %rdx
+; AVX-NEXT: shlxq %rsi, %rax, %rcx
+; AVX-NEXT: movq (%rdi), %rdx
; AVX-NEXT: btrq %rsi, %rdx
-; AVX-NEXT: orq %rax, %rdx
-; AVX-NEXT: btq %rsi, %rcx
; AVX-NEXT: setae %al
+; AVX-NEXT: orq %rcx, %rdx
; AVX-NEXT: movq %rdx, (%rdi)
; AVX-NEXT: retq
%rem = and i32 %position, 63
@@ -455,20 +411,17 @@ define i1 @test_ne_i128(ptr %word, i32 %position) nounwind {
define i1 @complement_ne_i128(ptr %word, i32 %position) nounwind {
; X86-LABEL: complement_ne_i128:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $96, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $96, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btcl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btcl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: complement_ne_i128:
@@ -477,9 +430,8 @@ define i1 @complement_ne_i128(ptr %word, i32 %position) nounwind {
; X64-NEXT: andl $96, %ecx
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setb %al
; X64-NEXT: btcl %esi, %edx
+; X64-NEXT: setb %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 127
@@ -496,20 +448,17 @@ define i1 @complement_ne_i128(ptr %word, i32 %position) nounwind {
define i1 @reset_eq_i128(ptr %word, i32 %position) nounwind {
; X86-LABEL: reset_eq_i128:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $96, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $96, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btrl %eax, %esi
; X86-NEXT: setae %al
-; X86-NEXT: btrl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: reset_eq_i128:
@@ -518,9 +467,8 @@ define i1 @reset_eq_i128(ptr %word, i32 %position) nounwind {
; X64-NEXT: andl $96, %ecx
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setae %al
; X64-NEXT: btrl %esi, %edx
+; X64-NEXT: setae %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 127
@@ -538,20 +486,17 @@ define i1 @reset_eq_i128(ptr %word, i32 %position) nounwind {
define i1 @set_ne_i128(ptr %word, i32 %position) nounwind {
; X86-LABEL: set_ne_i128:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: andl $96, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andl $96, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btsl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btsl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: set_ne_i128:
@@ -560,9 +505,8 @@ define i1 @set_ne_i128(ptr %word, i32 %position) nounwind {
; X64-NEXT: andl $96, %ecx
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setb %al
; X64-NEXT: btsl %esi, %edx
+; X64-NEXT: setb %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 127
@@ -588,9 +532,8 @@ define i1 @init_eq_i128(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; X86-NEXT: andl $96, %esi
; X86-NEXT: shrl $3, %esi
; X86-NEXT: movl (%edx,%esi), %edi
-; X86-NEXT: btl %ecx, %edi
-; X86-NEXT: setae %al
; X86-NEXT: btrl %ecx, %edi
+; X86-NEXT: setae %al
; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ebx
; X86-NEXT: # kill: def $cl killed $cl killed $ecx
; X86-NEXT: shll %cl, %ebx
@@ -607,10 +550,10 @@ define i1 @init_eq_i128(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; SSE-NEXT: andl $96, %esi
; SSE-NEXT: shrl $3, %esi
; SSE-NEXT: movl (%rdi,%rsi), %r8d
-; SSE-NEXT: btl %ecx, %r8d
+; SSE-NEXT: btrl %ecx, %r8d
; SSE-NEXT: setae %al
+; SSE-NEXT: # kill: def $cl killed $cl killed $ecx
; SSE-NEXT: shll %cl, %edx
-; SSE-NEXT: btrl %ecx, %r8d
; SSE-NEXT: orl %r8d, %edx
; SSE-NEXT: movl %edx, (%rdi,%rsi)
; SSE-NEXT: retq
@@ -621,9 +564,8 @@ define i1 @init_eq_i128(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; AVX-NEXT: andl $96, %ecx
; AVX-NEXT: shrl $3, %ecx
; AVX-NEXT: movl (%rdi,%rcx), %r8d
-; AVX-NEXT: btl %esi, %r8d
-; AVX-NEXT: setae %al
; AVX-NEXT: btrl %esi, %r8d
+; AVX-NEXT: setae %al
; AVX-NEXT: shlxl %esi, %edx, %edx
; AVX-NEXT: orl %r8d, %edx
; AVX-NEXT: movl %edx, (%rdi,%rcx)
@@ -679,20 +621,17 @@ define i1 @test_ne_i512(ptr %word, i32 %position) nounwind {
define i1 @complement_ne_i512(ptr %word, i32 %position) nounwind {
; X86-LABEL: complement_ne_i512:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: andl $60, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: andl $60, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btcl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btcl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: complement_ne_i512:
@@ -701,9 +640,8 @@ define i1 @complement_ne_i512(ptr %word, i32 %position) nounwind {
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: andl $60, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setb %al
; X64-NEXT: btcl %esi, %edx
+; X64-NEXT: setb %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 511
@@ -720,20 +658,17 @@ define i1 @complement_ne_i512(ptr %word, i32 %position) nounwind {
define i1 @reset_eq_i512(ptr %word, i32 %position) nounwind {
; X86-LABEL: reset_eq_i512:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: andl $60, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: andl $60, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btrl %eax, %esi
; X86-NEXT: setae %al
-; X86-NEXT: btrl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: reset_eq_i512:
@@ -742,9 +677,8 @@ define i1 @reset_eq_i512(ptr %word, i32 %position) nounwind {
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: andl $60, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setae %al
; X64-NEXT: btrl %esi, %edx
+; X64-NEXT: setae %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 511
@@ -762,20 +696,17 @@ define i1 @reset_eq_i512(ptr %word, i32 %position) nounwind {
define i1 @set_ne_i512(ptr %word, i32 %position) nounwind {
; X86-LABEL: set_ne_i512:
; X86: # %bb.0:
-; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %esi
-; X86-NEXT: shrl $3, %esi
-; X86-NEXT: andl $60, %esi
-; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: btl %edx, %edi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: shrl $3, %edx
+; X86-NEXT: andl $60, %edx
+; X86-NEXT: movl (%ecx,%edx), %esi
+; X86-NEXT: btsl %eax, %esi
; X86-NEXT: setb %al
-; X86-NEXT: btsl %edx, %edi
-; X86-NEXT: movl %edi, (%ecx,%esi)
+; X86-NEXT: movl %esi, (%ecx,%edx)
; X86-NEXT: popl %esi
-; X86-NEXT: popl %edi
; X86-NEXT: retl
;
; X64-LABEL: set_ne_i512:
@@ -784,9 +715,8 @@ define i1 @set_ne_i512(ptr %word, i32 %position) nounwind {
; X64-NEXT: shrl $3, %ecx
; X64-NEXT: andl $60, %ecx
; X64-NEXT: movl (%rdi,%rcx), %edx
-; X64-NEXT: btl %esi, %edx
-; X64-NEXT: setb %al
; X64-NEXT: btsl %esi, %edx
+; X64-NEXT: setb %al
; X64-NEXT: movl %edx, (%rdi,%rcx)
; X64-NEXT: retq
%rem = and i32 %position, 511
@@ -812,9 +742,8 @@ define i1 @init_eq_i512(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; X86-NEXT: shrl $3, %esi
; X86-NEXT: andl $60, %esi
; X86-NEXT: movl (%edx,%esi), %edi
-; X86-NEXT: btl %ecx, %edi
-; X86-NEXT: setae %al
; X86-NEXT: btrl %ecx, %edi
+; X86-NEXT: setae %al
; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ebx
; X86-NEXT: # kill: def $cl killed $cl killed $ecx
; X86-NEXT: shll %cl, %ebx
@@ -831,10 +760,10 @@ define i1 @init_eq_i512(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; SSE-NEXT: shrl $3, %esi
; SSE-NEXT: andl $60, %esi
; SSE-NEXT: movl (%rdi,%rsi), %r8d
-; SSE-NEXT: btl %ecx, %r8d
+; SSE-NEXT: btrl %ecx, %r8d
; SSE-NEXT: setae %al
+; SSE-NEXT: # kill: def $cl killed $cl killed $ecx
; SSE-NEXT: shll %cl, %edx
-; SSE-NEXT: btrl %ecx, %r8d
; SSE-NEXT: orl %r8d, %edx
; SSE-NEXT: movl %edx, (%rdi,%rsi)
; SSE-NEXT: retq
@@ -845,9 +774,8 @@ define i1 @init_eq_i512(ptr %word, i32 %position, i1 zeroext %value) nounwind {
; AVX-NEXT: shrl $3, %ecx
; AVX-NEXT: andl $60, %ecx
; AVX-NEXT: movl (%rdi,%rcx), %r8d
-; AVX-NEXT: btl %esi, %r8d
-; AVX-NEXT: setae %al
; AVX-NEXT: btrl %esi, %r8d
+; AVX-NEXT: setae %al
; AVX-NEXT: shlxl %esi, %edx, %edx
; AVX-NEXT: orl %r8d, %edx
; AVX-NEXT: movl %edx, (%rdi,%rcx)
@@ -1103,28 +1031,24 @@ define <8 x i16> @complement_ne_i128_bitcast(ptr %word, i32 %position) nounwind
define i32 @reset_multiload_i128(ptr %word, i32 %position, ptr %p) nounwind {
; X86-LABEL: reset_multiload_i128:
; X86: # %bb.0:
-; 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), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl (%eax), %eax
; X86-NEXT: movl %edx, %esi
; X86-NEXT: andl $96, %esi
; X86-NEXT: shrl $3, %esi
; X86-NEXT: movl (%ecx,%esi), %edi
-; X86-NEXT: movl %edi, %ebx
-; X86-NEXT: btrl %edx, %ebx
-; X86-NEXT: btl %edx, %edi
-; X86-NEXT: movl %ebx, (%ecx,%esi)
+; X86-NEXT: btrl %edx, %edi
+; X86-NEXT: movl %edi, (%ecx,%esi)
; X86-NEXT: jae .LBB23_2
; X86-NEXT: # %bb.1:
; X86-NEXT: xorl %eax, %eax
; X86-NEXT: .LBB23_2:
; X86-NEXT: popl %esi
; X86-NEXT: popl %edi
-; X86-NEXT: popl %ebx
; X86-NEXT: retl
;
; X64-LABEL: reset_multiload_i128:
@@ -1132,11 +1056,9 @@ define i32 @reset_multiload_i128(ptr %word, i32 %position, ptr %p) nounwind {
; X64-NEXT: movl %esi, %ecx
; X64-NEXT: andl $96, %ecx
; X64-NEXT: shrl $3, %ecx
-; X64-NEXT: movl (%rdi,%rcx), %r9d
-; X64-NEXT: movl %r9d, %r8d
-; X64-NEXT: btrl %esi, %r8d
+; X64-NEXT: movl (%rdi,%rcx), %r8d
; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: btl %esi, %r9d
+; X64-NEXT: btrl %esi, %r8d
; X64-NEXT: jb .LBB23_2
; X64-NEXT: # %bb.1:
; X64-NEXT: movl (%rdx), %eax
diff --git a/llvm/test/CodeGen/X86/bt-merge-fuse.ll b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
index ea23221940ee7..192931b023bfc 100644
--- a/llvm/test/CodeGen/X86/bt-merge-fuse.ll
+++ b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
@@ -9,10 +9,8 @@
define i1 @fuse_and_mask_on_modify(ptr %word, i32 %position) nounwind {
; CHECK-LABEL: fuse_and_mask_on_modify:
; CHECK: # %bb.0:
-; CHECK-NEXT: movl (%rdi), %eax
-; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: movl (%rdi), %ecx
; CHECK-NEXT: btcl %esi, %ecx
-; CHECK-NEXT: btl %esi, %eax
; CHECK-NEXT: setb %al
; CHECK-NEXT: movl %ecx, (%rdi)
; CHECK-NEXT: retq
@@ -30,10 +28,8 @@ define i1 @fuse_and_mask_on_modify(ptr %word, i32 %position) nounwind {
define i1 @fuse_and_mask_on_bt(ptr %word, i64 %position) nounwind {
; CHECK-LABEL: fuse_and_mask_on_bt:
; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: movq (%rdi), %rcx
; CHECK-NEXT: btsq %rsi, %rcx
-; CHECK-NEXT: btq %rsi, %rax
; CHECK-NEXT: setb %al
; CHECK-NEXT: movq %rcx, (%rdi)
; CHECK-NEXT: retq
@@ -53,10 +49,8 @@ define i1 @fuse_zext_pos(ptr %word, i32 %position) nounwind {
; CHECK-LABEL: fuse_zext_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: movq (%rdi), %rcx
; CHECK-NEXT: btrq %rsi, %rcx
-; CHECK-NEXT: btq %rsi, %rax
; CHECK-NEXT: setae %al
; CHECK-NEXT: movq %rcx, (%rdi)
; CHECK-NEXT: retq
>From 53f5d13efc4ba00104e5434301b604cfa186eb61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 13:22:42 +0200
Subject: [PATCH 04/13] [X86] Address review comments
- Drop _flag postfix from X86btr/X86bts/X86btc SDNode names.
- Drop "Fixes" tag from combineBTToBitOpFlag comment.
- Replace VT i16/i32/i64 early-return with assert; getBT promotes
smaller widths to i32 before creating X86ISD::BT, so only i32/i64
reach the combine.
- Use sd_match with m_BitwiseLogic + m_Specific(Src) + m_OneUse to
replace manual operand-finding, type-equality, and one-use checks.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 29 +++++++-----------------
llvm/lib/Target/X86/X86InstrCompiler.td | 24 ++++++++------------
llvm/lib/Target/X86/X86InstrFragments.td | 6 ++---
3 files changed, 21 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index b48fe3883ce02..24b18971196cd 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57179,16 +57179,17 @@ static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
// Src (AND(Src, rotl -2, X), OR(Src, shl 1, X), XOR(Src, shl 1, X)) into a
// single flag-producing X86ISD::{BTR,BTS,BTC} node. Both BT and BTR/BTS/BTC
// set CF from the pre-op bit value, so one instruction subsumes the other.
-// Fixes llvm#165291.
static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
+ using namespace SDPatternMatch;
SDValue Src = N->getOperand(0);
SDValue BitNo = N->getOperand(1);
EVT VT = Src.getValueType();
SDLoc DL(N);
- // BT is only emitted for legal integer widths (16/32/64); match those.
- if (VT != MVT::i16 && VT != MVT::i32 && VT != MVT::i64)
- return SDValue();
+ // X86ISD::BT is only emitted for i32/i64 (smaller widths are promoted in
+ // getBT before the node is created).
+ assert((VT == MVT::i32 || VT == MVT::i64) &&
+ "X86ISD::BT is only emitted for i32/i64");
unsigned BW = VT.getScalarSizeInBits();
SDValue PeeledBitNo = peekThroughBitPosExtTrunc(BitNo, BW);
@@ -57196,27 +57197,13 @@ static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
for (SDNode *User : Src->users()) {
if (User == N)
continue;
- unsigned UOpc = User->getOpcode();
- if (UOpc != ISD::AND && UOpc != ISD::OR && UOpc != ISD::XOR)
- continue;
- if (User->getValueType(0) != VT)
- continue;
- // Identify which operand of User is Src; the other is the mask.
- SDValue UOp0 = User->getOperand(0);
- SDValue UOp1 = User->getOperand(1);
SDValue Mask;
- if (UOp0 == SDValue(Src.getNode(), Src.getResNo()))
- Mask = UOp1;
- else if (UOp1 == SDValue(Src.getNode(), Src.getResNo()))
- Mask = UOp0;
- else
- continue;
- // We will replace the mask's consumer (User); require the mask to have no
- // other live uses so we can drop it.
- if (!Mask.hasOneUse())
+ if (!sd_match(User,
+ m_BitwiseLogic(m_Specific(Src), m_OneUse(m_Value(Mask)))))
continue;
+ unsigned UOpc = User->getOpcode();
unsigned FlagOp = 0;
SDValue ShAmt;
if (UOpc == ISD::AND && Mask.getOpcode() == ISD::ROTL) {
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 0437227921174..48fe41804663a 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -2089,20 +2089,16 @@ defm : OneBitPats<GR64, i64, BTR64rr, BTS64rr, BTC64rr, shiftMask64>;
// Flag-producing variants: reuse the BTR/BTS/BTC encodings so CF can replace
// a separate X86ISD::BT. Emitted by a DAG combine in combineBT.
-multiclass OneBitFlagPats<RegisterClass rc, Instruction btr, Instruction bts,
- Instruction btc, SDNode btr_flag, SDNode bts_flag,
- SDNode btc_flag> {
- def : Pat<(btr_flag rc:$src1, rc:$src2), (btr rc:$src1, rc:$src2)>;
- def : Pat<(bts_flag rc:$src1, rc:$src2), (bts rc:$src1, rc:$src2)>;
- def : Pat<(btc_flag rc:$src1, rc:$src2), (btc rc:$src1, rc:$src2)>;
-}
-
-defm : OneBitFlagPats<GR16, BTR16rr, BTS16rr, BTC16rr,
- X86btr_flag, X86bts_flag, X86btc_flag>;
-defm : OneBitFlagPats<GR32, BTR32rr, BTS32rr, BTC32rr,
- X86btr_flag, X86bts_flag, X86btc_flag>;
-defm : OneBitFlagPats<GR64, BTR64rr, BTS64rr, BTC64rr,
- X86btr_flag, X86bts_flag, X86btc_flag>;
+multiclass OneBitFlagPats<RegisterClass rc, Instruction btr_inst,
+ Instruction bts_inst, Instruction btc_inst> {
+ def : Pat<(X86btr rc:$src1, rc:$src2), (btr_inst rc:$src1, rc:$src2)>;
+ def : Pat<(X86bts rc:$src1, rc:$src2), (bts_inst rc:$src1, rc:$src2)>;
+ def : Pat<(X86btc rc:$src1, rc:$src2), (btc_inst rc:$src1, rc:$src2)>;
+}
+
+defm : OneBitFlagPats<GR16, BTR16rr, BTS16rr, BTC16rr>;
+defm : OneBitFlagPats<GR32, BTR32rr, BTS32rr, BTC32rr>;
+defm : OneBitFlagPats<GR64, BTR64rr, BTS64rr, BTC64rr>;
//===----------------------------------------------------------------------===//
// EFLAGS-defining Patterns
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index f40a615f4c8e0..521462f89a63c 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -169,9 +169,9 @@ def X86bt : SDNode<"X86ISD::BT", SDTX86CmpTest>;
// CF is set from the pre-operation bit value; BT on the same operands is
// therefore redundant after one of these nodes. The atomic (locked) variants
// live under X86ISD::LBTR/LBTS/LBTC.
-def X86btr_flag : SDNode<"X86ISD::BTR", SDTBinaryArithWithFlags>;
-def X86bts_flag : SDNode<"X86ISD::BTS", SDTBinaryArithWithFlags>;
-def X86btc_flag : SDNode<"X86ISD::BTC", SDTBinaryArithWithFlags>;
+def X86btr : SDNode<"X86ISD::BTR", SDTBinaryArithWithFlags>;
+def X86bts : SDNode<"X86ISD::BTS", SDTBinaryArithWithFlags>;
+def X86btc : SDNode<"X86ISD::BTC", SDTBinaryArithWithFlags>;
// Conditional compare instructions
def X86ccmp : SDNode<"X86ISD::CCMP", SDTX86Ccmp>;
>From 426757877cd4404c8bd1fa17d6ccc1aec1b6945d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 13:50:00 +0200
Subject: [PATCH 05/13] [X86] Tidy combineBTToBitOpFlag
- Drop the no-op getZExtOrTrunc on BT's bit index; BT's bit index is
constrained to be the same type as Src (SDTCisSameAs in SDTX86CmpTest)
so we can pass BitNo directly to BTR/BTS/BTC.
- Accept SIGN_EXTEND in peekThroughBitPosExtTrunc; like the existing
TRUNCATE/ZERO_EXTEND/ANY_EXTEND cases, it preserves the low log2(BW)
bits the bit-test cares about.
- Add a one-line comment on the AND-mask check direction.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 24b18971196cd..6fc152dd1c803 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57150,21 +57150,22 @@ static SDValue combineAndnp(SDNode *N, SelectionDAG &DAG,
return SDValue();
}
-// Strip TRUNCATE/ZERO_EXTEND/ANY_EXTEND wrappers and `and x, C` where C
-// preserves the low log2(BW) bits; these are transparent to BT/BTR/BTS/BTC,
-// which implicitly mask the bit index to log2(BW) bits.
+// Strip TRUNCATE/Z|S|ANY_EXTEND wrappers and `and x, C` where C preserves
+// the low log2(BW) bits; these are transparent to BT/BTR/BTS/BTC, which
+// implicitly mask the bit index to log2(BW) bits.
static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
APInt LowBits =
APInt::getLowBitsSet(V.getScalarValueSizeInBits(), Log2_32(BW));
for (;;) {
unsigned Op = V.getOpcode();
if (Op == ISD::TRUNCATE || Op == ISD::ZERO_EXTEND ||
- Op == ISD::ANY_EXTEND) {
+ Op == ISD::SIGN_EXTEND || Op == ISD::ANY_EXTEND) {
V = V.getOperand(0);
LowBits = LowBits.zextOrTrunc(V.getScalarValueSizeInBits());
continue;
}
if (Op == ISD::AND) {
+ // The mask must keep all bits the bit-test cares about set.
auto *C = dyn_cast<ConstantSDNode>(V.getOperand(1));
if (C && LowBits.isSubsetOf(C->getAPIntValue())) {
V = V.getOperand(0);
@@ -57230,10 +57231,11 @@ static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
if (peekThroughBitPosExtTrunc(ShAmt, BW) != PeeledBitNo)
continue;
- // BTR/BTS/BTC *rr take the bit index in a register of the same width as
- // the source. Extend or truncate to VT to match the instruction signature.
- SDValue BN = DAG.getZExtOrTrunc(BitNo, DL, VT);
- SDValue New = DAG.getNode(FlagOp, DL, DAG.getVTList(VT, MVT::i32), Src, BN);
+ // BT's bit index is constrained to Src's type, so we can reuse it as-is
+ // for BTR/BTS/BTC's bit index operand.
+ assert(BitNo.getValueType() == VT && "BT bit index must match Src type");
+ SDValue New =
+ DAG.getNode(FlagOp, DL, DAG.getVTList(VT, MVT::i32), Src, BitNo);
// Reroute the value output through User's consumers.
DAG.ReplaceAllUsesOfValueWith(SDValue(User, 0), New.getValue(0));
// Return the flags output so combineBT installs it as N's replacement.
>From 8bfaeddf897bc9d0764b4de6de5debac8eb619f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 15:54:17 +0200
Subject: [PATCH 06/13] [X86] Drop SIGN_EXTEND from peekThroughBitPosExtTrunc
Reverts the SIGN_EXTEND case added earlier in this branch. No constructed
test produces different codegen with vs. without it (the X86 lit suite
passes identically), and BT's BitNo and the bit-op's ShAmt are typically
the same SDValue when their original IR root is shared, so a structural
compare passes either way without stripping SIGN_EXTEND. Speculative
coverage with no regression test isn't worth keeping.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6fc152dd1c803..c8d80bb18658c 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57159,7 +57159,7 @@ static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
for (;;) {
unsigned Op = V.getOpcode();
if (Op == ISD::TRUNCATE || Op == ISD::ZERO_EXTEND ||
- Op == ISD::SIGN_EXTEND || Op == ISD::ANY_EXTEND) {
+ Op == ISD::ANY_EXTEND) {
V = V.getOperand(0);
LowBits = LowBits.zextOrTrunc(V.getScalarValueSizeInBits());
continue;
>From 81d4abd0705370af935c5f76dbd5c0ce93dca0a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 15:56:41 +0200
Subject: [PATCH 07/13] [X86] Drop dead i16 OneBitFlagPats; clarify SDNode
comment
i16 X86ISD::BT can never reach combineBTToBitOpFlag because getBT
promotes <32-bit Src to i32 before creating the BT node, so the
matching bit-modify (still on i16) and the BT (on i32) have different
Src types and m_Specific(Src) won't match. Verified via probe IR:
the i16 fold path does not fire. Drop the unused GR16 defm entry.
Also qualify the new SDNode header comment as "non-atomic" for
symmetry with the existing X86ISD::LBTR/LBTS/LBTC reference.
---
llvm/lib/Target/X86/X86InstrCompiler.td | 3 ++-
llvm/lib/Target/X86/X86InstrFragments.td | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 48fe41804663a..0f28f3e368167 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -2096,7 +2096,8 @@ multiclass OneBitFlagPats<RegisterClass rc, Instruction btr_inst,
def : Pat<(X86btc rc:$src1, rc:$src2), (btc_inst rc:$src1, rc:$src2)>;
}
-defm : OneBitFlagPats<GR16, BTR16rr, BTS16rr, BTC16rr>;
+// Only i32/i64 are relevant: getBT promotes <32-bit Src to i32 before
+// creating X86ISD::BT, so an i16 BT can never reach combineBTToBitOpFlag.
defm : OneBitFlagPats<GR32, BTR32rr, BTS32rr, BTC32rr>;
defm : OneBitFlagPats<GR64, BTR64rr, BTS64rr, BTC64rr>;
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index 521462f89a63c..9316360c5e02a 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -165,7 +165,7 @@ let IsStrictFP = true in {
// X86 bit-test instructions.
def X86bt : SDNode<"X86ISD::BT", SDTX86CmpTest>;
-// X86 bit-test-and-modify instructions: res, EFLAGS = op src, bitno.
+// X86 non-atomic bit-test-and-modify instructions: res, EFLAGS = op src, bitno.
// CF is set from the pre-operation bit value; BT on the same operands is
// therefore redundant after one of these nodes. The atomic (locked) variants
// live under X86ISD::LBTR/LBTS/LBTC.
>From 390518328f7b74c7e77f8fa8916d03b7546cc5c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 16:38:39 +0200
Subject: [PATCH 08/13] [X86] Inline OneBitFlagPats now that GR16 entry is gone
With only the GR32 and GR64 widths in scope, the multiclass abstraction
has only two instantiations and obscures more than it saves. Inline the
six Pat<> defs directly.
---
llvm/lib/Target/X86/X86InstrCompiler.td | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 0f28f3e368167..5e8e54da0dfc0 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -2088,18 +2088,14 @@ defm : OneBitPats<GR32, i32, BTR32rr, BTS32rr, BTC32rr, shiftMask32>;
defm : OneBitPats<GR64, i64, BTR64rr, BTS64rr, BTC64rr, shiftMask64>;
// Flag-producing variants: reuse the BTR/BTS/BTC encodings so CF can replace
-// a separate X86ISD::BT. Emitted by a DAG combine in combineBT.
-multiclass OneBitFlagPats<RegisterClass rc, Instruction btr_inst,
- Instruction bts_inst, Instruction btc_inst> {
- def : Pat<(X86btr rc:$src1, rc:$src2), (btr_inst rc:$src1, rc:$src2)>;
- def : Pat<(X86bts rc:$src1, rc:$src2), (bts_inst rc:$src1, rc:$src2)>;
- def : Pat<(X86btc rc:$src1, rc:$src2), (btc_inst rc:$src1, rc:$src2)>;
-}
-
-// Only i32/i64 are relevant: getBT promotes <32-bit Src to i32 before
-// creating X86ISD::BT, so an i16 BT can never reach combineBTToBitOpFlag.
-defm : OneBitFlagPats<GR32, BTR32rr, BTS32rr, BTC32rr>;
-defm : OneBitFlagPats<GR64, BTR64rr, BTS64rr, BTC64rr>;
+// a separate X86ISD::BT. Emitted by a DAG combine in combineBT. Only i32/i64
+// since getBT promotes <32-bit Src to i32 before creating X86ISD::BT.
+def : Pat<(X86btr GR32:$src1, GR32:$src2), (BTR32rr GR32:$src1, GR32:$src2)>;
+def : Pat<(X86bts GR32:$src1, GR32:$src2), (BTS32rr GR32:$src1, GR32:$src2)>;
+def : Pat<(X86btc GR32:$src1, GR32:$src2), (BTC32rr GR32:$src1, GR32:$src2)>;
+def : Pat<(X86btr GR64:$src1, GR64:$src2), (BTR64rr GR64:$src1, GR64:$src2)>;
+def : Pat<(X86bts GR64:$src1, GR64:$src2), (BTS64rr GR64:$src1, GR64:$src2)>;
+def : Pat<(X86btc GR64:$src1, GR64:$src2), (BTC64rr GR64:$src1, GR64:$src2)>;
//===----------------------------------------------------------------------===//
// EFLAGS-defining Patterns
>From 31c26cf14cf7a59888af89a37605a37dbd5a4404 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 7 May 2026 19:06:02 +0200
Subject: [PATCH 09/13] [X86] Polish combineBT fuse: assert and comment
- peekThroughBitPosExtTrunc: assert that the source width is >= log2(BW)
after peeling a TRUNCATE; in X86 the smallest int (i8) is 8 bits and
log2(BW) <= 6, so the LowBits zextOrTrunc cannot silently lose bits,
but the assert documents the invariant and makes the helper safer
to lift later.
- Restore the header comment that was lost when SIGN_EXTEND was dropped.
- Note in OneBitFlagPats why the new patterns differ from OneBitPats
(no GR8 sub-reg insertion: the bit index is already same-width).
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 8 +++++---
llvm/lib/Target/X86/X86InstrCompiler.td | 4 +++-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index c8d80bb18658c..07a2f56d5dd43 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57150,9 +57150,9 @@ static SDValue combineAndnp(SDNode *N, SelectionDAG &DAG,
return SDValue();
}
-// Strip TRUNCATE/Z|S|ANY_EXTEND wrappers and `and x, C` where C preserves
-// the low log2(BW) bits; these are transparent to BT/BTR/BTS/BTC, which
-// implicitly mask the bit index to log2(BW) bits.
+// Strip TRUNCATE/ZERO_EXTEND/ANY_EXTEND wrappers and `and x, C` where C
+// preserves the low log2(BW) bits; these are transparent to BT/BTR/BTS/BTC,
+// which implicitly mask the bit index to log2(BW) bits.
static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
APInt LowBits =
APInt::getLowBitsSet(V.getScalarValueSizeInBits(), Log2_32(BW));
@@ -57161,6 +57161,8 @@ static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
if (Op == ISD::TRUNCATE || Op == ISD::ZERO_EXTEND ||
Op == ISD::ANY_EXTEND) {
V = V.getOperand(0);
+ assert(V.getScalarValueSizeInBits() >= Log2_32(BW) &&
+ "low bits constraint must survive width adjustment");
LowBits = LowBits.zextOrTrunc(V.getScalarValueSizeInBits());
continue;
}
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 5e8e54da0dfc0..b228f4deb704e 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -2089,7 +2089,9 @@ defm : OneBitPats<GR64, i64, BTR64rr, BTS64rr, BTC64rr, shiftMask64>;
// Flag-producing variants: reuse the BTR/BTS/BTC encodings so CF can replace
// a separate X86ISD::BT. Emitted by a DAG combine in combineBT. Only i32/i64
-// since getBT promotes <32-bit Src to i32 before creating X86ISD::BT.
+// since getBT promotes <32-bit Src to i32 before creating X86ISD::BT. Unlike
+// the OneBitPats above, the bit index here is already a same-width register
+// (combineBTToBitOpFlag emits it that way), so no GR8 sub-reg insertion.
def : Pat<(X86btr GR32:$src1, GR32:$src2), (BTR32rr GR32:$src1, GR32:$src2)>;
def : Pat<(X86bts GR32:$src1, GR32:$src2), (BTS32rr GR32:$src1, GR32:$src2)>;
def : Pat<(X86btc GR32:$src1, GR32:$src2), (BTC32rr GR32:$src1, GR32:$src2)>;
>From d75dba1348f2f12af84c77978ef91f0b8731bb94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 14 May 2026 14:11:15 +0200
Subject: [PATCH 10/13] [X86] Fold bt-merge-mismatched-bitpos.ll into
bt-merge-fuse.ll
Move the three negative tests into the existing peek-through test file as
negative cases, per review feedback.
---
llvm/test/CodeGen/X86/bt-merge-fuse.ll | 64 ++++++++++++++++++
.../CodeGen/X86/bt-merge-mismatched-bitpos.ll | 67 -------------------
2 files changed, 64 insertions(+), 67 deletions(-)
delete mode 100644 llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
diff --git a/llvm/test/CodeGen/X86/bt-merge-fuse.ll b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
index 192931b023bfc..23ed1e28f7ce3 100644
--- a/llvm/test/CodeGen/X86/bt-merge-fuse.ll
+++ b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
@@ -64,3 +64,67 @@ define i1 @fuse_zext_pos(ptr %word, i32 %position) nounwind {
store i64 %res, ptr %word
ret i1 %cmp
}
+
+; Negative: bit positions differ, BTC and BT must both remain.
+define i1 @no_fuse_diff_pos_xor(ptr %word, i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_xor:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: btcl %edx, %ecx
+; CHECK-NEXT: btl %esi, %eax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movl %ecx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i32 1, %a
+ %bitb = shl i32 1, %b
+ %ld = load i32, ptr %word
+ %res = xor i32 %ld, %bitb
+ %test = and i32 %ld, %bita
+ %cmp = icmp ne i32 %test, 0
+ store i32 %res, ptr %word
+ ret i1 %cmp
+}
+
+; Negative: bit positions differ, BTS and BT must both remain.
+define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_or:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btsq %rdx, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i64 1, %a
+ %bitb = shl i64 1, %b
+ %ld = load i64, ptr %word
+ %res = or i64 %ld, %bitb
+ %test = and i64 %ld, %bita
+ %cmp = icmp ne i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
+
+; Negative: bit positions differ, BTR and BT must both remain.
+define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: no_fuse_diff_pos_and:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movq (%rdi), %rax
+; CHECK-NEXT: movq %rax, %rcx
+; CHECK-NEXT: btrq %rdx, %rcx
+; CHECK-NEXT: btq %rsi, %rax
+; CHECK-NEXT: setae %al
+; CHECK-NEXT: movq %rcx, (%rdi)
+; CHECK-NEXT: retq
+ %bita = shl i64 1, %a
+ %bitb = shl i64 1, %b
+ %notb = xor i64 %bitb, -1
+ %ld = load i64, ptr %word
+ %res = and i64 %ld, %notb
+ %test = and i64 %ld, %bita
+ %cmp = icmp eq i64 %test, 0
+ store i64 %res, ptr %word
+ ret i1 %cmp
+}
diff --git a/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll b/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
deleted file mode 100644
index 4936167ed6349..0000000000000
--- a/llvm/test/CodeGen/X86/bt-merge-mismatched-bitpos.ll
+++ /dev/null
@@ -1,67 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s
-
-; combineBT only fuses BT with BTR/BTS/BTC when the bit positions match
-; (modulo trunc/zext/any-ext/and-with-mask). When the bit positions differ,
-; both instructions must remain in the output.
-
-define i1 @no_fuse_diff_pos_xor(ptr %word, i32 %a, i32 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_xor:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movl (%rdi), %eax
-; CHECK-NEXT: movl %eax, %ecx
-; CHECK-NEXT: btcl %edx, %ecx
-; CHECK-NEXT: btl %esi, %eax
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movl %ecx, (%rdi)
-; CHECK-NEXT: retq
- %bita = shl i32 1, %a
- %bitb = shl i32 1, %b
- %ld = load i32, ptr %word
- %res = xor i32 %ld, %bitb
- %test = and i32 %ld, %bita
- %cmp = icmp ne i32 %test, 0
- store i32 %res, ptr %word
- ret i1 %cmp
-}
-
-define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_or:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
-; CHECK-NEXT: btsq %rdx, %rcx
-; CHECK-NEXT: btq %rsi, %rax
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
- %bita = shl i64 1, %a
- %bitb = shl i64 1, %b
- %ld = load i64, ptr %word
- %res = or i64 %ld, %bitb
- %test = and i64 %ld, %bita
- %cmp = icmp ne i64 %test, 0
- store i64 %res, ptr %word
- ret i1 %cmp
-}
-
-define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_and:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
-; CHECK-NEXT: btrq %rdx, %rcx
-; CHECK-NEXT: btq %rsi, %rax
-; CHECK-NEXT: setae %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
- %bita = shl i64 1, %a
- %bitb = shl i64 1, %b
- %notb = xor i64 %bitb, -1
- %ld = load i64, ptr %word
- %res = and i64 %ld, %notb
- %test = and i64 %ld, %bita
- %cmp = icmp eq i64 %test, 0
- store i64 %res, ptr %word
- ret i1 %cmp
-}
>From 529f881d9c5168c19620f1a1363ae677b17c0179 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Mon, 18 May 2026 15:28:12 +0200
Subject: [PATCH 11/13] [X86] Use sd_match per opcode in combineBTToBitOpFlag
Replace the hybrid (outer sd_match + manual Mask shape check) with one
sd_match per opcode that fully expresses the AND/OR/XOR + rotl/shl
pattern and extracts ShAmt directly.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 39 +++++++++++--------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 07a2f56d5dd43..a8c490be144e0 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57201,32 +57201,27 @@ static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
if (User == N)
continue;
- SDValue Mask;
- if (!sd_match(User,
- m_BitwiseLogic(m_Specific(Src), m_OneUse(m_Value(Mask)))))
- continue;
-
- unsigned UOpc = User->getOpcode();
unsigned FlagOp = 0;
SDValue ShAmt;
- if (UOpc == ISD::AND && Mask.getOpcode() == ISD::ROTL) {
+ if (sd_match(User,
+ m_And(m_Specific(Src),
+ m_OneUse(m_Rotl(m_SpecificInt(APInt::getAllOnes(BW) - 1),
+ m_Value(ShAmt)))))) {
// (and Src, (rotl -2, X)): clears bit X.
- if (auto *C = dyn_cast<ConstantSDNode>(Mask.getOperand(0)))
- if (C->getAPIntValue() == APInt::getAllOnes(BW) - 1) {
- FlagOp = X86ISD::BTR;
- ShAmt = Mask.getOperand(1);
- }
- } else if ((UOpc == ISD::OR || UOpc == ISD::XOR) &&
- Mask.getOpcode() == ISD::SHL) {
- // (or/xor Src, (shl 1, X)): sets/flips bit X.
- if (auto *C = dyn_cast<ConstantSDNode>(Mask.getOperand(0)))
- if (C->getAPIntValue() == 1) {
- FlagOp = UOpc == ISD::OR ? X86ISD::BTS : X86ISD::BTC;
- ShAmt = Mask.getOperand(1);
- }
- }
- if (!FlagOp)
+ FlagOp = X86ISD::BTR;
+ } else if (sd_match(User, m_Or(m_Specific(Src),
+ m_OneUse(m_Shl(m_SpecificInt(1),
+ m_Value(ShAmt)))))) {
+ // (or Src, (shl 1, X)): sets bit X.
+ FlagOp = X86ISD::BTS;
+ } else if (sd_match(User, m_Xor(m_Specific(Src),
+ m_OneUse(m_Shl(m_SpecificInt(1),
+ m_Value(ShAmt)))))) {
+ // (xor Src, (shl 1, X)): flips bit X.
+ FlagOp = X86ISD::BTC;
+ } else {
continue;
+ }
// The BT and the bit-op must address the same bit. They can differ only
// by truncation/extension or an AND that preserves the low log2(BW) bits.
>From 8fc0f92780ad327c17eb2c53fea34cfc1741fe49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Mon, 18 May 2026 15:41:04 +0200
Subject: [PATCH 12/13] [X86] combineBT: cover peek-through subset check;
clarify Src match
- Add a negative test where the modify side's bit position is masked
by 30 (low bit cleared). LowBits.isSubsetOf rejects the strip, so
the bit positions don't match and BTC/BT both remain.
- Note next to Src why peek-through isn't needed there (m_Specific
already requires SDValue identity).
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 3 +++
llvm/test/CodeGen/X86/bt-merge-fuse.ll | 25 +++++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index a8c490be144e0..ef7a43f0806a3 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57184,6 +57184,9 @@ static SDValue peekThroughBitPosExtTrunc(SDValue V, unsigned BW) {
// set CF from the pre-op bit value, so one instruction subsumes the other.
static SDValue combineBTToBitOpFlag(SDNode *N, SelectionDAG &DAG) {
using namespace SDPatternMatch;
+ // Src is the BT source; matching against it with m_Specific requires
+ // SDValue identity with the modify's operand, so no peek-through is needed
+ // here (only the bit *position* may differ by ext/trunc/and-mask).
SDValue Src = N->getOperand(0);
SDValue BitNo = N->getOperand(1);
EVT VT = Src.getValueType();
diff --git a/llvm/test/CodeGen/X86/bt-merge-fuse.ll b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
index 23ed1e28f7ce3..0861938373b03 100644
--- a/llvm/test/CodeGen/X86/bt-merge-fuse.ll
+++ b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
@@ -107,6 +107,31 @@ define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
ret i1 %cmp
}
+; Negative: mask on the modify side drops a low bit (bit 0 of and-30 is clear),
+; so peek-through must leave the masked position in place and BTC/BT remain.
+define i1 @no_fuse_partial_mask_on_modify(ptr %word, i32 %position) nounwind {
+; CHECK-LABEL: no_fuse_partial_mask_on_modify:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl %esi, %eax
+; CHECK-NEXT: andb $30, %al
+; CHECK-NEXT: movl (%rdi), %ecx
+; CHECK-NEXT: movl %ecx, %edx
+; CHECK-NEXT: btcl %eax, %edx
+; CHECK-NEXT: andl $30, %esi
+; CHECK-NEXT: btl %esi, %ecx
+; CHECK-NEXT: setb %al
+; CHECK-NEXT: movl %edx, (%rdi)
+; CHECK-NEXT: retq
+ %ofs = and i32 %position, 30
+ %bit = shl i32 1, %ofs
+ %ld = load i32, ptr %word
+ %res = xor i32 %ld, %bit
+ %test = and i32 %ld, %bit
+ %cmp = icmp ne i32 %test, 0
+ store i32 %res, ptr %word
+ ret i1 %cmp
+}
+
; Negative: bit positions differ, BTR and BT must both remain.
define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
; CHECK-LABEL: no_fuse_diff_pos_and:
>From c9aa7d3bc76dd46c9ac8d18cdf5492d53dcddf8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Tue, 2 Jun 2026 12:21:29 +0200
Subject: [PATCH 13/13] [X86][test] Add i686 (X86) coverage to bt-merge-fuse.ll
Per RKSimon's review on PR #193612: dual RUN for x86_64 and i686 with
X64/X86 prefixes. The fold is a no-op on i686 for i64 cases (BT/BTR/BTS/BTC
aren't 32-bit-friendly for 64-bit values), so the X86 lines pin that
behavior too.
---
llvm/test/CodeGen/X86/bt-merge-fuse.ll | 312 ++++++++++++++++++++-----
1 file changed, 250 insertions(+), 62 deletions(-)
diff --git a/llvm/test/CodeGen/X86/bt-merge-fuse.ll b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
index 0861938373b03..9b95df1fef783 100644
--- a/llvm/test/CodeGen/X86/bt-merge-fuse.ll
+++ b/llvm/test/CodeGen/X86/bt-merge-fuse.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s --check-prefixes=CHECK,X64
+; RUN: llc < %s -mtriple=i686-- | FileCheck %s --check-prefixes=CHECK,X86
; Pin the peek-through paths in combineBTToBitOpFlag: the bit positions of
; BT and the bit-modify can differ by an (and x, BW-1) mask or by trunc/zext
@@ -7,13 +8,23 @@
; (and x, 31) on the modify side, bare on the BT side.
define i1 @fuse_and_mask_on_modify(ptr %word, i32 %position) nounwind {
-; CHECK-LABEL: fuse_and_mask_on_modify:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movl (%rdi), %ecx
-; CHECK-NEXT: btcl %esi, %ecx
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movl %ecx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: fuse_and_mask_on_modify:
+; X64: # %bb.0:
+; X64-NEXT: movl (%rdi), %ecx
+; X64-NEXT: btcl %esi, %ecx
+; X64-NEXT: setb %al
+; X64-NEXT: movl %ecx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: fuse_and_mask_on_modify:
+; X86: # %bb.0:
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl (%ecx), %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: btcl %eax, %edx
+; X86-NEXT: setb %al
+; X86-NEXT: movl %edx, (%ecx)
+; X86-NEXT: retl
%ofs = and i32 %position, 31
%bit = shl i32 1, %ofs
%ld = load i32, ptr %word
@@ -26,13 +37,41 @@ define i1 @fuse_and_mask_on_modify(ptr %word, i32 %position) nounwind {
; (and x, 63) on the BT side, bare on the modify side.
define i1 @fuse_and_mask_on_bt(ptr %word, i64 %position) nounwind {
-; CHECK-LABEL: fuse_and_mask_on_bt:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rcx
-; CHECK-NEXT: btsq %rsi, %rcx
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: fuse_and_mask_on_bt:
+; X64: # %bb.0:
+; X64-NEXT: movq (%rdi), %rcx
+; X64-NEXT: btsq %rsi, %rcx
+; X64-NEXT: setb %al
+; X64-NEXT: movq %rcx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: fuse_and_mask_on_bt:
+; X86: # %bb.0:
+; X86-NEXT: pushl %edi
+; X86-NEXT: pushl %esi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $1, %esi
+; X86-NEXT: xorl %edi, %edi
+; X86-NEXT: shldl %cl, %esi, %edi
+; X86-NEXT: shll %cl, %esi
+; X86-NEXT: testb $32, %cl
+; X86-NEXT: je .LBB1_2
+; X86-NEXT: # %bb.1:
+; X86-NEXT: movl %esi, %edi
+; X86-NEXT: xorl %esi, %esi
+; X86-NEXT: .LBB1_2:
+; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: andl $32, %eax
+; X86-NEXT: shrl $3, %eax
+; X86-NEXT: movl (%edx,%eax), %eax
+; X86-NEXT: btl %ecx, %eax
+; X86-NEXT: setb %al
+; X86-NEXT: orl %esi, (%edx)
+; X86-NEXT: orl %edi, 4(%edx)
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: retl
%bit = shl i64 1, %position
%ld = load i64, ptr %word
%res = or i64 %ld, %bit
@@ -46,14 +85,53 @@ define i1 @fuse_and_mask_on_bt(ptr %word, i64 %position) nounwind {
; ZERO_EXTEND wrapper on the bit position.
define i1 @fuse_zext_pos(ptr %word, i32 %position) nounwind {
-; CHECK-LABEL: fuse_zext_pos:
-; CHECK: # %bb.0:
-; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
-; CHECK-NEXT: movq (%rdi), %rcx
-; CHECK-NEXT: btrq %rsi, %rcx
-; CHECK-NEXT: setae %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: fuse_zext_pos:
+; X64: # %bb.0:
+; X64-NEXT: # kill: def $esi killed $esi def $rsi
+; X64-NEXT: movq (%rdi), %rcx
+; X64-NEXT: btrq %rsi, %rcx
+; X64-NEXT: setae %al
+; X64-NEXT: movq %rcx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: fuse_zext_pos:
+; X86: # %bb.0:
+; X86-NEXT: pushl %ebp
+; 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), %edx
+; X86-NEXT: movl $1, %eax
+; X86-NEXT: xorl %esi, %esi
+; X86-NEXT: shldl %cl, %eax, %esi
+; X86-NEXT: shll %cl, %eax
+; X86-NEXT: testb $32, %cl
+; X86-NEXT: je .LBB2_2
+; X86-NEXT: # %bb.1:
+; X86-NEXT: movl %eax, %esi
+; X86-NEXT: movl $0, %eax
+; X86-NEXT: .LBB2_2:
+; X86-NEXT: notl %esi
+; X86-NEXT: notl %eax
+; X86-NEXT: movl (%edx), %ebx
+; X86-NEXT: movl 4(%edx), %edi
+; X86-NEXT: movl %edi, %ebp
+; X86-NEXT: jne .LBB2_4
+; X86-NEXT: # %bb.3:
+; X86-NEXT: movl %ebx, %ebp
+; X86-NEXT: .LBB2_4:
+; X86-NEXT: andl %esi, %edi
+; X86-NEXT: andl %eax, %ebx
+; X86-NEXT: btl %ecx, %ebp
+; X86-NEXT: setae %al
+; X86-NEXT: movl %ebx, (%edx)
+; X86-NEXT: movl %edi, 4(%edx)
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: popl %ebx
+; X86-NEXT: popl %ebp
+; X86-NEXT: retl
%zext = zext i32 %position to i64
%bit = shl i64 1, %zext
%mask = xor i64 %bit, -1
@@ -67,15 +145,30 @@ define i1 @fuse_zext_pos(ptr %word, i32 %position) nounwind {
; Negative: bit positions differ, BTC and BT must both remain.
define i1 @no_fuse_diff_pos_xor(ptr %word, i32 %a, i32 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_xor:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movl (%rdi), %eax
-; CHECK-NEXT: movl %eax, %ecx
-; CHECK-NEXT: btcl %edx, %ecx
-; CHECK-NEXT: btl %esi, %eax
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movl %ecx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: no_fuse_diff_pos_xor:
+; X64: # %bb.0:
+; X64-NEXT: movl (%rdi), %eax
+; X64-NEXT: movl %eax, %ecx
+; X64-NEXT: btcl %edx, %ecx
+; X64-NEXT: btl %esi, %eax
+; X64-NEXT: setb %al
+; X64-NEXT: movl %ecx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: no_fuse_diff_pos_xor:
+; X86: # %bb.0:
+; X86-NEXT: pushl %esi
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl (%ecx), %edx
+; X86-NEXT: movl %edx, %esi
+; X86-NEXT: btcl %eax, %esi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: btl %eax, %edx
+; X86-NEXT: setb %al
+; X86-NEXT: movl %esi, (%ecx)
+; X86-NEXT: popl %esi
+; X86-NEXT: retl
%bita = shl i32 1, %a
%bitb = shl i32 1, %b
%ld = load i32, ptr %word
@@ -88,15 +181,51 @@ define i1 @no_fuse_diff_pos_xor(ptr %word, i32 %a, i32 %b) nounwind {
; Negative: bit positions differ, BTS and BT must both remain.
define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_or:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
-; CHECK-NEXT: btsq %rdx, %rcx
-; CHECK-NEXT: btq %rsi, %rax
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: no_fuse_diff_pos_or:
+; X64: # %bb.0:
+; X64-NEXT: movq (%rdi), %rax
+; X64-NEXT: movq %rax, %rcx
+; X64-NEXT: btsq %rdx, %rcx
+; X64-NEXT: btq %rsi, %rax
+; X64-NEXT: setb %al
+; X64-NEXT: movq %rcx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: no_fuse_diff_pos_or:
+; X86: # %bb.0:
+; X86-NEXT: pushl %ebx
+; X86-NEXT: pushl %edi
+; X86-NEXT: pushl %esi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $1, %esi
+; X86-NEXT: xorl %edi, %edi
+; X86-NEXT: shldl %cl, %esi, %edi
+; X86-NEXT: shll %cl, %esi
+; X86-NEXT: testb $32, %cl
+; X86-NEXT: je .LBB4_2
+; X86-NEXT: # %bb.1:
+; X86-NEXT: movl %esi, %edi
+; X86-NEXT: xorl %esi, %esi
+; X86-NEXT: .LBB4_2:
+; X86-NEXT: movl (%edx), %ebx
+; X86-NEXT: movl 4(%edx), %ecx
+; X86-NEXT: orl %ecx, %edi
+; X86-NEXT: orl %ebx, %esi
+; X86-NEXT: testb $32, %al
+; X86-NEXT: jne .LBB4_4
+; X86-NEXT: # %bb.3:
+; X86-NEXT: movl %ebx, %ecx
+; X86-NEXT: .LBB4_4:
+; X86-NEXT: btl %eax, %ecx
+; X86-NEXT: setb %al
+; X86-NEXT: movl %esi, (%edx)
+; X86-NEXT: movl %edi, 4(%edx)
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: popl %ebx
+; X86-NEXT: retl
%bita = shl i64 1, %a
%bitb = shl i64 1, %b
%ld = load i64, ptr %word
@@ -110,18 +239,37 @@ define i1 @no_fuse_diff_pos_or(ptr %word, i64 %a, i64 %b) nounwind {
; Negative: mask on the modify side drops a low bit (bit 0 of and-30 is clear),
; so peek-through must leave the masked position in place and BTC/BT remain.
define i1 @no_fuse_partial_mask_on_modify(ptr %word, i32 %position) nounwind {
-; CHECK-LABEL: no_fuse_partial_mask_on_modify:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movl %esi, %eax
-; CHECK-NEXT: andb $30, %al
-; CHECK-NEXT: movl (%rdi), %ecx
-; CHECK-NEXT: movl %ecx, %edx
-; CHECK-NEXT: btcl %eax, %edx
-; CHECK-NEXT: andl $30, %esi
-; CHECK-NEXT: btl %esi, %ecx
-; CHECK-NEXT: setb %al
-; CHECK-NEXT: movl %edx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: no_fuse_partial_mask_on_modify:
+; X64: # %bb.0:
+; X64-NEXT: movl %esi, %eax
+; X64-NEXT: andb $30, %al
+; X64-NEXT: movl (%rdi), %ecx
+; X64-NEXT: movl %ecx, %edx
+; X64-NEXT: btcl %eax, %edx
+; X64-NEXT: andl $30, %esi
+; X64-NEXT: btl %esi, %ecx
+; X64-NEXT: setb %al
+; X64-NEXT: movl %edx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: no_fuse_partial_mask_on_modify:
+; X86: # %bb.0:
+; X86-NEXT: pushl %edi
+; X86-NEXT: pushl %esi
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl %eax, %edx
+; X86-NEXT: andb $30, %dl
+; X86-NEXT: movl (%ecx), %esi
+; X86-NEXT: movl %esi, %edi
+; X86-NEXT: btcl %edx, %edi
+; X86-NEXT: andl $30, %eax
+; X86-NEXT: btl %eax, %esi
+; X86-NEXT: setb %al
+; X86-NEXT: movl %edi, (%ecx)
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: retl
%ofs = and i32 %position, 30
%bit = shl i32 1, %ofs
%ld = load i32, ptr %word
@@ -134,15 +282,53 @@ define i1 @no_fuse_partial_mask_on_modify(ptr %word, i32 %position) nounwind {
; Negative: bit positions differ, BTR and BT must both remain.
define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
-; CHECK-LABEL: no_fuse_diff_pos_and:
-; CHECK: # %bb.0:
-; CHECK-NEXT: movq (%rdi), %rax
-; CHECK-NEXT: movq %rax, %rcx
-; CHECK-NEXT: btrq %rdx, %rcx
-; CHECK-NEXT: btq %rsi, %rax
-; CHECK-NEXT: setae %al
-; CHECK-NEXT: movq %rcx, (%rdi)
-; CHECK-NEXT: retq
+; X64-LABEL: no_fuse_diff_pos_and:
+; X64: # %bb.0:
+; X64-NEXT: movq (%rdi), %rax
+; X64-NEXT: movq %rax, %rcx
+; X64-NEXT: btrq %rdx, %rcx
+; X64-NEXT: btq %rsi, %rax
+; X64-NEXT: setae %al
+; X64-NEXT: movq %rcx, (%rdi)
+; X64-NEXT: retq
+;
+; X86-LABEL: no_fuse_diff_pos_and:
+; X86: # %bb.0:
+; X86-NEXT: pushl %ebx
+; X86-NEXT: pushl %edi
+; X86-NEXT: pushl %esi
+; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl $1, %esi
+; X86-NEXT: xorl %edi, %edi
+; X86-NEXT: shldl %cl, %esi, %edi
+; X86-NEXT: shll %cl, %esi
+; X86-NEXT: testb $32, %cl
+; X86-NEXT: je .LBB6_2
+; X86-NEXT: # %bb.1:
+; X86-NEXT: movl %esi, %edi
+; X86-NEXT: xorl %esi, %esi
+; X86-NEXT: .LBB6_2:
+; X86-NEXT: notl %edi
+; X86-NEXT: notl %esi
+; X86-NEXT: movl (%edx), %ebx
+; X86-NEXT: movl 4(%edx), %ecx
+; X86-NEXT: andl %ecx, %edi
+; X86-NEXT: andl %ebx, %esi
+; X86-NEXT: testb $32, %al
+; X86-NEXT: jne .LBB6_4
+; X86-NEXT: # %bb.3:
+; X86-NEXT: movl %ebx, %ecx
+; X86-NEXT: .LBB6_4:
+; X86-NEXT: btl %eax, %ecx
+; X86-NEXT: setae %al
+; X86-NEXT: movl %esi, (%edx)
+; X86-NEXT: movl %edi, 4(%edx)
+; X86-NEXT: popl %esi
+; X86-NEXT: popl %edi
+; X86-NEXT: popl %ebx
+; X86-NEXT: retl
%bita = shl i64 1, %a
%bitb = shl i64 1, %b
%notb = xor i64 %bitb, -1
@@ -153,3 +339,5 @@ define i1 @no_fuse_diff_pos_and(ptr %word, i64 %a, i64 %b) nounwind {
store i64 %res, ptr %word
ret i1 %cmp
}
+;; 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