diff options
| author | Oren Ben Simhon <oren.ben.simhon@intel.com> | 2017-05-03 13:07:19 +0000 |
|---|---|---|
| committer | Oren Ben Simhon <oren.ben.simhon@intel.com> | 2017-05-03 13:07:19 +0000 |
| commit | dbd4bba1ece18ccfbdec969ec0d05aed2e151d53 (patch) | |
| tree | a0cf943fc7adc5b8121c8436e0ed2355fc3db848 | |
| parent | ef5798acf5ea024f69f012abfbec1cdfe2e44cd0 (diff) | |
| download | bcm5719-llvm-dbd4bba1ece18ccfbdec969ec0d05aed2e151d53.tar.gz bcm5719-llvm-dbd4bba1ece18ccfbdec969ec0d05aed2e151d53.zip | |
[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
| -rw-r--r-- | llvm/lib/Target/X86/X86FastISel.cpp | 9 | ||||
| -rw-r--r-- | llvm/lib/Target/X86/X86ISelLowering.cpp | 30 | ||||
| -rw-r--r-- | llvm/lib/Target/X86/X86RegisterInfo.cpp | 9 | ||||
| -rw-r--r-- | llvm/test/CodeGen/X86/x86-32-intrcc.ll | 32 | ||||
| -rw-r--r-- | llvm/test/CodeGen/X86/x86-64-intrcc.ll | 51 | ||||
| -rw-r--r-- | llvm/test/CodeGen/X86/x86-no_caller_saved_registers-preserve.ll | 54 | ||||
| -rw-r--r-- | llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll | 31 |
7 files changed, 167 insertions, 49 deletions
diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index fd11b671dd9..ebd179e786d 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -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; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 25dd1b697af..19857c25d93 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -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. diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index 1f16f3c9a14..cf2ceef8013 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -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; diff --git a/llvm/test/CodeGen/X86/x86-32-intrcc.ll b/llvm/test/CodeGen/X86/x86-32-intrcc.ll index 9794f2cb3e4..ac0e7e11e0e 100644 --- a/llvm/test/CodeGen/X86/x86-32-intrcc.ll +++ b/llvm/test/CodeGen/X86/x86-32-intrcc.ll @@ -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 } diff --git a/llvm/test/CodeGen/X86/x86-64-intrcc.ll b/llvm/test/CodeGen/X86/x86-64-intrcc.ll index c8bc9e716ce..75ca1af79b3 100644 --- a/llvm/test/CodeGen/X86/x86-64-intrcc.ll +++ b/llvm/test/CodeGen/X86/x86-64-intrcc.ll @@ -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 } diff --git a/llvm/test/CodeGen/X86/x86-no_caller_saved_registers-preserve.ll b/llvm/test/CodeGen/X86/x86-no_caller_saved_registers-preserve.ll new file mode 100644 index 00000000000..7e370c25e31 --- /dev/null +++ b/llvm/test/CodeGen/X86/x86-no_caller_saved_registers-preserve.ll @@ -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" } diff --git a/llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll b/llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll new file mode 100644 index 00000000000..9c62e3ee6ba --- /dev/null +++ b/llvm/test/CodeGen/X86/x86-no_caller_saved_registers.ll @@ -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" }
|

