https://github.com/zhouguangyuan0718 updated https://github.com/llvm/llvm-project/pull/186123
>From 2d76a30d94655cf653d6fa74ab3632f57b3bb0ab 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 on x86-32 Add clang driver support for -ffixed-edi and map it to the reserve-edi target feature on i386. Teach the X86 backend to treat EDI as a user-reserved register in register lookup, reserved-register tracking, and callee-save handling, and avoid selecting REP MOVS/REP STOS when EDI is reserved. Add driver, Sema, and codegen tests covering option handling, named global register variables, and the resulting code generation changes. Signed-off-by: ZhouGuangyuan <[email protected]> --- clang/include/clang/Options/Options.td | 2 ++ clang/lib/Basic/Targets/X86.h | 28 +++++++++++++++- clang/lib/Driver/ToolChains/Arch/X86.cpp | 7 ++++ clang/test/Driver/x86-fixed-di-register.c | 6 ++++ clang/test/Sema/x86-fixed-global-register.c | 36 +++++++++++++++++++++ llvm/lib/Target/X86/X86.td | 2 ++ llvm/lib/Target/X86/X86FrameLowering.cpp | 2 +- llvm/lib/Target/X86/X86ISelLowering.cpp | 8 ++--- llvm/lib/Target/X86/X86RegisterInfo.cpp | 22 ++++++++----- llvm/lib/Target/X86/X86SelectionDAGInfo.cpp | 21 +++++++++--- llvm/test/CodeGen/X86/reserveDIreg.ll | 35 ++++++++++++++++++++ 11 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 clang/test/Driver/x86-fixed-di-register.c create mode 100644 clang/test/Sema/x86-fixed-global-register.c create mode 100644 llvm/test/CodeGen/X86/reserveDIreg.ll diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 4bbdd9c8c0e58..b946e0a9bfe52 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -7175,6 +7175,8 @@ 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)">; 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..20f6814487d52 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -256,6 +256,18 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { HasSizeMismatch = RegSize != 32; return true; } + if (RegName.contains("di")) { + if (getTargetOpts().FeatureMap.lookup("reserve-edi")) { + if (RegName == "edi") { + HasSizeMismatch = RegSize != 32; + } else if (RegName == "di") { + HasSizeMismatch = RegSize != 16; + } else if (RegName == "dil" || RegName == "dih") { + HasSizeMismatch = RegSize != 8; + } + return true; + } + } return false; } @@ -814,8 +826,22 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo { if (Reg64.back() == 'd' || Reg64.back() == 'w' || Reg64.back() == 'b') { Reg64 = Reg64.substr(0, Reg64.size() - 1); } - if (getTargetOpts().FeatureMap.lookup(("reserve-" + Reg64).str())) + if (getTargetOpts().FeatureMap.lookup(("reserve-" + Reg64).str())) { + switch (RegName.back()) { + case 'd': + HasSizeMismatch = RegSize != 32; + break; + case 'w': + HasSizeMismatch = RegSize != 16; + break; + case 'b': + HasSizeMismatch = RegSize != 8; + break; + default: + HasSizeMismatch = RegSize != 64; + } return true; + } // Check if the register is a 32-bit register the backend can handle. return X86TargetInfo::validateGlobalRegisterVariable(RegName, RegSize, diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp b/clang/lib/Driver/ToolChains/Arch/X86.cpp index c113da6733370..b484d6f3c283b 100644 --- a/clang/lib/Driver/ToolChains/Arch/X86.cpp +++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp @@ -349,6 +349,13 @@ 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"); + } #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-register.c b/clang/test/Driver/x86-fixed-di-register.c new file mode 100644 index 0000000000000..bdae9f0684d0b --- /dev/null +++ b/clang/test/Driver/x86-fixed-di-register.c @@ -0,0 +1,6 @@ +// 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: 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' diff --git a/clang/test/Sema/x86-fixed-global-register.c b/clang/test/Sema/x86-fixed-global-register.c new file mode 100644 index 0000000000000..c1872a1d13ec0 --- /dev/null +++ b/clang/test/Sema/x86-fixed-global-register.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -DX64_NORESERVE -verify=common,x64_noreserve -fsyntax-only +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -DX64_RESERVE -target-feature +reserve-r11 -verify=common,x64_reserve -fsyntax-only +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu %s -DX86_NORESERVE -verify=common,x86_noreserve -fsyntax-only +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu %s -DX86_RESERVE -target-feature +reserve-edi -verify=common,x86_reserve -fsyntax-only + +#if defined(X64_NORESERVE) || defined(X64_RESERVE) +register long long x64_rsp_ok __asm__("rsp"); +register int x64_rsp_bad_size __asm__("rsp"); // common-error {{size of register 'rsp' does not match variable size}} +register long long x64_rbp_ok __asm__("rbp"); +#endif + +#ifdef X64_NORESERVE +register long long x64_r11_noreserve __asm__("r11"); // x64_noreserve-error {{register 'r11' unsuitable for global register variables on this target}} +#endif + +#ifdef X64_RESERVE +register long long x64_r11_ok __asm__("r11"); +register int x64_r11d_ok __asm__("r11d"); +register short x64_r11w_ok __asm__("r11w"); +register char x64_r11b_ok __asm__("r11b"); +#endif + +#if defined(X86_NORESERVE) || defined(X86_RESERVE) +register int x86_esp_ok __asm__("esp"); +register long long x86_esp_bad_size __asm__("esp"); // common-error {{size of register 'esp' does not match variable size}} +register int x86_ebp_ok __asm__("ebp"); +#endif + +#ifdef X86_NORESERVE +register int x86_edi_noreserve __asm__("edi"); // x86_noreserve-error {{register 'edi' unsuitable for global register variables on this target}} +#endif + +#ifdef X86_RESERVE +register int x86_edi_ok __asm__("edi"); +register char x86_edi_bad_size __asm__("edi"); // x86_reserve-error {{size of register 'edi' does not match variable size}} +#endif diff --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td index eca763735c315..25592e47148bf 100644 --- a/llvm/lib/Target/X86/X86.td +++ b/llvm/lib/Target/X86/X86.td @@ -38,6 +38,8 @@ 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 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..83dd6ea287e83 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -516,17 +516,23 @@ 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 { + if (ST.isRegisterReservedByUser(X86::EDI)) + for (const MCPhysReg &SubReg : sub_and_superregs_inclusive(X86::EDI)) + 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..dedae127476cf 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,18 @@ 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 only when EDI is + // reserved so the generic expander can avoid emitting REP MOVS. + if (!Subtarget.is64Bit() && Subtarget.isRegisterReservedByUser(X86::EDI)) + 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 +403,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/reserveDIreg.ll b/llvm/test/CodeGen/X86/reserveDIreg.ll new file mode 100644 index 0000000000000..cd5625071f968 --- /dev/null +++ b/llvm/test/CodeGen/X86/reserveDIreg.ll @@ -0,0 +1,35 @@ +;; Check if manually reserved EDI is always excluded from being saved by the +;; function prolog/epilog, as per GCC behavior, and that REP MOVS/STOS are not +;; selected when EDI is reserved on x86-32. + +; 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 @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 @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 +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
