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;
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");
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];
"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));
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);
}
// 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));
}
}
- 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);
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");
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
// 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.
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;
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
}
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
}
--- /dev/null
+; 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\r
+;; 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"\r
+;; doesn't need to preserve registers except for the arguments passed \r
+;; to "bar" (%ESI, %EDI and %XMM0).
+define x86_64_sysvcc float @foo(i32 %a0, i32 %a1, float %b0) {
+; CHECK-LABEL: foo\r
+; CHECK: movaps %xmm0, %xmm1\r
+; CHECK-NEXT: movl %esi, %ecx\r
+; CHECK-NEXT: movl %edi, %edx\r
+; CHECK-NEXT: callq bar\r
+; CHECK-NEXT: addl %edx, %eax\r
+; CHECK-NEXT: addl %ecx, %eax\r
+; CHECK-NEXT: xorps %xmm0, %xmm0\r
+; CHECK-NEXT: cvtsi2ssl %eax, %xmm0\r
+; CHECK-NEXT: addss %xmm0, %xmm1\r
+; 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" }
--- /dev/null
+; RUN: llc -mtriple=x86_64-unknown-unknown < %s | FileCheck %s\r
+; RUN: llc -mtriple=x86_64-unknown-unknown -O0 < %s | FileCheck %s\r
+; RUN: llc -mtriple=i686-unknown-unknown -mattr=+sse2 < %s | FileCheck %s\r
+; RUN: llc -mtriple=i686-unknown-unknown -mattr=+sse2 -O0 < %s | FileCheck %s\r
+\r
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r
+;; In functions with 'no_caller_saved_registers' attribute, all registers should\r
+;; be preserved except for registers used for passing/returning arguments.\r
+;; The test checks that function "bar" preserves xmm0 register.\r
+;; It also checks that caller function "foo" does not store registers for callee \r
+;; "bar". For example, there is no store/load/access to xmm registers.\r
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r
+\r
+define i32 @bar(i32 %a0, i32 %a1, i32 %a2, i32 %a3, i32 %a4, i32 %a5, i32 %a6, i32 %a7, i32 %a8) #0 {\r
+; CHECK-LABEL: bar\r
+; CHECK: mov{{.*}} %xmm0\r
+; CHECK: mov{{.*}} {{.*}}, %xmm0\r
+; CHECK: ret\r
+ call void asm sideeffect "", "~{xmm0}"()\r
+ ret i32 1\r
+}\r
+\r
+define x86_intrcc void @foo(i8* nocapture readnone %c) {\r
+; CHECK-LABEL: foo\r
+; CHECK-NOT: xmm\r
+entry:\r
+ tail call i32 @bar(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8) #0\r
+ ret void\r
+}\r
+\r
+attributes #0 = { "no_caller_saved_registers" }\r