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; def mno_apxf : Flag<["-"], "mno-apxf">, Group; def mapx_inline_asm_use_gpr32 : Flag<["-"], "mapx-inline-asm-use-gpr32">, Group, HelpText<"Enable use of GPR32 in inline assembly for APX">; +def ffixed_edi : Flag<["-"], "ffixed-edi">, Group, + HelpText<"Reserve the edi register (x86 only)">; foreach i = {8, 10-15} in def ffixed_r#i : Flag<["-"], "ffixed-r"#i>, 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..43ecf2689c968 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -256,6 +256,16 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { HasSizeMismatch = RegSize != 32; return true; } + if (RegName.ends_with("di")) { + if (getTargetOpts().FeatureMap.lookup("reserve-edi")) { + if (RegName == "edi") { + HasSizeMismatch = RegSize != 32; + } else if (RegName == "di") { + HasSizeMismatch = RegSize != 16; + } + return true; + } + } return false; } @@ -814,8 +824,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(); - 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(); + // 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(); 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(); + // 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(); - // 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/lib/Target/X86/X86Subtarget.cpp b/llvm/lib/Target/X86/X86Subtarget.cpp index 4e2e98410f325..1501ce2a47705 100644 --- a/llvm/lib/Target/X86/X86Subtarget.cpp +++ b/llvm/lib/Target/X86/X86Subtarget.cpp @@ -302,6 +302,8 @@ void X86Subtarget::initSubtargetFeatures(StringRef CPU, StringRef TuneCPU, PreferVectorWidth = 128; else if (Prefer256Bit) PreferVectorWidth = 256; + + HasUserReservedRegisters = ReservedRReg.any(); } X86Subtarget &X86Subtarget::initializeSubtargetDependencies(StringRef CPU, diff --git a/llvm/lib/Target/X86/X86Subtarget.h b/llvm/lib/Target/X86/X86Subtarget.h index 2e3c23eeca35b..692c7938ddc00 100644 --- a/llvm/lib/Target/X86/X86Subtarget.h +++ b/llvm/lib/Target/X86/X86Subtarget.h @@ -102,6 +102,8 @@ class X86Subtarget final : public X86GenSubtargetInfo { /// Required vector width from function attribute. unsigned RequiredVectorWidth; + bool HasUserReservedRegisters; + X86SelectionDAGInfo TSInfo; // Ordering here is important. X86InstrInfo initializes X86RegisterInfo which // X86TargetLowering needs. @@ -162,7 +164,7 @@ class X86Subtarget final : public X86GenSubtargetInfo { bool isRegisterReservedByUser(Register i) const override { return ReservedRReg[i.id()]; } - bool hasUserReservedRegisters() const { return ReservedRReg.any(); } + bool hasUserReservedRegisters() const { return HasUserReservedRegisters; } private: /// Initialize the full set of dependencies so we can use an initializer diff --git a/llvm/test/CodeGen/X86/reserveDIreg.ll b/llvm/test/CodeGen/X86/reserveDIreg.ll new file mode 100644 index 0000000000000..86ddf5795ca52 --- /dev/null +++ b/llvm/test/CodeGen/X86/reserveDIreg.ll @@ -0,0 +1,63 @@ +;; 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-NEXT: movl $256, %edi +; CHECK-NEXT: #APP +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: retl + call i32 asm sideeffect "", "={edi},{edi}"(i32 256) + ret void +} + +define void @no_reserve_edi() { +; CHECK-LABEL: no_reserve_edi: +; CHECK: # %bb.0: +; CHECK-NEXT: pushl %edi +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: .cfi_offset %edi, -8 +; CHECK-NEXT: movl $256, %edi +; CHECK-NEXT: #APP +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: popl %edi +; CHECK-NEXT: .cfi_def_cfa_offset 4 +; 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 +} + +define void @copy_no_reserved(ptr %dst, ptr %src) minsize { +; CHECK-LABEL: copy_no_reserved: +; CHECK: 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_no_reserved(ptr %dst) minsize { +; CHECK-LABEL: set_no_reserved: +; CHECK: rep;stos + call void @llvm.memset.p0.i32(ptr align 4 %dst, i8 0, i32 128, i1 false) + ret void +} \ No newline at end of file