[llvm] [X86] Coalesce redundant vector register clears in zero_call_used_regs (PR #212147)

Akshay K via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 21:09:13 PDT 2026


https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/212147

>From ee41e5f8cf4fed08a542625a3b7fff379965d370 Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Sat, 25 Jul 2026 18:00:02 -0400
Subject: [PATCH] [X86] Coalesce redundant vector register clears in
 zero_call_used_regs

Map the aliasing XMM/YMM/ZMM views of each vector register to a single clear
per lane, mirroring the existing GPR handling. This removes the duplicate
XMM0-15 zeroing and the spurious VZEROUPPER emitted for -fzero-call-used-regs,
reducing both code size and latency.

Fixes #191911
---
 llvm/lib/Target/X86/X86FrameLowering.cpp      | 34 ++++++++-
 .../CodeGen/X86/zero-call-used-regs-simd.ll   | 72 ++++++-------------
 2 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 7b9f03c87ecdc..e143da425538c 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -688,7 +688,39 @@ void X86FrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
   for (MCRegister Reg : GPRsToZero.set_bits())
     TII.buildClearRegister(Reg, MBB, MBBI, DL);
 
-  // Zero out the remaining registers.
+  // Coalesce the aliasing XMM/YMM/ZMM views of each vector register so a lane
+  // is cleared only once, mirroring the GPR handling above.
+  auto getVectorClearReg = [&](MCRegister Reg) -> MCRegister {
+    if (!X86::VR128RegClass.contains(Reg) &&
+        !X86::VR128XRegClass.contains(Reg) &&
+        !X86::VR256RegClass.contains(Reg) &&
+        !X86::VR256XRegClass.contains(Reg) && !X86::VR512RegClass.contains(Reg))
+      return MCRegister();
+
+    // Clearing the XMM zeroes the whole lane. XMM0-15 use the compact VEX form;
+    // XMM16-31 are EVEX-only, reachable only via the ZMM form.
+    MCRegister Xmm = TRI->getSubReg(Reg, X86::sub_xmm);
+    if (!Xmm)
+      Xmm = Reg;
+    if (X86::VR128RegClass.contains(Xmm))
+      return Xmm;
+    MCRegister Zmm =
+        TRI->getMatchingSuperReg(Xmm, X86::sub_xmm, &X86::VR512RegClass);
+    assert(Zmm && "XMM16-31 must have an enclosing ZMM to clear through");
+    return Zmm;
+  };
+
+  BitVector VecRegsToZero(TRI->getNumRegs());
+  for (MCRegister Reg : RegsToZero.set_bits())
+    if (MCRegister Clear = getVectorClearReg(Reg)) {
+      VecRegsToZero.set(Clear.id());
+      RegsToZero.reset(Reg);
+    }
+
+  for (MCRegister Reg : VecRegsToZero.set_bits())
+    TII.buildClearRegister(Reg, MBB, MBBI, DL);
+
+  // Zero out the remaining registers (e.g. mask registers).
   for (MCRegister Reg : RegsToZero.set_bits())
     TII.buildClearRegister(Reg, MBB, MBBI, DL);
 }
diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll
index 1d5aa24e81f8c..15af5b333874d 100644
--- a/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll
+++ b/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll
@@ -177,8 +177,8 @@ define void @zero_k(<8 x i32> %arg, <8 x i1> %mask) #0 {
 ; AVX2-NEXT:    vpmovzxwd {{.*#+}} ymm1 = xmm1[0],zero,xmm1[1],zero,xmm1[2],zero,xmm1[3],zero,xmm1[4],zero,xmm1[5],zero,xmm1[6],zero,xmm1[7],zero
 ; AVX2-NEXT:    vpslld $31, %ymm1, %ymm1
 ; AVX2-NEXT:    vpmaskmovd %ymm0, %ymm1, 0
-; AVX2-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX2-NEXT:    vpxor %xmm0, %xmm0, %xmm0
+; AVX2-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX2-NEXT:    vzeroupper
 ; AVX2-NEXT:    retq
 ;
@@ -188,8 +188,8 @@ define void @zero_k(<8 x i32> %arg, <8 x i1> %mask) #0 {
 ; AVX512VL-NEXT:    vpslld $31, %ymm1, %ymm1
 ; AVX512VL-NEXT:    vptestmd %ymm1, %ymm1, %k1
 ; AVX512VL-NEXT:    vmovdqa32 %ymm0, 0 {%k1}
-; AVX512VL-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX512VL-NEXT:    vpxor %xmm0, %xmm0, %xmm0
+; AVX512VL-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX512VL-NEXT:    kxorw %k0, %k0, %k1
 ; AVX512VL-NEXT:    vzeroupper
 ; AVX512VL-NEXT:    retq
@@ -199,8 +199,8 @@ define void @zero_k(<8 x i32> %arg, <8 x i1> %mask) #0 {
 ; AVX512BW-NEXT:    vpsllw $15, %xmm1, %xmm1
 ; AVX512BW-NEXT:    vpmovw2m %xmm1, %k1
 ; AVX512BW-NEXT:    vmovdqa32 %ymm0, 0 {%k1}
-; AVX512BW-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX512BW-NEXT:    vpxor %xmm0, %xmm0, %xmm0
+; AVX512BW-NEXT:    vpxor %xmm1, %xmm1, %xmm1
 ; AVX512BW-NEXT:    kxorq %k0, %k0, %k1
 ; AVX512BW-NEXT:    vzeroupper
 ; AVX512BW-NEXT:    retq
@@ -208,7 +208,7 @@ define void @zero_k(<8 x i32> %arg, <8 x i1> %mask) #0 {
   ret void
 }
 
-; FIXME: Under "all", each vector lane is cleared once (not once per XMM/YMM/ZMM view),
+; Under "all", each vector lane is cleared once (not once per XMM/YMM/ZMM view),
 ; and no spurious VZEROUPPER is emitted.
 define void @all_no_live_vec() #1 {
 ; SSE-LABEL: all_no_live_vec:
@@ -344,30 +344,6 @@ define void @all_no_live_vec() #1 {
 ; AVX512VL-NEXT:    vxorps %xmm13, %xmm13, %xmm13
 ; AVX512VL-NEXT:    vxorps %xmm14, %xmm14, %xmm14
 ; AVX512VL-NEXT:    vxorps %xmm15, %xmm15, %xmm15
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k0
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k1
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k2
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k3
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k4
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k5
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k6
-; AVX512VL-NEXT:    kxorw %k0, %k0, %k7
-; AVX512VL-NEXT:    vxorps %xmm0, %xmm0, %xmm0
-; AVX512VL-NEXT:    vxorps %xmm1, %xmm1, %xmm1
-; AVX512VL-NEXT:    vxorps %xmm2, %xmm2, %xmm2
-; AVX512VL-NEXT:    vxorps %xmm3, %xmm3, %xmm3
-; AVX512VL-NEXT:    vxorps %xmm4, %xmm4, %xmm4
-; AVX512VL-NEXT:    vxorps %xmm5, %xmm5, %xmm5
-; AVX512VL-NEXT:    vxorps %xmm6, %xmm6, %xmm6
-; AVX512VL-NEXT:    vxorps %xmm7, %xmm7, %xmm7
-; AVX512VL-NEXT:    vxorps %xmm8, %xmm8, %xmm8
-; AVX512VL-NEXT:    vxorps %xmm9, %xmm9, %xmm9
-; AVX512VL-NEXT:    vxorps %xmm10, %xmm10, %xmm10
-; AVX512VL-NEXT:    vxorps %xmm11, %xmm11, %xmm11
-; AVX512VL-NEXT:    vxorps %xmm12, %xmm12, %xmm12
-; AVX512VL-NEXT:    vxorps %xmm13, %xmm13, %xmm13
-; AVX512VL-NEXT:    vxorps %xmm14, %xmm14, %xmm14
-; AVX512VL-NEXT:    vxorps %xmm15, %xmm15, %xmm15
 ; AVX512VL-NEXT:    vpxord %xmm16, %xmm16, %xmm16
 ; AVX512VL-NEXT:    vpxord %xmm17, %xmm17, %xmm17
 ; AVX512VL-NEXT:    vpxord %xmm18, %xmm18, %xmm18
@@ -384,6 +360,14 @@ define void @all_no_live_vec() #1 {
 ; AVX512VL-NEXT:    vpxord %xmm29, %xmm29, %xmm29
 ; AVX512VL-NEXT:    vpxord %xmm30, %xmm30, %xmm30
 ; AVX512VL-NEXT:    vpxord %xmm31, %xmm31, %xmm31
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k0
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k1
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k2
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k3
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k4
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k5
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k6
+; AVX512VL-NEXT:    kxorw %k0, %k0, %k7
 ; AVX512VL-NEXT:    retq
 ;
 ; AVX512BW-LABEL: all_no_live_vec:
@@ -429,30 +413,6 @@ define void @all_no_live_vec() #1 {
 ; AVX512BW-NEXT:    vxorps %xmm13, %xmm13, %xmm13
 ; AVX512BW-NEXT:    vxorps %xmm14, %xmm14, %xmm14
 ; AVX512BW-NEXT:    vxorps %xmm15, %xmm15, %xmm15
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k0
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k1
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k2
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k3
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k4
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k5
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k6
-; AVX512BW-NEXT:    kxorq %k0, %k0, %k7
-; AVX512BW-NEXT:    vxorps %xmm0, %xmm0, %xmm0
-; AVX512BW-NEXT:    vxorps %xmm1, %xmm1, %xmm1
-; AVX512BW-NEXT:    vxorps %xmm2, %xmm2, %xmm2
-; AVX512BW-NEXT:    vxorps %xmm3, %xmm3, %xmm3
-; AVX512BW-NEXT:    vxorps %xmm4, %xmm4, %xmm4
-; AVX512BW-NEXT:    vxorps %xmm5, %xmm5, %xmm5
-; AVX512BW-NEXT:    vxorps %xmm6, %xmm6, %xmm6
-; AVX512BW-NEXT:    vxorps %xmm7, %xmm7, %xmm7
-; AVX512BW-NEXT:    vxorps %xmm8, %xmm8, %xmm8
-; AVX512BW-NEXT:    vxorps %xmm9, %xmm9, %xmm9
-; AVX512BW-NEXT:    vxorps %xmm10, %xmm10, %xmm10
-; AVX512BW-NEXT:    vxorps %xmm11, %xmm11, %xmm11
-; AVX512BW-NEXT:    vxorps %xmm12, %xmm12, %xmm12
-; AVX512BW-NEXT:    vxorps %xmm13, %xmm13, %xmm13
-; AVX512BW-NEXT:    vxorps %xmm14, %xmm14, %xmm14
-; AVX512BW-NEXT:    vxorps %xmm15, %xmm15, %xmm15
 ; AVX512BW-NEXT:    vpxord %xmm16, %xmm16, %xmm16
 ; AVX512BW-NEXT:    vpxord %xmm17, %xmm17, %xmm17
 ; AVX512BW-NEXT:    vpxord %xmm18, %xmm18, %xmm18
@@ -469,6 +429,14 @@ define void @all_no_live_vec() #1 {
 ; AVX512BW-NEXT:    vpxord %xmm29, %xmm29, %xmm29
 ; AVX512BW-NEXT:    vpxord %xmm30, %xmm30, %xmm30
 ; AVX512BW-NEXT:    vpxord %xmm31, %xmm31, %xmm31
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k0
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k1
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k2
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k3
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k4
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k5
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k6
+; AVX512BW-NEXT:    kxorq %k0, %k0, %k7
 ; AVX512BW-NEXT:    retq
   ret void
 }



More information about the llvm-commits mailing list