https://github.com/zhouguangyuan0718 created 
https://github.com/llvm/llvm-project/pull/186123

## Summary
Add x86-32 support for reserving EDI/ESI via `-ffixed-edi` and `-ffixed-esi`.
Update the X86 backend to respect those reservations in register handling,
callee-save logic, and memcpy/memset lowering, and add driver/codegen tests.

## Test Plan
Not run (not requested).

>From c94801badc6703dcf9c3ee54c48983c046889a7e Mon Sep 17 00:00:00 2001
From: ZhouGuangyuan <[email protected]>
Date: Thu, 12 Mar 2026 12:33:45 +0800
Subject: [PATCH] [X86] Support reserving EDI and ESI on x86-32

Add clang driver support for -ffixed-edi and -ffixed-esi and map them to
reserve-edi/reserve-esi target features on i386.

Teach the X86 backend to treat EDI/ESI as user-reserved registers in
register lookup, reserved-register tracking, and callee-save handling,
and avoid selecting REP MOVS/REP STOS when those registers are reserved.

Add driver and codegen tests covering option handling and the resulting
code generation changes.

Signed-off-by: ZhouGuangyuan <[email protected]>
---
 clang/include/clang/Options/Options.td       |  4 ++
 clang/lib/Basic/Targets/X86.h                | 11 ++++
 clang/lib/Driver/ToolChains/Arch/X86.cpp     | 14 +++++
 clang/test/Driver/x86-fixed-di-si-register.c | 17 ++++++
 llvm/lib/Target/X86/X86.td                   |  4 ++
 llvm/lib/Target/X86/X86FrameLowering.cpp     |  2 +-
 llvm/lib/Target/X86/X86ISelLowering.cpp      |  8 +--
 llvm/lib/Target/X86/X86RegisterInfo.cpp      | 23 ++++---
 llvm/lib/Target/X86/X86SelectionDAGInfo.cpp  | 22 +++++--
 llvm/test/CodeGen/X86/reserveDISIreg.ll      | 63 ++++++++++++++++++++
 10 files changed, 149 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Driver/x86-fixed-di-si-register.c
 create mode 100644 llvm/test/CodeGen/X86/reserveDISIreg.ll

diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 4bbdd9c8c0e58..27dc99b2280b6 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -7175,6 +7175,10 @@ def mapxf : Flag<["-"], "mapxf">, 
Group<m_x86_Features_Group>;
 def mno_apxf : Flag<["-"], "mno-apxf">, Group<m_x86_Features_Group>;
 def mapx_inline_asm_use_gpr32 : Flag<["-"], "mapx-inline-asm-use-gpr32">, 
Group<m_Group>,
                                 HelpText<"Enable use of GPR32 in inline 
assembly for APX">;
+def ffixed_edi : Flag<["-"], "ffixed-edi">, Group<m_Group>,
+                 HelpText<"Reserve the edi register (x86 only)">;
+def ffixed_esi : Flag<["-"], "ffixed-esi">, Group<m_Group>,
+                 HelpText<"Reserve the esi register (x86 only)">;
 foreach i = {8, 10-15} in
     def ffixed_r#i : Flag<["-"], "ffixed-r"#i>, Group<m_Group>,
     HelpText<"Reserve the r"#i#" register (x86_64 only)">;
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index f99bbf363458f..08e80412aca3f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -485,6 +485,17 @@ class LLVM_LIBRARY_VISIBILITY X86_32TargetInfo : public 
X86TargetInfo {
     return -1;
   }
 
+  bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize,
+                                      bool &HasSizeMismatch) const override {
+    if (RegName == "edi" || RegName == "esi") {
+      HasSizeMismatch = RegSize != 32;
+      return getTargetOpts().FeatureMap.lookup(("reserve-" + RegName).str());
+    }
+
+    return X86TargetInfo::validateGlobalRegisterVariable(RegName, RegSize,
+                                                         HasSizeMismatch);
+  }
+
   bool validateOperandSize(const llvm::StringMap<bool> &FeatureMap,
                            StringRef Constraint, unsigned Size) const override 
{
     switch (Constraint[0]) {
diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp 
b/clang/lib/Driver/ToolChains/Arch/X86.cpp
index c113da6733370..0a8ffa871630f 100644
--- a/clang/lib/Driver/ToolChains/Arch/X86.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp
@@ -349,6 +349,20 @@ void x86::getX86TargetFeatures(const Driver &D, const 
llvm::Triple &Triple,
   }
 
   // Handle features corresponding to "-ffixed-X" options
+  if (Args.hasArg(options::OPT_ffixed_edi)) {
+    if (ArchType != llvm::Triple::x86)
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << "-ffixed-edi" << Triple.getTriple();
+    else
+      Features.push_back("+reserve-edi");
+  }
+  if (Args.hasArg(options::OPT_ffixed_esi)) {
+    if (ArchType != llvm::Triple::x86)
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << "-ffixed-esi" << Triple.getTriple();
+    else
+      Features.push_back("+reserve-esi");
+  }
 #define RESERVE_REG(REG)                                                       
\
   if (Args.hasArg(options::OPT_ffixed_##REG))                                  
\
     Features.push_back("+reserve-" #REG);
diff --git a/clang/test/Driver/x86-fixed-di-si-register.c 
b/clang/test/Driver/x86-fixed-di-si-register.c
new file mode 100644
index 0000000000000..eff7fcf992993
--- /dev/null
+++ b/clang/test/Driver/x86-fixed-di-si-register.c
@@ -0,0 +1,17 @@
+// RUN: %clang --target=i386-unknown-linux-gnu -ffixed-edi -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-EDI < %t %s
+// CHECK-FIXED-EDI: "-target-feature" "+reserve-edi"
+
+// RUN: %clang --target=i386-unknown-linux-gnu -ffixed-esi -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-ESI < %t %s
+// CHECK-FIXED-ESI: "-target-feature" "+reserve-esi"
+
+// RUN: %clang --target=i386-unknown-linux-gnu -ffixed-edi -ffixed-esi -### %s 
2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-EDI < %t %s
+// RUN: FileCheck --check-prefix=CHECK-FIXED-ESI < %t %s
+
+// RUN: not %clang --target=x86_64-unknown-linux-gnu -ffixed-edi -### %s 2>&1 
| FileCheck --check-prefix=CHECK-NO-X64-EDI %s
+// CHECK-NO-X64-EDI: error: unsupported option '-ffixed-edi' for target 
'x86_64-unknown-linux-gnu'
+
+// RUN: not %clang --target=x86_64-unknown-linux-gnu -ffixed-esi -### %s 2>&1 
| FileCheck --check-prefix=CHECK-NO-X64-ESI %s
+// CHECK-NO-X64-ESI: error: unsupported option '-ffixed-esi' for target 
'x86_64-unknown-linux-gnu'
diff --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td
index eca763735c315..c983792fbfa4b 100644
--- a/llvm/lib/Target/X86/X86.td
+++ b/llvm/lib/Target/X86/X86.td
@@ -38,6 +38,10 @@ foreach i = {8-15} in
 foreach i = {16-31} in
     def FeatureReserveR#i : SubtargetFeature<"reserve-r"#i, 
"ReservedRReg[X86::R"#i#"]", "true",
                                              "Reserve R"#i#", making it 
unavailable as a GPR">;
+def FeatureReserveEDI : SubtargetFeature<"reserve-edi", 
"ReservedRReg[X86::EDI]", "true",
+                                         "Reserve EDI, making it unavailable 
as a GPR">;
+def FeatureReserveESI : SubtargetFeature<"reserve-esi", 
"ReservedRReg[X86::ESI]", "true",
+                                         "Reserve ESI, making it unavailable 
as a GPR">;
 
 def FeatureX87     : SubtargetFeature<"x87","HasX87", "true",
                                       "Enable X87 float instructions">;
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp 
b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 8791470b0b5e1..438ba03554dfd 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -3211,7 +3211,7 @@ void 
X86FrameLowering::determineCalleeSaves(MachineFunction &MF,
       BasePtr = getX86SubSuperRegister(BasePtr, 64);
     SavedRegs.set(BasePtr);
   }
-  if (STI.is64Bit()) {
+  if (STI.hasUserReservedRegisters()) {
     for (int Reg = SavedRegs.find_first(); Reg != -1;
          Reg = SavedRegs.find_next(Reg)) {
       if (STI.isRegisterReservedByUser(Reg)) {
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp 
b/llvm/lib/Target/X86/X86ISelLowering.cpp
index bbe32aee10db5..d689836b9d8ea 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -28817,11 +28817,9 @@ Register X86TargetLowering::getRegisterByName(const 
char* RegName, LLT VT,
   if (Reg)
     return Reg;
 
-  if (Subtarget.is64Bit()) {
-    Reg = MatchRegisterName(RegName);
-    if (!Subtarget.isRegisterReservedByUser(Reg))
-      Reg = Register();
-  }
+  Reg = MatchRegisterName(RegName);
+  if (!Subtarget.isRegisterReservedByUser(Reg))
+    Reg = Register();
 
   return Reg;
 }
diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp 
b/llvm/lib/Target/X86/X86RegisterInfo.cpp
index 8888bf3a46a55..ddab1ff3269e8 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -516,17 +516,24 @@ BitVector X86RegisterInfo::getReservedRegs(const 
MachineFunction &MF) const {
   Reserved.set(X86::SSP);
 
   auto &ST = MF.getSubtarget<X86Subtarget>();
-  if (ST.is64Bit() && ST.hasUserReservedRegisters()) {
-    // Set r# as reserved register if user required
-    for (unsigned Reg = X86::R8; Reg <= X86::R15; ++Reg)
-      if (ST.isRegisterReservedByUser(Reg))
-        for (const MCPhysReg &SubReg : subregs_inclusive(Reg))
-          Reserved.set(SubReg);
-    if (ST.hasEGPR())
-      for (unsigned Reg = X86::R16; Reg <= X86::R31; ++Reg)
+  if (ST.hasUserReservedRegisters()) {
+    if (ST.is64Bit()) {
+      // Set r# as reserved register if user required.
+      for (unsigned Reg = X86::R8; Reg <= X86::R15; ++Reg)
         if (ST.isRegisterReservedByUser(Reg))
           for (const MCPhysReg &SubReg : subregs_inclusive(Reg))
             Reserved.set(SubReg);
+      if (ST.hasEGPR())
+        for (unsigned Reg = X86::R16; Reg <= X86::R31; ++Reg)
+          if (ST.isRegisterReservedByUser(Reg))
+            for (const MCPhysReg &SubReg : subregs_inclusive(Reg))
+              Reserved.set(SubReg);
+    } else {
+      for (unsigned Reg : {X86::EDI, X86::ESI})
+        if (ST.isRegisterReservedByUser(Reg))
+          for (const MCPhysReg &SubReg : subregs_inclusive(Reg))
+            Reserved.set(SubReg);
+    }
   }
 
   // Set the instruction pointer register and its aliases as reserved.
diff --git a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp 
b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
index dff8832e851d9..619c57d08980e 100644
--- a/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
+++ b/llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
@@ -264,10 +264,18 @@ SDValue X86SelectionDAGInfo::EmitTargetCodeForMemset(
     SelectionDAG &DAG, const SDLoc &dl, SDValue Chain, SDValue Dst, SDValue 
Val,
     SDValue Size, Align Alignment, bool isVolatile, bool AlwaysInline,
     MachinePointerInfo DstPtrInfo) const {
+  const X86Subtarget &Subtarget =
+      DAG.getMachineFunction().getSubtarget<X86Subtarget>();
+
   // If to a segment-relative address space, use the default lowering.
   if (DstPtrInfo.getAddrSpace() >= 256)
     return SDValue();
 
+  // REP STOS uses EDI on x86-32. Fall back if the user reserved EDI, so the
+  // generic expander can avoid emitting REP STOS.
+  if (!Subtarget.is64Bit() && Subtarget.isRegisterReservedByUser(X86::EDI))
+    return SDValue();
+
   // If the base register might conflict with our physical registers, bail out.
   const MCPhysReg ClobberSet[] = {X86::RCX, X86::RAX, X86::RDI,
                                   X86::ECX, X86::EAX, X86::EDI};
@@ -278,8 +286,6 @@ SDValue X86SelectionDAGInfo::EmitTargetCodeForMemset(
   if (!ConstantSize)
     return SDValue();
 
-  const X86Subtarget &Subtarget =
-      DAG.getMachineFunction().getSubtarget<X86Subtarget>();
   return emitConstantSizeRepstos(
       DAG, Subtarget, dl, Chain, Dst, Val, ConstantSize->getZExtValue(),
       Size.getValueType(), Alignment, isVolatile, AlwaysInline, DstPtrInfo);
@@ -378,10 +384,19 @@ SDValue X86SelectionDAGInfo::EmitTargetCodeForMemcpy(
     SelectionDAG &DAG, const SDLoc &dl, SDValue Chain, SDValue Dst, SDValue 
Src,
     SDValue Size, Align Alignment, bool isVolatile, bool AlwaysInline,
     MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const {
+  const X86Subtarget &Subtarget =
+      DAG.getMachineFunction().getSubtarget<X86Subtarget>();
+
   // If to a segment-relative address space, use the default lowering.
   if (DstPtrInfo.getAddrSpace() >= 256 || SrcPtrInfo.getAddrSpace() >= 256)
     return SDValue();
 
+  // REP MOVS uses EDI/ESI on x86-32. Fall back if the user reserved either
+  // string-op register, so the generic expander can avoid emitting REP MOVS.
+  if (!Subtarget.is64Bit() && (Subtarget.isRegisterReservedByUser(X86::EDI) ||
+                               Subtarget.isRegisterReservedByUser(X86::ESI)))
+    return SDValue();
+
   // If the base registers conflict with our physical registers, use the 
default
   // lowering.
   const MCPhysReg ClobberSet[] = {X86::RCX, X86::RSI, X86::RDI,
@@ -389,9 +404,6 @@ SDValue X86SelectionDAGInfo::EmitTargetCodeForMemcpy(
   if (isBaseRegConflictPossible(DAG, ClobberSet))
     return SDValue();
 
-  const X86Subtarget &Subtarget =
-      DAG.getMachineFunction().getSubtarget<X86Subtarget>();
-
   // If enabled and available, use fast short rep mov.
   if (UseFSRMForMemcpy && Subtarget.hasFSRM())
     return emitRepmovs(Subtarget, DAG, dl, Chain, Dst, Src, Size, MVT::i8);
diff --git a/llvm/test/CodeGen/X86/reserveDISIreg.ll 
b/llvm/test/CodeGen/X86/reserveDISIreg.ll
new file mode 100644
index 0000000000000..0d44e37d6ab07
--- /dev/null
+++ b/llvm/test/CodeGen/X86/reserveDISIreg.ll
@@ -0,0 +1,63 @@
+;; Check if manually reserved EDI/ESI are always excluded from being saved by
+;; the function prolog/epilog, as per GCC behavior. REP MOVS should not be
+;; selected when either register is reserved, while REP STOS only depends on
+;; EDI.
+
+; RUN: llc < %s -mtriple=i386-unknown-linux-gnu -verify-machineinstrs | 
FileCheck %s
+
+declare void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly, ptr nocapture 
readonly, i32, i1 immarg)
+declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg)
+
+define void @tedi() "target-features"="+reserve-edi" {
+; CHECK-LABEL: tedi:
+; CHECK: # %bb.0:
+; CHECK-NOT:        pushl   %edi
+; CHECK-NEXT:        movl    $256, %edi
+; CHECK-NEXT:        #APP
+; CHECK-NEXT:        #NO_APP
+; CHECK-NOT:        popl    %edi
+; CHECK-NEXT:        retl
+  call i32 asm sideeffect "", "={edi},{edi}"(i32 256)
+  ret void
+}
+
+define void @tesi() "target-features"="+reserve-esi" {
+; CHECK-LABEL: tesi:
+; CHECK: # %bb.0:
+; CHECK-NOT:        pushl   %esi
+; CHECK-NEXT:        movl    $256, %esi
+; CHECK-NEXT:        #APP
+; CHECK-NEXT:        #NO_APP
+; CHECK-NOT:        popl    %esi
+; CHECK-NEXT:        retl
+  call i32 asm sideeffect "", "={esi},{esi}"(i32 256)
+  ret void
+}
+
+define void @copy_reserved_edi(ptr %dst, ptr %src) minsize 
"target-features"="+reserve-edi" {
+; CHECK-LABEL: copy_reserved_edi:
+; CHECK-NOT:        rep;movs
+  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %dst, ptr align 4 %src, i32 
128, i1 false)
+  ret void
+}
+
+define void @copy_reserved_esi(ptr %dst, ptr %src) minsize 
"target-features"="+reserve-esi" {
+; CHECK-LABEL: copy_reserved_esi:
+; CHECK-NOT:        rep;movs
+  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %dst, ptr align 4 %src, i32 
128, i1 false)
+  ret void
+}
+
+define void @set_reserved_edi(ptr %dst) minsize 
"target-features"="+reserve-edi" {
+; CHECK-LABEL: set_reserved_edi:
+; CHECK-NOT:        rep;stos
+  call void @llvm.memset.p0.i32(ptr align 4 %dst, i8 0, i32 128, i1 false)
+  ret void
+}
+
+define void @set_reserved_esi(ptr %dst) minsize 
"target-features"="+reserve-esi" {
+; CHECK-LABEL: set_reserved_esi:
+; CHECK:            rep;stosl
+  call void @llvm.memset.p0.i32(ptr align 4 %dst, i8 0, i32 128, i1 false)
+  ret void
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to