[X86] Support reserving EDI on x86-32 (#186123)
Which is under discussion in https://github.com/llvm/llvm-project/issues/179036. x86-64 support is added in https://github.com/llvm/llvm-project/pull/180242. Now add x86-32 support for reserving EDI via `-ffixed-edi` Update the X86 backend to respect those reservations in register handling, callee-save logic, and memcpy/memset lowering, and add driver/codegen tests. 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 <zhouguangyuan.xian@gmail.com>
This commit is contained in:
parent
495c518b96
commit
3a1d5b5b8c
@ -7214,6 +7214,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)">;
|
||||
|
||||
@ -257,6 +257,16 @@ public:
|
||||
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;
|
||||
}
|
||||
@ -815,8 +825,22 @@ public:
|
||||
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,
|
||||
|
||||
@ -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);
|
||||
|
||||
6
clang/test/Driver/x86-fixed-di-register.c
Normal file
6
clang/test/Driver/x86-fixed-di-register.c
Normal file
@ -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'
|
||||
36
clang/test/Sema/x86-fixed-global-register.c
Normal file
36
clang/test/Sema/x86-fixed-global-register.c
Normal file
@ -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
|
||||
@ -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">;
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -28834,11 +28834,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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -303,6 +303,8 @@ void X86Subtarget::initSubtargetFeatures(StringRef CPU, StringRef TuneCPU,
|
||||
PreferVectorWidth = 128;
|
||||
else if (Prefer256Bit)
|
||||
PreferVectorWidth = 256;
|
||||
|
||||
HasUserReservedRegisters = ReservedRReg.any();
|
||||
}
|
||||
|
||||
X86Subtarget &X86Subtarget::initializeSubtargetDependencies(StringRef CPU,
|
||||
|
||||
@ -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 @@ public:
|
||||
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
|
||||
|
||||
63
llvm/test/CodeGen/X86/reserveDIreg.ll
Normal file
63
llvm/test/CodeGen/X86/reserveDIreg.ll
Normal file
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user