[llvm] X86: Preserve dead flags on EFLAGS when lowering CMPXCHG16B pseudos (PR #227278)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 04:20:55 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
Co-Authored-By: Claude Opus 5 <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/227278.diff
4 Files Affected:
- (modified) llvm/lib/Target/X86/X86ExpandPseudo.cpp (+1)
- (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+3)
- (added) llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.ll (+90)
- (added) llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.mir (+40)
``````````diff
diff --git a/llvm/lib/Target/X86/X86ExpandPseudo.cpp b/llvm/lib/Target/X86/X86ExpandPseudo.cpp
index b5c196b29f958d..b36951a91da6bf 100644
--- a/llvm/lib/Target/X86/X86ExpandPseudo.cpp
+++ b/llvm/lib/Target/X86/X86ExpandPseudo.cpp
@@ -484,6 +484,7 @@ bool X86ExpandPseudoImpl::expandMI(MachineBasicBlock &MBB,
NewInstr->addOperand(Base);
for (unsigned Idx = 1 + 1; Idx < 1 + X86::AddrNumOperands; ++Idx)
NewInstr->addOperand(MBBI->getOperand(Idx));
+ NewInstr->getOperand(7).setIsDead(MBBI->getOperand(11).isDead());
// Finally, restore the value of RBX.
TII->copyPhysReg(MBB, MBBI, DL, X86::RBX, SaveRbx,
/*SrcIsKill*/ true);
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index ddf9241ef7956c..ffd64c7715192e 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -39268,6 +39268,7 @@ X86TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
case X86::LCMPXCHG16B_NO_RBX: {
const X86RegisterInfo *TRI = Subtarget.getRegisterInfo();
Register BasePtr = TRI->getBaseRegister();
+ bool DeadEFLAGS = MI.getOperand(8).isDead(); // implicit-def $eflags
if (TRI->hasBasePointer(*MF) &&
(BasePtr == X86::RBX || BasePtr == X86::EBX)) {
if (!BB->isLiveIn(BasePtr))
@@ -39284,6 +39285,7 @@ X86TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
MIB.add(MI.getOperand(Idx));
MIB.add(MI.getOperand(X86::AddrNumOperands));
MIB.addReg(SaveRBX);
+ MIB->getOperand(11).setIsDead(DeadEFLAGS); // implicit-def $eflags
} else {
// Simple case, just copy the virtual register to RBX.
BuildMI(*BB, MI, MIMD, TII->get(TargetOpcode::COPY), X86::RBX)
@@ -39292,6 +39294,7 @@ X86TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
BuildMI(*BB, MI, MIMD, TII->get(X86::LCMPXCHG16B));
for (unsigned Idx = 0; Idx < X86::AddrNumOperands; ++Idx)
MIB.add(MI.getOperand(Idx));
+ MIB->getOperand(7).setIsDead(DeadEFLAGS); // implicit-def $eflags
}
MI.eraseFromParent();
return BB;
diff --git a/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.ll b/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.ll
new file mode 100644
index 00000000000000..7ae91473222ead
--- /dev/null
+++ b/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.ll
@@ -0,0 +1,90 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-- -mattr=+cx16 -stop-after=finalize-isel < %s | FileCheck %s
+
+define i128 @cmpxchg_i128_unused_success(ptr %p, i128 %cmp, i128 %new) {
+ ; CHECK-LABEL: name: cmpxchg_i128_unused_success
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $rdi, $rsi, $rdx, $rcx, $r8
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $r8
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rcx
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr64 = COPY $rsi
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gr64 = COPY $rdi
+ ; CHECK-NEXT: $rax = COPY [[COPY3]]
+ ; CHECK-NEXT: $rdx = COPY [[COPY2]]
+ ; CHECK-NEXT: $rcx = COPY [[COPY]]
+ ; CHECK-NEXT: $rbx = COPY [[COPY1]]
+ ; CHECK-NEXT: LCMPXCHG16B [[COPY4]], 1, $noreg, 0, $noreg, implicit-def $rax, implicit-def $rdx, implicit-def dead $eflags, implicit $rax, implicit $rbx, implicit $rcx, implicit $rdx
+ ; CHECK-NEXT: [[COPY5:%[0-9]+]]:gr64 = COPY $rax
+ ; CHECK-NEXT: [[COPY6:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: $rax = COPY [[COPY5]]
+ ; CHECK-NEXT: $rdx = COPY [[COPY6]]
+ ; CHECK-NEXT: RET 0, $rax, $rdx
+ %pair = cmpxchg ptr %p, i128 %cmp, i128 %new seq_cst seq_cst
+ %val = extractvalue { i128, i1 } %pair, 0
+ ret i128 %val
+}
+
+define i1 @cmpxchg_i128_used_success(ptr %p, i128 %cmp, i128 %new) {
+ ; CHECK-LABEL: name: cmpxchg_i128_used_success
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $rdi, $rsi, $rdx, $rcx, $r8
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $r8
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rcx
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr64 = COPY $rsi
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gr64 = COPY $rdi
+ ; CHECK-NEXT: $rax = COPY [[COPY3]]
+ ; CHECK-NEXT: $rdx = COPY [[COPY2]]
+ ; CHECK-NEXT: $rcx = COPY [[COPY]]
+ ; CHECK-NEXT: $rbx = COPY [[COPY1]]
+ ; CHECK-NEXT: LCMPXCHG16B [[COPY4]], 1, $noreg, 0, $noreg, implicit-def $rax, implicit-def $rdx, implicit-def $eflags, implicit $rax, implicit $rbx, implicit $rcx, implicit $rdx
+ ; CHECK-NEXT: [[COPY5:%[0-9]+]]:gr64 = COPY $rax
+ ; CHECK-NEXT: [[COPY6:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: [[SETCCr:%[0-9]+]]:gr8 = SETCCr 4, implicit $eflags
+ ; CHECK-NEXT: $al = COPY [[SETCCr]]
+ ; CHECK-NEXT: RET 0, $al
+ %pair = cmpxchg ptr %p, i128 %cmp, i128 %new seq_cst seq_cst
+ %success = extractvalue { i128, i1 } %pair, 1
+ ret i1 %success
+}
+
+; Force a base pointer in RBX, which selects LCMPXCHG16B_SAVE_RBX.
+define i128 @cmpxchg_i128_unused_success_base_ptr(ptr %p, i128 %cmp, i128 %new, i64 %n) {
+ ; CHECK-LABEL: name: cmpxchg_i128_unused_success_base_ptr
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $rdi, $rsi, $rdx, $rcx, $r8, $r9, $rbx
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $r9
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $r8
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr64 = COPY $rcx
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gr64 = COPY $rsi
+ ; CHECK-NEXT: [[COPY5:%[0-9]+]]:gr64 = COPY $rdi
+ ; CHECK-NEXT: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
+ ; CHECK-NEXT: [[ADD64ri32_:%[0-9]+]]:gr64 = nuw ADD64ri32 [[COPY]], 15, implicit-def dead $eflags
+ ; CHECK-NEXT: [[AND64ri32_:%[0-9]+]]:gr64 = AND64ri32 [[ADD64ri32_]], -16, implicit-def dead $eflags
+ ; CHECK-NEXT: [[COPY6:%[0-9]+]]:gr64 = COPY $rsp
+ ; CHECK-NEXT: [[SUB64rr:%[0-9]+]]:gr64 = SUB64rr [[COPY6]], killed [[AND64ri32_]], implicit-def dead $eflags
+ ; CHECK-NEXT: $rsp = COPY [[SUB64rr]]
+ ; CHECK-NEXT: ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
+ ; CHECK-NEXT: MOV64mr %stack.1.over, 1, $noreg, 0, $noreg, [[SUB64rr]] :: (store (s64) into %ir.over, align 64)
+ ; CHECK-NEXT: $rax = COPY [[COPY4]]
+ ; CHECK-NEXT: $rdx = COPY [[COPY3]]
+ ; CHECK-NEXT: $rcx = COPY [[COPY1]]
+ ; CHECK-NEXT: [[COPY7:%[0-9]+]]:gr64 = COPY $rbx
+ ; CHECK-NEXT: [[LCMPXCHG16B_SAVE_RBX:%[0-9]+]]:gr64 = LCMPXCHG16B_SAVE_RBX [[COPY5]], 1, $noreg, 0, $noreg, [[COPY2]], [[COPY7]], implicit-def $rax, implicit-def $rdx, implicit-def $rbx, implicit-def dead $eflags, implicit $rax, implicit $rcx, implicit $rdx
+ ; CHECK-NEXT: [[COPY8:%[0-9]+]]:gr64 = COPY $rax
+ ; CHECK-NEXT: [[COPY9:%[0-9]+]]:gr64 = COPY $rdx
+ ; CHECK-NEXT: $rax = COPY [[COPY8]]
+ ; CHECK-NEXT: $rdx = COPY [[COPY9]]
+ ; CHECK-NEXT: RET 0, $rax, $rdx
+ %dyn = alloca i8, i64 %n
+ %over = alloca i64, align 64
+ store ptr %dyn, ptr %over
+ %pair = cmpxchg ptr %p, i128 %cmp, i128 %new seq_cst seq_cst
+ %val = extractvalue { i128, i1 } %pair, 0
+ ret i128 %val
+}
diff --git a/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.mir b/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.mir
new file mode 100644
index 00000000000000..f8db2870ad31b7
--- /dev/null
+++ b/llvm/test/CodeGen/X86/cmpxchg16b-dead-eflags.mir
@@ -0,0 +1,40 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=x86_64-- -mattr=+cx16 -run-pass=x86-expand-pseudo -o - %s | FileCheck %s
+
+---
+name: expand_save_rbx_dead_eflags
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $rdi, $rsi, $rax, $rcx, $rdx, $r14
+
+ ; CHECK-LABEL: name: expand_save_rbx_dead_eflags
+ ; CHECK: liveins: $rdi, $rsi, $rax, $rcx, $rdx, $r14
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: $rbx = MOV64rr $rsi
+ ; CHECK-NEXT: LCMPXCHG16B $rdi, 1, $noreg, 0, $noreg, implicit-def $rax, implicit-def $rdx, implicit-def dead $eflags, implicit $rax, implicit $rbx, implicit $rcx, implicit $rdx
+ ; CHECK-NEXT: $rbx = MOV64rr killed $r14
+ ; CHECK-NEXT: RET64 implicit $rax, implicit $rdx
+ $r14 = LCMPXCHG16B_SAVE_RBX $rdi, 1, $noreg, 0, $noreg, $rsi, $r14, implicit-def $rax, implicit-def $rdx, implicit-def $rbx, implicit-def dead $eflags, implicit $rax, implicit $rcx, implicit $rdx
+ RET64 implicit $rax, implicit $rdx
+...
+
+---
+name: expand_save_rbx_live_eflags
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $rdi, $rsi, $rax, $rcx, $rdx, $r14
+
+ ; CHECK-LABEL: name: expand_save_rbx_live_eflags
+ ; CHECK: liveins: $rdi, $rsi, $rax, $rcx, $rdx, $r14
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: $rbx = MOV64rr $rsi
+ ; CHECK-NEXT: LCMPXCHG16B $rdi, 1, $noreg, 0, $noreg, implicit-def $rax, implicit-def $rdx, implicit-def $eflags, implicit $rax, implicit $rbx, implicit $rcx, implicit $rdx
+ ; CHECK-NEXT: $rbx = MOV64rr killed $r14
+ ; CHECK-NEXT: $al = SETCCr 4, implicit $eflags
+ ; CHECK-NEXT: RET64 implicit $al
+ $r14 = LCMPXCHG16B_SAVE_RBX $rdi, 1, $noreg, 0, $noreg, $rsi, $r14, implicit-def $rax, implicit-def $rdx, implicit-def $rbx, implicit-def $eflags, implicit $rax, implicit $rcx, implicit $rdx
+ $al = SETCCr 4, implicit $eflags
+ RET64 implicit $al
+...
``````````
</details>
https://github.com/llvm/llvm-project/pull/227278
More information about the llvm-commits
mailing list