[llvm] [X86] Fix null dereference in optimizeCompareInstr after lzcnt/tzcnt (PR #224282)
Simon Pilgrim via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 03:08:09 PDT 2026
https://github.com/RKSimon updated https://github.com/llvm/llvm-project/pull/224282
>From a785d8d91efc083697bf6e57021fa02bfbd08061 Mon Sep 17 00:00:00 2001
From: "Ziegler, Tim" <tim.ziegler at intel.com>
Date: Thu, 17 Sep 2026 14:37:10 +0200
Subject: [PATCH 1/3] Fix nullpointer
---
llvm/lib/Target/X86/X86InstrInfo.cpp | 3 ++
.../CodeGen/X86/x86-lzcnt-cmp-null-deref.mir | 54 +++++++++++++++++++
2 files changed, 57 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir
diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index f57658ae5e20e..5dd76755a109e 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -5805,6 +5805,9 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
}
}
+ if (LTZCNTInst && !MI)
+ return false;
+
// If we have to update users but EFLAGS is live-out abort, since we cannot
// easily find all of the users.
if ((MI != nullptr || ShouldUpdateCC) && FlagsMayLiveOut) {
diff --git a/llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir b/llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir
new file mode 100644
index 0000000000000..73e259cbc2478
--- /dev/null
+++ b/llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir
@@ -0,0 +1,54 @@
+# RUN: llc -mtriple=x86_64-- -mattr=+lzcnt -run-pass=peephole-opt -o - %s | FileCheck %s
+#
+# CID 3654645 / FORWARD_NULL: optimizeCompareInstr() breaks out of the backward
+# scan when only LTZCNTInst is set, but assigns MI = LTZCNTInst only after it has
+# found an ADC/SBB/RCL/RCR user of EFLAGS in the forward scan. When no such user
+# exists in the compare's block, both MI and Sub stay null and
+# "Sub->getParent()" dereferences null.
+
+--- |
+ define i32 @flags_clobbered_after_cmp(i64 %x) { ret i32 0 }
+ define i64 @flags_live_out_of_cmp_block(i64 %x) { ret i64 0 }
+...
+
+# Case A: EFLAGS is redefined right after the compare by an instruction that does
+# not read it, so the forward scan breaks before the LTZCNT handling.
+---
+name: flags_clobbered_after_cmp
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $rdi
+ ; CHECK-LABEL: name: flags_clobbered_after_cmp
+ ; CHECK: CMP64ri32 %0, 1, implicit-def $eflags
+ %0:gr64 = COPY $rdi
+ %1:gr64 = LZCNT64rr %0, implicit-def $eflags
+ CMP64ri32 %0, 1, implicit-def $eflags
+ %2:gr32 = MOV32r0 implicit-def dead $eflags
+ $eax = COPY %2
+ RET64 implicit $eax
+...
+
+# Case B: no EFLAGS user at all in the compare's block; EFLAGS is live out to a
+# successor. The "(MI != nullptr || ShouldUpdateCC) && FlagsMayLiveOut" guard
+# that is meant to reject this is skipped because MI is null.
+---
+name: flags_live_out_of_cmp_block
+tracksRegLiveness: true
+body: |
+ bb.0:
+ successors: %bb.1
+ liveins: $rdi
+ ; CHECK-LABEL: name: flags_live_out_of_cmp_block
+ ; CHECK: CMP64ri32 %0, 1, implicit-def $eflags
+ %0:gr64 = COPY $rdi
+ %1:gr64 = LZCNT64rr %0, implicit-def $eflags
+ CMP64ri32 %0, 1, implicit-def $eflags
+ JMP_1 %bb.1
+
+ bb.1:
+ liveins: $eflags
+ %2:gr64 = ADC64ri32 %1, 0, implicit-def dead $eflags, implicit $eflags
+ $rax = COPY %2
+ RET64 implicit $rax
+...
>From ca1bb4fc6b36763771f87dc81e54f129eadbde6e Mon Sep 17 00:00:00 2001
From: "Ziegler, Tim" <tim.ziegler at intel.com>
Date: Fri, 18 Sep 2026 09:31:09 +0200
Subject: [PATCH 2/3] File rename
---
.../{x86-lzcnt-cmp-null-deref.mir => lzcnt-cmp-null-deref.mir} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename llvm/test/CodeGen/X86/{x86-lzcnt-cmp-null-deref.mir => lzcnt-cmp-null-deref.mir} (100%)
diff --git a/llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir b/llvm/test/CodeGen/X86/lzcnt-cmp-null-deref.mir
similarity index 100%
rename from llvm/test/CodeGen/X86/x86-lzcnt-cmp-null-deref.mir
rename to llvm/test/CodeGen/X86/lzcnt-cmp-null-deref.mir
>From aeae9a65c6769a9c7850a373c47ece26dcf7c7d3 Mon Sep 17 00:00:00 2001
From: "Ziegler, Tim" <tim.ziegler at intel.com>
Date: Fri, 18 Sep 2026 09:31:40 +0200
Subject: [PATCH 3/3] Add example comment
---
llvm/lib/Target/X86/X86InstrInfo.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index 5dd76755a109e..f279daf8ad499 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -5590,6 +5590,14 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
break;
}
+ // Try to use CF produced by an LZCNT/TZCNT reading %SrcReg: it and
+ // "cmp $1, %SrcReg" both set CF iff %SrcReg is zero. The other flags
+ // differ, so all EFLAGS users need to read CF only (ADC/SBB/RCL/RCR).
+ // Example:
+ // lzcntq %rdi, %rax
+ // ... // EFLAGS not changed
+ // cmpq $1, %rdi // <-- can be removed
+ // adcq $0, %rax // reads CF only
if (isCmpRedundantAfterLTZCNT(SrcReg, SrcReg2, CmpMask, CmpValue,
Inst)) {
LTZCNTInst = &Inst;
More information about the llvm-commits
mailing list