[llvm] [X86] Remove `def32` and drop redundant zero extensions after `isel` (PR #225462)
Akash Manna via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 26 22:00:36 PDT 2026
https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/225462
>From f1fd1e9c35dfc32a851c2a6eed4917f3e8953ba8 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Wed, 16 Sep 2026 22:40:06 +0530
Subject: [PATCH 1/2] [X86] Add tests for implicit i32->i64 zero extension
(NFC)
Pre-commit the current codegen for #122104. The pr222714 and pr218382
functions use the index without zeroing its upper 32 bits.
---
llvm/test/CodeGen/X86/pr122104.ll | 128 ++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/pr122104.ll
diff --git a/llvm/test/CodeGen/X86/pr122104.ll b/llvm/test/CodeGen/X86/pr122104.ll
new file mode 100644
index 0000000000000..3d4b20b789acd
--- /dev/null
+++ b/llvm/test/CodeGen/X86/pr122104.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s
+
+; The zero_extend of the index is selected before its 'and' operand, which
+; shrinkAndImmediate then replaces with the truncated cmov result. The upper
+; 32 bits of that value are not zero, so the index must still be zero-extended.
+define i64 @pr222714(i64 %x, ptr %p) {
+; CHECK-LABEL: pr222714:
+; CHECK: # %bb.0:
+; CHECK-NEXT: shlq $30, %rdi
+; CHECK-NEXT: movabsq $17179869183, %rcx # imm = 0x3FFFFFFFF
+; CHECK-NEXT: movabsq $-4294967295, %rax # imm = 0xFFFFFFFF00000001
+; CHECK-NEXT: addq %rax, %rcx
+; CHECK-NEXT: testq %rdi, %rdi
+; CHECK-NEXT: cmovneq %rax, %rcx
+; CHECK-NEXT: movl %ecx, %eax
+; CHECK-NEXT: addq (%rsi,%rcx,8), %rax
+; CHECK-NEXT: retq
+ %m = and i64 %x, 17179869183
+ %c = icmp eq i64 %m, 0
+ %s = select i1 %c, i64 17179869183, i64 0
+ %a = add i64 %s, -4294967295
+ %idx = and i64 %a, 1
+ %gep = getelementptr inbounds i64, ptr %p, i64 %idx
+ %ld = load i64, ptr %gep, align 8
+ %lo = and i64 %a, 255
+ %r = add i64 %ld, %lo
+ ret i64 %r
+}
+
+; Same through a vector round trip: bit 32 of the extracted lane is set.
+define <4 x i32> @pr218382(i32 %x) "target-features"="+avx2,+popcnt" {
+; CHECK-LABEL: pr218382:
+; CHECK: # %bb.0:
+; CHECK-NEXT: andl $1, %edi
+; CHECK-NEXT: popcntl %edi, %eax
+; CHECK-NEXT: vpbroadcastd {{.*#+}} xmm0 = [1,1,1,1]
+; CHECK-NEXT: vpinsrd $0, %eax, %xmm0, %xmm0
+; CHECK-NEXT: vmovq %xmm0, %rax
+; CHECK-NEXT: vpxor %xmm0, %xmm0, %xmm0
+; CHECK-NEXT: vmovdqa %xmm0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT: movl $0, -24(%rsp,%rax,4)
+; CHECK-NEXT: vmovaps -{{[0-9]+}}(%rsp), %xmm0
+; CHECK-NEXT: retq
+ %a = and i32 %x, 1
+ %c = call i32 @llvm.ctpop.i32(i32 %a)
+ %v = insertelement <4 x i32> splat (i32 1), i32 %c, i64 0
+ %b = bitcast <4 x i32> %v to <2 x i64>
+ %e = extractelement <2 x i64> %b, i64 0
+ %idx = trunc i64 %e to i8
+ %r = insertelement <4 x i32> zeroinitializer, i32 0, i8 %idx
+ ret <4 x i32> %r
+}
+
+define i64 @zext_trunc(i64 %x) {
+; CHECK-LABEL: zext_trunc:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: retq
+ %t = trunc i64 %x to i32
+ %z = zext i32 %t to i64
+ ret i64 %z
+}
+
+; 32-bit instructions zero the upper bits: no movl below.
+
+define i64 @zext_add(i32 %a, i32 %b) {
+; CHECK-LABEL: zext_add:
+; CHECK: # %bb.0:
+; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
+; CHECK-NEXT: # kill: def $edi killed $edi def $rdi
+; CHECK-NEXT: leal (%rdi,%rsi), %eax
+; CHECK-NEXT: retq
+ %s = add i32 %a, %b
+ %z = zext i32 %s to i64
+ ret i64 %z
+}
+
+define i64 @zext_and(i32 %a) {
+; CHECK-LABEL: zext_and:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movzbl %dil, %eax
+; CHECK-NEXT: retq
+ %m = and i32 %a, 255
+ %z = zext i32 %m to i64
+ ret i64 %z
+}
+
+define i64 @zext_popcnt(i32 %a) "target-features"="+popcnt" {
+; CHECK-LABEL: zext_popcnt:
+; CHECK: # %bb.0:
+; CHECK-NEXT: popcntl %edi, %eax
+; CHECK-NEXT: retq
+ %c = call i32 @llvm.ctpop.i32(i32 %a)
+ %z = zext i32 %c to i64
+ ret i64 %z
+}
+
+define i64 @zext_cttz(i32 %a) {
+; CHECK-LABEL: zext_cttz:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $32, %eax
+; CHECK-NEXT: rep bsfl %edi, %eax
+; CHECK-NEXT: retq
+ %c = call i32 @llvm.cttz.i32(i32 %a, i1 false)
+ %z = zext i32 %c to i64
+ ret i64 %z
+}
+
+define i64 @zext_insert_byte(i32 %x, i32 %y, ptr %p) {
+; CHECK-LABEL: zext_insert_byte:
+; CHECK: # %bb.0:
+; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
+; CHECK-NEXT: # kill: def $edi killed $edi def $rdi
+; CHECK-NEXT: leal (%rdi,%rsi), %eax
+; CHECK-NEXT: movb (%rdx), %al
+; CHECK-NEXT: retq
+ %s = add i32 %x, %y
+ %hi = and i32 %s, -256
+ %b = load i8, ptr %p
+ %zb = zext i8 %b to i32
+ %o = or i32 %hi, %zb
+ %z = zext i32 %o to i64
+ ret i64 %z
+}
+
+declare i32 @llvm.ctpop.i32(i32)
+declare i32 @llvm.cttz.i32(i32, i1)
>From 2ca7eaf9e074419c025ff3448b1f4e9795fffb9f Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Wed, 16 Sep 2026 22:50:10 +0530
Subject: [PATCH 2/2] [X86] Remove def32 and drop redundant zero extensions
after isel
def32 decided from the unselected operand of a zero_extend that the
upper 32 bits would be zero. Isel selects users before operands, so a
later transform on that operand (shrinkAndImmediate replacing an 'and'
with its truncate) could break the assumption after the SUBREG_TO_REG
was emitted. Always select the zero extension with an explicit MOV32rr
and remove it in PostprocessISelDAG when the final machine node is a
real 32-bit instruction.
Fixes #122104
---
llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 97 +++++++++++++++++++++++-
llvm/lib/Target/X86/X86InstrCompiler.td | 44 +++--------
llvm/lib/Target/X86/X86InstrExtension.td | 9 +--
llvm/lib/Target/X86/X86InstrFragments.td | 19 -----
llvm/test/CodeGen/X86/insert.ll | 4 +-
llvm/test/CodeGen/X86/pr122104.ll | 13 ++--
6 files changed, 117 insertions(+), 69 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index 4478930016f63..dda4cabc7345f 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -535,6 +535,11 @@ namespace {
return Mask.countr_one() >= Width;
}
+ /// Return true if the selected i32 value \p V is guaranteed to have the
+ /// upper 32 bits of its 64-bit super-register zeroed. Only valid once
+ /// every node has been selected.
+ bool isDef32(SDValue V) const;
+
/// Return an SDNode that returns the value of the global base register.
/// Output instructions required to initialize the global base register,
/// if necessary.
@@ -1623,6 +1628,76 @@ bool X86DAGToDAGISel::tryOptimizeRem8Extend(SDNode *N) {
return true;
}
+// Any x86-64 instruction writing a 32-bit register zeroes the upper 32 bits of
+// the 64-bit register. This is only decided after selection, on the final
+// machine node: isel selects users before operands, so a predicate on the
+// unselected operand of a zero_extend can be invalidated by a transform that
+// later replaces that operand (e.g. shrinkAndImmediate replacing a redundant
+// 'and' with its truncate operand).
+bool X86DAGToDAGISel::isDef32(SDValue V) const {
+ const X86RegisterInfo *TRI = Subtarget->getRegisterInfo();
+ while (V.getValueType() == MVT::i32 && V.isMachineOpcode()) {
+ unsigned Opc = V.getMachineOpcode();
+ switch (Opc) {
+ case TargetOpcode::COPY:
+ case TargetOpcode::COPY_TO_REGCLASS:
+ V = V.getOperand(0);
+ continue;
+ case TargetOpcode::INSERT_SUBREG: {
+ // The upper 32 bits are those of the base register.
+ unsigned SubIdx = V.getConstantOperandVal(2);
+ if (SubIdx != X86::sub_8bit && SubIdx != X86::sub_8bit_hi &&
+ SubIdx != X86::sub_16bit)
+ return false;
+ V = V.getOperand(0);
+ continue;
+ }
+ case X86::BSF32rr:
+ case X86::BSF32rm:
+ case X86::BSR32rr:
+ case X86::BSR32rm: {
+ // A zero source leaves the tied destination untouched, i.e. the
+ // fallback value. An IMPLICIT_DEF fallback means the result is poison.
+ SDValue Fallback = V.getOperand(0);
+ if (Fallback.isMachineOpcode() &&
+ Fallback.getMachineOpcode() == TargetOpcode::IMPLICIT_DEF)
+ return true;
+ V = Fallback;
+ continue;
+ }
+ // Pseudos that expand to a single instruction writing the whole register.
+ case X86::MOV32r0:
+ case X86::MOV32r1:
+ case X86::MOV32r_1:
+ case X86::SETB_C32r:
+ case X86::ADD32rr_DB:
+ case X86::ADD32ri_DB:
+ return true;
+ default:
+ break;
+ }
+
+ // EXTRACT_SUBREG, IMPLICIT_DEF and other pseudos (e.g. CMOV_GR32, which
+ // expands to a PHI) provide no guarantee.
+ if (Opc <= TargetOpcode::GENERIC_OP_END)
+ return false;
+ const MCInstrDesc &Desc = getInstrInfo()->get(Opc);
+ if (Desc.isPseudo())
+ return false;
+
+ // A real instruction writes all 32 bits of an explicit GR32 definition.
+ unsigned ResNo = V.getResNo();
+ if (ResNo >= Desc.getNumDefs())
+ return false;
+ const MCOperandInfo &OpInfo = Desc.operands()[ResNo];
+ if (OpInfo.OperandType != MCOI::OPERAND_REGISTER ||
+ OpInfo.isLookupRegClassByHwMode())
+ return false;
+ return X86::GR32RegClass.hasSubClassEq(TRI->getRegClass(OpInfo.RegClass));
+ }
+ return false;
+}
+
void X86DAGToDAGISel::PostprocessISelDAG() {
// Skip peepholes at -O0.
if (TM.getOptLevel() == CodeGenOptLevel::None)
@@ -1772,16 +1847,30 @@ void X86DAGToDAGISel::PostprocessISelDAG() {
MadeChange = true;
continue;
}
- // Attempt to remove vectors moves that were inserted to zero upper bits.
+ // Attempt to remove moves that were inserted to zero upper bits.
case TargetOpcode::SUBREG_TO_REG: {
unsigned SubRegIdx = N->getConstantOperandVal(1);
- if (SubRegIdx != X86::sub_xmm && SubRegIdx != X86::sub_ymm)
- continue;
-
SDValue Move = N->getOperand(0);
if (!Move.isMachineOpcode())
continue;
+ // (SUBREG_TO_REG (MOV32rr X)) from the i32->i64 zero_extend patterns:
+ // drop the MOV32rr if X already zeroes the upper 32 bits.
+ if (SubRegIdx == X86::sub_32bit) {
+ if (Move.getMachineOpcode() != X86::MOV32rr ||
+ !isDef32(Move.getOperand(0)))
+ continue;
+ SDNode *Res = CurDAG->UpdateNodeOperands(N, Move.getOperand(0),
+ N->getOperand(1));
+ if (Res != N)
+ ReplaceUses(N, Res);
+ MadeChange = true;
+ continue;
+ }
+
+ if (SubRegIdx != X86::sub_xmm && SubRegIdx != X86::sub_ymm)
+ continue;
+
// Make sure its one of the move opcodes we recognize.
switch (Move.getMachineOpcode()) {
default:
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 45619663c45cb..f66d4c4a10111 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1555,12 +1555,9 @@ def : Pat<(i64 (anyext GR32:$src)),
def : Pat<(i32 (anyext_sdiv GR8:$src)), (MOVSX32rr8 GR8:$src)>;
-// In the case of a 32-bit def that is known to implicitly zero-extend,
-// we can use a SUBREG_TO_REG.
-def : Pat<(i64 (zext def32:$src)),
- (SUBREG_TO_REG GR32:$src, sub_32bit)>;
-def : Pat<(i64 (and (anyext def32:$src), 0x00000000FFFFFFFF)),
- (SUBREG_TO_REG GR32:$src, sub_32bit)>;
+// Select like (i64 (zext GR32:$src)), see X86InstrExtension.td.
+def : Pat<(i64 (and (anyext GR32:$src), 0x00000000FFFFFFFF)),
+ (SUBREG_TO_REG (MOV32rr GR32:$src), sub_32bit)>;
//===----------------------------------------------------------------------===//
// Pattern match OR as ADD
@@ -1707,34 +1704,15 @@ def : Pat<(or (and GR64:$dst, -65536),
(i64 (zextloadi16 addr:$src))),
(INSERT_SUBREG (i64 (COPY $dst)), (MOV16rm i16mem:$src), sub_16bit)>;
-let Predicates = [Not64BitMode] in {
- def : Pat<(or (and GR32:$dst, -256),
- (i32 (zextloadi8 addr:$src))),
- (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm i8mem:$src), sub_8bit)>;
+// The upper 32 bits of the result are those of $dst; a zero extension of the
+// result only drops its MOV32rr if X86DAGToDAGISel::isDef32 holds for $dst.
+def : Pat<(or (and GR32:$dst, -256),
+ (i32 (zextloadi8 addr:$src))),
+ (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm i8mem:$src), sub_8bit)>;
- def : Pat<(or (and GR32:$dst, -65536),
- (i32 (zextloadi16 addr:$src))),
- (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm i16mem:$src), sub_16bit)>;
-}
-
-let Predicates = [In64BitMode] in {
- def : Pat<(or (and def32:$dst, -256),
- (i32 (zextloadi8 addr:$src))),
- (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm i8mem:$src), sub_8bit)>;
-
- def : Pat<(or (and def32:$dst, -65536),
- (i32 (zextloadi16 addr:$src))),
- (INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm i16mem:$src), sub_16bit)>;
-
- // These patterns use MOV32rr since GR32 could have junk in the upper 32-bits.
- def : Pat<(or (and GR32:$dst, -256),
- (i32 (zextloadi8 addr:$src))),
- (MOV32rr (INSERT_SUBREG (i32 (COPY $dst)), (MOV8rm i8mem:$src), sub_8bit))>;
-
- def : Pat<(or (and GR32:$dst, -65536),
- (i32 (zextloadi16 addr:$src))),
- (MOV32rr (INSERT_SUBREG (i32 (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)>;
// 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
diff --git a/llvm/lib/Target/X86/X86InstrExtension.td b/llvm/lib/Target/X86/X86InstrExtension.td
index 7bf0cb1d64755..72b4ad3d31450 100644
--- a/llvm/lib/Target/X86/X86InstrExtension.td
+++ b/llvm/lib/Target/X86/X86InstrExtension.td
@@ -211,11 +211,10 @@ def : Pat<(i64 (zext GR16:$src)),
def : Pat<(zextloadi64i16 addr:$src),
(SUBREG_TO_REG (MOVZX32rm16 addr:$src), sub_32bit)>;
-// The preferred way to do 32-bit-to-64-bit zero extension on x86-64 is to use a
-// SUBREG_TO_REG to utilize implicit zero-extension, however this isn't possible
-// when the 32-bit value is defined by a truncate or is copied from something
-// where the high bits aren't necessarily all zero. In such cases, we fall back
-// to these explicit zext instructions.
+// 32-bit-to-64-bit zero extension always uses an explicit MOV32rr. Whether the
+// instruction that ends up defining $src already zeroes the upper 32 bits is
+// only known once every node has been selected, so the MOV32rr is removed in
+// X86DAGToDAGISel::PostprocessISelDAG instead (see isDef32).
def : Pat<(i64 (zext GR32:$src)),
(SUBREG_TO_REG (MOV32rr GR32:$src), sub_32bit)>;
def : Pat<(i64 (zextloadi64i32 addr:$src)),
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index 4a32284e79207..88dbbe5b599e5 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -841,25 +841,6 @@ def anyext_sdiv : PatFrag<(ops node:$lhs), (anyext node:$lhs),[{
N->getOperand(0).getResNo() == 1);
}]>;
-// Any instruction that defines a 32-bit result leaves the high half of the
-// register. Truncate can be lowered to EXTRACT_SUBREG. CopyFromReg may
-// be copying from a truncate. AssertSext/AssertZext/AssertAlign aren't saying
-// anything about the upper 32 bits, they're probably just qualifying a
-// CopyFromReg. FREEZE may be coming from a a truncate. BitScan fall through
-// values may not zero the upper bits correctly.
-// Any other 32-bit operation will zero-extend up to 64 bits.
-def def32 : PatLeaf<(i32 GR32:$src), [{
- return N->getOpcode() != ISD::TRUNCATE &&
- N->getOpcode() != TargetOpcode::EXTRACT_SUBREG &&
- N->getOpcode() != ISD::CopyFromReg &&
- N->getOpcode() != ISD::AssertSext &&
- N->getOpcode() != ISD::AssertZext &&
- N->getOpcode() != ISD::AssertAlign &&
- N->getOpcode() != ISD::FREEZE &&
- !((N->getOpcode() == X86ISD::BSF || N->getOpcode() == X86ISD::BSR) &&
- (!N->getOperand(0).isUndef() && !isa<ConstantSDNode>(N->getOperand(0))));
-}]>;
-
def shiftMask8 : PatFrag<(ops node:$lhs), (and node:$lhs, imm), [{
return isUnneededShiftMask(N, 3);
}]>;
diff --git a/llvm/test/CodeGen/X86/insert.ll b/llvm/test/CodeGen/X86/insert.ll
index 53d0040fa2464..f8b44881287de 100644
--- a/llvm/test/CodeGen/X86/insert.ll
+++ b/llvm/test/CodeGen/X86/insert.ll
@@ -58,8 +58,8 @@ define i32 @sub8_32(i32 noundef %res, ptr %byte) {
;
; X64-LABEL: sub8_32:
; X64: # %bb.0: # %entry
-; X64-NEXT: movb (%rsi), %dil
; X64-NEXT: movl %edi, %eax
+; X64-NEXT: movb (%rsi), %al
; X64-NEXT: retq
entry:
%and = and i32 %res, -256
@@ -81,8 +81,8 @@ define i32 @sub16_32(i32 noundef %res, ptr %byte) {
;
; X64-LABEL: sub16_32:
; X64: # %bb.0: # %entry
-; X64-NEXT: movw (%rsi), %di
; X64-NEXT: movl %edi, %eax
+; X64-NEXT: movw (%rsi), %ax
; X64-NEXT: retq
entry:
%and = and i32 %res, -65536
diff --git a/llvm/test/CodeGen/X86/pr122104.ll b/llvm/test/CodeGen/X86/pr122104.ll
index 3d4b20b789acd..2ee36a7bb64b7 100644
--- a/llvm/test/CodeGen/X86/pr122104.ll
+++ b/llvm/test/CodeGen/X86/pr122104.ll
@@ -8,13 +8,13 @@ define i64 @pr222714(i64 %x, ptr %p) {
; CHECK-LABEL: pr222714:
; CHECK: # %bb.0:
; CHECK-NEXT: shlq $30, %rdi
-; CHECK-NEXT: movabsq $17179869183, %rcx # imm = 0x3FFFFFFFF
-; CHECK-NEXT: movabsq $-4294967295, %rax # imm = 0xFFFFFFFF00000001
-; CHECK-NEXT: addq %rax, %rcx
+; CHECK-NEXT: movabsq $17179869183, %rax # imm = 0x3FFFFFFFF
+; CHECK-NEXT: movabsq $-4294967295, %rcx # imm = 0xFFFFFFFF00000001
+; CHECK-NEXT: addq %rcx, %rax
; CHECK-NEXT: testq %rdi, %rdi
-; CHECK-NEXT: cmovneq %rax, %rcx
-; CHECK-NEXT: movl %ecx, %eax
-; CHECK-NEXT: addq (%rsi,%rcx,8), %rax
+; CHECK-NEXT: cmovneq %rcx, %rax
+; CHECK-NEXT: movl %eax, %eax
+; CHECK-NEXT: addq (%rsi,%rax,8), %rax
; CHECK-NEXT: retq
%m = and i64 %x, 17179869183
%c = icmp eq i64 %m, 0
@@ -39,6 +39,7 @@ define <4 x i32> @pr218382(i32 %x) "target-features"="+avx2,+popcnt" {
; CHECK-NEXT: vmovq %xmm0, %rax
; CHECK-NEXT: vpxor %xmm0, %xmm0, %xmm0
; CHECK-NEXT: vmovdqa %xmm0, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT: movl %eax, %eax
; CHECK-NEXT: movl $0, -24(%rsp,%rax,4)
; CHECK-NEXT: vmovaps -{{[0-9]+}}(%rsp), %xmm0
; CHECK-NEXT: retq
More information about the llvm-commits
mailing list