[X86] Support of no_caller_saved_registers attribute
This patch implements the LLVM part for no_caller_saved_registers attribute as appears here: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=5ed3cc7b66af4758f7849ed6f65f4365be8223be. In order to implement the attribute, we use the dynamic CSR mechanism to remove returned/passed arguments from the function regmask/CSR list. Differential Revision: https://reviews.llvm.org/D31876 llvm-svn: 302020
This commit is contained in:
parent
ef5798acf5
commit
dbd4bba1ec
@ -3181,6 +3181,15 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
|
||||
bool Is64Bit = Subtarget->is64Bit();
|
||||
bool IsWin64 = Subtarget->isCallingConvWin64(CC);
|
||||
|
||||
const CallInst *CI =
|
||||
CLI.CS ? dyn_cast<CallInst>(CLI.CS->getInstruction()) : nullptr;
|
||||
const Function *CalledFn = CI ? CI->getCalledFunction() : nullptr;
|
||||
|
||||
// Functions with no_caller_saved_registers that need special handling.
|
||||
if ((CI && CI->hasFnAttr("no_caller_saved_registers")) ||
|
||||
(CalledFn && CalledFn->hasFnAttribute("no_caller_saved_registers")))
|
||||
return false;
|
||||
|
||||
// Handle only C, fastcc, and webkit_js calling conventions for now.
|
||||
switch (CC) {
|
||||
default: return false;
|
||||
|
||||
@ -2180,6 +2180,12 @@ X86TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||||
MachineFunction &MF = DAG.getMachineFunction();
|
||||
X86MachineFunctionInfo *FuncInfo = MF.getInfo<X86MachineFunctionInfo>();
|
||||
|
||||
// In some cases we need to disable registers from the default CSR list.
|
||||
// For example, when they are used for argument passing.
|
||||
bool ShouldDisableCalleeSavedRegister =
|
||||
CallConv == CallingConv::X86_RegCall ||
|
||||
MF.getFunction()->hasFnAttribute("no_caller_saved_registers");
|
||||
|
||||
if (CallConv == CallingConv::X86_INTR && !Outs.empty())
|
||||
report_fatal_error("X86 interrupts may not return any value");
|
||||
|
||||
@ -2201,7 +2207,7 @@ X86TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||||
assert(VA.isRegLoc() && "Can only return in registers!");
|
||||
|
||||
// Add the register to the CalleeSaveDisableRegs list.
|
||||
if (CallConv == CallingConv::X86_RegCall)
|
||||
if (ShouldDisableCalleeSavedRegister)
|
||||
MF.getRegInfo().disableCalleeSavedRegister(VA.getLocReg());
|
||||
|
||||
SDValue ValToCopy = OutVals[OutsIndex];
|
||||
@ -2280,7 +2286,7 @@ X86TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||||
"Expecting two registers after Pass64BitArgInRegs");
|
||||
|
||||
// Add the second register to the CalleeSaveDisableRegs list.
|
||||
if (CallConv == CallingConv::X86_RegCall)
|
||||
if (ShouldDisableCalleeSavedRegister)
|
||||
MF.getRegInfo().disableCalleeSavedRegister(RVLocs[I].getLocReg());
|
||||
} else {
|
||||
RegsToPass.push_back(std::make_pair(VA.getLocReg(), ValToCopy));
|
||||
@ -2340,7 +2346,7 @@ X86TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||||
DAG.getRegister(RetValReg, getPointerTy(DAG.getDataLayout())));
|
||||
|
||||
// Add the returned register to the CalleeSaveDisableRegs list.
|
||||
if (CallConv == CallingConv::X86_RegCall)
|
||||
if (ShouldDisableCalleeSavedRegister)
|
||||
MF.getRegInfo().disableCalleeSavedRegister(RetValReg);
|
||||
}
|
||||
|
||||
@ -2540,7 +2546,7 @@ SDValue X86TargetLowering::LowerCallResult(
|
||||
|
||||
// In some calling conventions we need to remove the used registers
|
||||
// from the register mask.
|
||||
if (RegMask && CallConv == CallingConv::X86_RegCall) {
|
||||
if (RegMask) {
|
||||
for (MCSubRegIterator SubRegs(VA.getLocReg(), TRI, /*IncludeSelf=*/true);
|
||||
SubRegs.isValid(); ++SubRegs)
|
||||
RegMask[*SubRegs / 32] &= ~(1u << (*SubRegs % 32));
|
||||
@ -3237,7 +3243,8 @@ SDValue X86TargetLowering::LowerFormalArguments(
|
||||
}
|
||||
}
|
||||
|
||||
if (CallConv == CallingConv::X86_RegCall) {
|
||||
if (CallConv == CallingConv::X86_RegCall ||
|
||||
Fn->hasFnAttribute("no_caller_saved_registers")) {
|
||||
const MachineRegisterInfo &MRI = MF.getRegInfo();
|
||||
for (const auto &Pair : make_range(MRI.livein_begin(), MRI.livein_end()))
|
||||
MF.getRegInfo().disableCalleeSavedRegister(Pair.first);
|
||||
@ -3329,6 +3336,11 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
bool IsSibcall = false;
|
||||
X86MachineFunctionInfo *X86Info = MF.getInfo<X86MachineFunctionInfo>();
|
||||
auto Attr = MF.getFunction()->getFnAttribute("disable-tail-calls");
|
||||
const CallInst *CI =
|
||||
CLI.CS ? dyn_cast<CallInst>(CLI.CS->getInstruction()) : nullptr;
|
||||
const Function *Fn = CI ? CI->getCalledFunction() : nullptr;
|
||||
bool HasNCSR = (CI && CI->hasFnAttr("no_caller_saved_registers")) ||
|
||||
(Fn && Fn->hasFnAttribute("no_caller_saved_registers"));
|
||||
|
||||
if (CallConv == CallingConv::X86_INTR)
|
||||
report_fatal_error("X86 interrupts may not be called directly");
|
||||
@ -3741,7 +3753,11 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
RegsToPass[i].second.getValueType()));
|
||||
|
||||
// Add a register mask operand representing the call-preserved registers.
|
||||
const uint32_t *Mask = RegInfo->getCallPreservedMask(MF, CallConv);
|
||||
// If HasNCSR is asserted (attribute NoCallerSavedRegisters exists) then we
|
||||
// set X86_INTR calling convention because it has the same CSR mask
|
||||
// (same preserved registers).
|
||||
const uint32_t *Mask = RegInfo->getCallPreservedMask(
|
||||
MF, HasNCSR ? CallingConv::X86_INTR : CallConv);
|
||||
assert(Mask && "Missing call preserved mask for calling convention");
|
||||
|
||||
// If this is an invoke in a 32-bit function using a funclet-based
|
||||
@ -3764,7 +3780,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
|
||||
// In some calling conventions we need to remove the used physical registers
|
||||
// from the reg mask.
|
||||
if (CallConv == CallingConv::X86_RegCall) {
|
||||
if (CallConv == CallingConv::X86_RegCall || HasNCSR) {
|
||||
const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
|
||||
|
||||
// Allocate a new Reg Mask and copy Mask.
|
||||
|
||||
@ -276,7 +276,14 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
bool HasAVX512 = Subtarget.hasAVX512();
|
||||
bool CallsEHReturn = MF->callsEHReturn();
|
||||
|
||||
switch (MF->getFunction()->getCallingConv()) {
|
||||
CallingConv::ID CC = MF->getFunction()->getCallingConv();
|
||||
|
||||
// If attribute NoCallerSavedRegisters exists then we set X86_INTR calling
|
||||
// convention because it has the CSR list.
|
||||
if (MF->getFunction()->hasFnAttribute("no_caller_saved_registers"))
|
||||
CC = CallingConv::X86_INTR;
|
||||
|
||||
switch (CC) {
|
||||
case CallingConv::GHC:
|
||||
case CallingConv::HiPE:
|
||||
return CSR_NoRegs_SaveList;
|
||||
|
||||
@ -57,23 +57,23 @@ define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* %frame, i32 %eco
|
||||
define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* %frame, i32 %ecode) {
|
||||
call void asm sideeffect "", "~{eax},~{ebx},~{ebp}"()
|
||||
; CHECK-LABEL: test_isr_clobbers
|
||||
; CHECK-SSE-NEXT: pushl %ebp
|
||||
; CHECK-SSE-NEXT: pushl %ebx
|
||||
; CHECK-SSE-NEXT; pushl %eax
|
||||
; CHECK-SSE-NEXT: popl %eax
|
||||
; CHECK-SSE-NEXT: popl %ebx
|
||||
; CHECK-SSE-NEXT: popl %ebp
|
||||
; CHECK-SSE-NEXT: addl $4, %esp
|
||||
; CHECK-SSE-NEXT: iretl
|
||||
; CHECK: pushl %ebp
|
||||
; CHECK: pushl %ebx
|
||||
; CHECK: pushl %eax
|
||||
; CHECK: popl %eax
|
||||
; CHECK: popl %ebx
|
||||
; CHECK: popl %ebp
|
||||
; CHECK: addl $4, %esp
|
||||
; CHECK: iretl
|
||||
; CHECK0-LABEL: test_isr_clobbers
|
||||
; CHECK0-SSE-NEXT: pushl %ebp
|
||||
; CHECK0-SSE-NEXT: pushl %ebx
|
||||
; CHECK0-SSE-NEXT; pushl %eax
|
||||
; CHECK0-SSE-NEXT: popl %eax
|
||||
; CHECK0-SSE-NEXT: popl %ebx
|
||||
; CHECK0-SSE-NEXT: popl %ebp
|
||||
; CHECK0-SSE-NEXT: addl $4, %esp
|
||||
; CHECK0-SSE-NEXT: iretl
|
||||
; CHECK0: pushl %ebp
|
||||
; CHECK0: pushl %ebx
|
||||
; CHECK0: pushl %eax
|
||||
; CHECK0: popl %eax
|
||||
; CHECK0: popl %ebx
|
||||
; CHECK0: popl %ebp
|
||||
; CHECK0: addl $4, %esp
|
||||
; CHECK0: iretl
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
@ -59,32 +59,33 @@ define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* %frame, i64 %eco
|
||||
define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* %frame, i64 %ecode) {
|
||||
call void asm sideeffect "", "~{rax},~{rbx},~{rbp},~{r11},~{xmm0}"()
|
||||
; CHECK-LABEL: test_isr_clobbers
|
||||
; CHECK-SSE-NEXT: pushq %rax
|
||||
; CHECK-SSE-NEXT: pushq %rax
|
||||
; CHECK-SSE-NEXT; pushq %r11
|
||||
; CHECK-SSE-NEXT: pushq %rbp
|
||||
; CHECK-SSE-NEXT: pushq %rbx
|
||||
; CHECK-SSE-NEXT: movaps %xmm0
|
||||
; CHECK-SSE-NEXT: movaps %xmm0
|
||||
; CHECK-SSE-NEXT: popq %rbx
|
||||
; CHECK-SSE-NEXT: popq %rbp
|
||||
; CHECK-SSE-NEXT: popq %r11
|
||||
; CHECK-SSE-NEXT: popq %rax
|
||||
; CHECK-SSE-NEXT: addq $8, %rsp
|
||||
; CHECK-SSE-NEXT: iretq
|
||||
|
||||
; CHECK: pushq %rax
|
||||
; CHECK: pushq %rbp
|
||||
; CHECK: pushq %r11
|
||||
; CHECK: pushq %rbx
|
||||
; CHECK: movaps %xmm0
|
||||
; CHECK: movaps {{.*}}, %xmm0
|
||||
; CHECK: popq %rbx
|
||||
; CHECK: popq %r11
|
||||
; CHECK: popq %rbp
|
||||
; CHECK: popq %rax
|
||||
; CHECK: addq $16, %rsp
|
||||
; CHECK: iretq
|
||||
; CHECK0-LABEL: test_isr_clobbers
|
||||
; CHECK0-SSE-NEXT: pushq %rax
|
||||
; CHECK0-SSE-NEXT; pushq %r11
|
||||
; CHECK0-SSE-NEXT: pushq %rbp
|
||||
; CHECK0-SSE-NEXT: pushq %rbx
|
||||
; CHECK0-SSE-NEXT: movaps %xmm0
|
||||
; CHECK0-SSE-NEXT: movaps %xmm0
|
||||
; CHECK0-SSE-NEXT: popq %rbx
|
||||
; CHECK0-SSE-NEXT: popq %rbp
|
||||
; CHECK0-SSE-NEXT: popq %r11
|
||||
; CHECK0-SSE-NEXT: popq %rax
|
||||
; CHECK0-SSE-NEXT: addq $16, %rsp
|
||||
; CHECK0-SSE-NEXT: iretq
|
||||
|
||||
; CHECK0: pushq %rax
|
||||
; CHECK0: pushq %rbp
|
||||
; CHECK0: pushq %r11
|
||||
; CHECK0: pushq %rbx
|
||||
; CHECK0: movaps %xmm0
|
||||
; CHECK0: movaps {{.*}}, %xmm0
|
||||
; CHECK0: popq %rbx
|
||||
; CHECK0: popq %r11
|
||||
; CHECK0: popq %rbp
|
||||
; CHECK0: popq %rax
|
||||
; CHECK0: addq $16, %rsp
|
||||
; CHECK0: iretq
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py for function "bar"
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown < %s | FileCheck %s
|
||||
|
||||
;; In functions with 'no_caller_saved_registers' attribute, all registers should
|
||||
;; be preserved except for registers used for passing/returning arguments.
|
||||
;; In the following function registers %RDI, %RSI and %XMM0 are used to store
|
||||
;; arguments %a0, %a1 and %b0 accordingally. The value is returned in %RAX.
|
||||
;; The above registers should not be preserved, however other registers
|
||||
;; (that are modified by the function) should be preserved (%RDX and %XMM1).
|
||||
define x86_64_sysvcc i32 @bar(i32 %a0, i32 %a1, float %b0) #0 {
|
||||
; CHECK-LABEL: bar:
|
||||
; CHECK: # BB#0:
|
||||
; CHECK-NEXT: pushq %rdx
|
||||
; CHECK-NEXT: .Lcfi0:
|
||||
; CHECK-NEXT: .cfi_def_cfa_offset 16
|
||||
; CHECK-NEXT: movaps %xmm1, -{{[0-9]+}}(%rsp) # 16-byte Spill
|
||||
; CHECK-NEXT: .Lcfi1:
|
||||
; CHECK-NEXT: .cfi_offset %rdx, -16
|
||||
; CHECK-NEXT: .Lcfi2:
|
||||
; CHECK-NEXT: .cfi_offset %xmm1, -32
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: movl $4, %eax
|
||||
; CHECK-NEXT: movaps -{{[0-9]+}}(%rsp), %xmm1 # 16-byte Reload
|
||||
; CHECK-NEXT: popq %rdx
|
||||
; CHECK-NEXT: retq
|
||||
call void asm sideeffect "", "~{rax},~{rdx},~{xmm1},~{rdi},~{rsi},~{xmm0}"()
|
||||
ret i32 4
|
||||
}
|
||||
|
||||
;; Because "bar" has 'no_caller_saved_registers' attribute, function "foo"
|
||||
;; doesn't need to preserve registers except for the arguments passed
|
||||
;; to "bar" (%ESI, %EDI and %XMM0).
|
||||
define x86_64_sysvcc float @foo(i32 %a0, i32 %a1, float %b0) {
|
||||
; CHECK-LABEL: foo
|
||||
; CHECK: movaps %xmm0, %xmm1
|
||||
; CHECK-NEXT: movl %esi, %ecx
|
||||
; CHECK-NEXT: movl %edi, %edx
|
||||
; CHECK-NEXT: callq bar
|
||||
; CHECK-NEXT: addl %edx, %eax
|
||||
; CHECK-NEXT: addl %ecx, %eax
|
||||
; CHECK-NEXT: xorps %xmm0, %xmm0
|
||||
; CHECK-NEXT: cvtsi2ssl %eax, %xmm0
|
||||
; CHECK-NEXT: addss %xmm0, %xmm1
|
||||
; CHECK: retq
|
||||
%call = call i32 @bar(i32 %a0, i32 %a1, float %b0) #0
|
||||
%c0 = add i32 %a0, %call
|
||||
%c1 = add i32 %c0, %a1
|
||||
%c2 = sitofp i32 %c1 to float
|
||||
%c3 = fadd float %c2, %b0
|
||||
ret float %c3
|
||||
}
|
||||
|
||||
attributes #0 = { "no_caller_saved_registers" }
|
||||
31
llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll
Normal file
31
llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll
Normal file
@ -0,0 +1,31 @@
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown < %s | FileCheck %s
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown -O0 < %s | FileCheck %s
|
||||
; RUN: llc -mtriple=i686-unknown-unknown -mattr=+sse2 < %s | FileCheck %s
|
||||
; RUN: llc -mtriple=i686-unknown-unknown -mattr=+sse2 -O0 < %s | FileCheck %s
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; In functions with 'no_caller_saved_registers' attribute, all registers should
|
||||
;; be preserved except for registers used for passing/returning arguments.
|
||||
;; The test checks that function "bar" preserves xmm0 register.
|
||||
;; It also checks that caller function "foo" does not store registers for callee
|
||||
;; "bar". For example, there is no store/load/access to xmm registers.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
define i32 @bar(i32 %a0, i32 %a1, i32 %a2, i32 %a3, i32 %a4, i32 %a5, i32 %a6, i32 %a7, i32 %a8) #0 {
|
||||
; CHECK-LABEL: bar
|
||||
; CHECK: mov{{.*}} %xmm0
|
||||
; CHECK: mov{{.*}} {{.*}}, %xmm0
|
||||
; CHECK: ret
|
||||
call void asm sideeffect "", "~{xmm0}"()
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
define x86_intrcc void @foo(i8* nocapture readnone %c) {
|
||||
; CHECK-LABEL: foo
|
||||
; CHECK-NOT: xmm
|
||||
entry:
|
||||
tail call i32 @bar(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8) #0
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "no_caller_saved_registers" }
|
||||
Loading…
x
Reference in New Issue
Block a user