[llvm] [ISEL] Fix x86-64 instruction selection bug leaking upper 32 bits (PR #205600)

Ammar Askar via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 25 09:17:03 PDT 2026


https://github.com/ammaraskar updated https://github.com/llvm/llvm-project/pull/205600

>From 38148d1dbe1980aabaeed0705d3957cb204167f2 Mon Sep 17 00:00:00 2001
From: Ammar Askar <aaskar at google.com>
Date: Wed, 24 Jun 2026 17:19:11 +0000
Subject: [PATCH 1/2] [ISEL] Fix x86-64 instruction selection bug leaking upper
 32 bits

The x86 backend had optimization patterns that matched:
  (or (and GR32:$dst, -256), (i32 (zextloadi8 addr:$src)))
and lowered it to:
  (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)

INSERT_SUBREG for sub_8bit emits a movb instruction which preserves
the upper 56 bits. Now, if the GR32 dst came from a node that does
not zero the upper 32 bits (like IMPLICIT_DEF or EXTRACT_SUBREG),
those upper 32 bits would be leaked into the resulting register
without being zeroed.

This fixes #205291 by ensuring the input operand satisfies def32
which requires the upper 32 bits of the register to be set.
---
 llvm/lib/Target/X86/X86InstrCompiler.td |  4 ++--
 llvm/test/CodeGen/X86/insert.ll         | 28 +++++++++++++++++++++----
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 35dfdf2d0067c..6a6604d2a046f 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1680,7 +1680,7 @@ def : Pat<(or (and GR64:$dst, -256),
               (i64 (zextloadi8 addr:$src))),
           (INSERT_SUBREG (i64 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>;
 
-def : Pat<(or (and GR32:$dst, -256),
+def : Pat<(or (and def32:$dst, -256),
               (i32 (zextloadi8 addr:$src))),
           (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>;
 
@@ -1688,7 +1688,7 @@ def : Pat<(or (and GR64:$dst, -65536),
               (i64 (zextloadi16 addr:$src))),
           (INSERT_SUBREG (i64 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>;
 
-def : Pat<(or (and GR32:$dst, -65536),
+def : Pat<(or (and def32:$dst, -65536),
               (i32 (zextloadi16 addr:$src))),
           (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>;
 
diff --git a/llvm/test/CodeGen/X86/insert.ll b/llvm/test/CodeGen/X86/insert.ll
index 381de2ecaa164..dd6b533624cec 100644
--- a/llvm/test/CodeGen/X86/insert.ll
+++ b/llvm/test/CodeGen/X86/insert.ll
@@ -58,8 +58,9 @@ define i32 @sub8_32(i32 noundef %res, ptr %byte) {
 ;
 ; X64-LABEL: sub8_32:
 ; X64:       # %bb.0: # %entry
-; X64-NEXT:    movl %edi, %eax
-; X64-NEXT:    movb (%rsi), %al
+; X64-NEXT:    andl $-256, %edi
+; X64-NEXT:    movzbl (%rsi), %eax
+; X64-NEXT:    orl %edi, %eax
 ; X64-NEXT:    retq
 entry:
   %and = and i32 %res, -256
@@ -81,8 +82,9 @@ define i32 @sub16_32(i32 noundef %res, ptr %byte) {
 ;
 ; X64-LABEL: sub16_32:
 ; X64:       # %bb.0: # %entry
-; X64-NEXT:    movl %edi, %eax
-; X64-NEXT:    movw (%rsi), %ax
+; X64-NEXT:    andl $-65536, %edi # imm = 0xFFFF0000
+; X64-NEXT:    movzwl (%rsi), %eax
+; X64-NEXT:    orl %edi, %eax
 ; X64-NEXT:    retq
 entry:
   %and = and i32 %res, -65536
@@ -91,3 +93,21 @@ entry:
   %or = or i32 %and, %conv2
   ret i32 %or
 }
+
+define void @sub8_32_store_i64ptr(i32 noundef %res, ptr %byte, ptr %out) {
+; X64-LABEL: sub8_32_store_i64ptr:
+; X64:       # %bb.0: # %entry
+; X64-NEXT:    andl $-256, %edi
+; X64-NEXT:    movzbl (%rsi), %eax
+; X64-NEXT:    orl %edi, %eax
+; X64-NEXT:    movq %rax, (%rdx)
+; X64-NEXT:    retq
+entry:
+  %and = and i32 %res, -256
+  %d = load i8, ptr %byte, align 1
+  %conv2 = zext i8 %d to i32
+  %or = or i32 %and, %conv2
+  %z = zext i32 %or to i64
+  store i64 %z, ptr %out
+  ret void
+}

>From a9f3c30fb8e02e36e237bda9172d32f644eaf0f6 Mon Sep 17 00:00:00 2001
From: Ammar Askar <aaskar at google.com>
Date: Thu, 25 Jun 2026 16:16:50 +0000
Subject: [PATCH 2/2] Predicate patterns on In64BitMode and Not64BitMode

---
 llvm/lib/Target/X86/X86InstrCompiler.td | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 6a6604d2a046f..5d6d56457dd68 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1680,17 +1680,29 @@ def : Pat<(or (and GR64:$dst, -256),
               (i64 (zextloadi8 addr:$src))),
           (INSERT_SUBREG (i64 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>;
 
+def : Pat<(or (and GR32:$dst, -256),
+              (i32 (zextloadi8 addr:$src))),
+          (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>,
+      Requires<[Not64BitMode]>;
+
 def : Pat<(or (and def32:$dst, -256),
               (i32 (zextloadi8 addr:$src))),
-          (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>;
+          (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm  i8mem:$src), sub_8bit)>,
+      Requires<[In64BitMode]>;
 
 def : Pat<(or (and GR64:$dst, -65536),
               (i64 (zextloadi16 addr:$src))),
           (INSERT_SUBREG (i64 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>;
 
+def : Pat<(or (and GR32:$dst, -65536),
+              (i32 (zextloadi16 addr:$src))),
+          (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>,
+      Requires<[Not64BitMode]>;
+
 def : Pat<(or (and def32:$dst, -65536),
               (i32 (zextloadi16 addr:$src))),
-          (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>;
+          (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm  i16mem:$src), sub_16bit)>,
+      Requires<[In64BitMode]>;
 
 // To avoid needing to materialize an immediate in a register, use a 32-bit and
 // with implicit zero-extension instead of a 64-bit and if the immediate has at



More information about the llvm-commits mailing list