Win64: Don't emit unwind info for "leaf" functions (PR30337)

According to MSDN (see the PR), functions which don't touch any callee-saved
registers (including %rsp) don't need any unwind info.

This patch makes LLVM not emit unwind info for such functions, to save
binary size.

Differential Revision: https://reviews.llvm.org/D24748

llvm-svn: 282185
This commit is contained in:
Hans Wennborg 2016-09-22 19:50:05 +00:00
parent ba26d470c1
commit d1cad41742
9 changed files with 69 additions and 14 deletions

View File

@ -20,6 +20,7 @@
#include "llvm/ADT/BitVector.h" #include "llvm/ADT/BitVector.h"
#include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist.h"
#include "llvm/ADT/Optional.h"
#include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/IR/DebugLoc.h" #include "llvm/IR/DebugLoc.h"
@ -234,6 +235,9 @@ class MachineFunction {
/// True if the function includes any inline assembly. /// True if the function includes any inline assembly.
bool HasInlineAsm = false; bool HasInlineAsm = false;
/// True if any WinCFI instruction have been emitted in this function.
Optional<bool> HasWinCFI;
/// Current high-level properties of the IR of the function (e.g. is in SSA /// Current high-level properties of the IR of the function (e.g. is in SSA
/// form or whether registers have been allocated) /// form or whether registers have been allocated)
MachineFunctionProperties Properties; MachineFunctionProperties Properties;
@ -372,6 +376,12 @@ public:
HasInlineAsm = B; HasInlineAsm = B;
} }
bool hasWinCFI() const {
assert(HasWinCFI.hasValue() && "HasWinCFI not set yet!");
return *HasWinCFI;
}
void setHasWinCFI(bool v) { HasWinCFI = v; }
/// Get the function properties /// Get the function properties
const MachineFunctionProperties &getProperties() const { return Properties; } const MachineFunctionProperties &getProperties() const { return Properties; }
MachineFunctionProperties &getProperties() { return Properties; } MachineFunctionProperties &getProperties() { return Properties; }

View File

@ -90,7 +90,7 @@ void WinException::beginFunction(const MachineFunction *MF) {
// If we're not using CFI, we don't want the CFI or the personality, but we // If we're not using CFI, we don't want the CFI or the personality, but we
// might want EH tables if we had EH pads. // might want EH tables if we had EH pads.
if (!Asm->MAI->usesWindowsCFI()) { if (!Asm->MAI->usesWindowsCFI() || (!MF->hasWinCFI() && !Per)) {
shouldEmitLSDA = hasEHFunclets; shouldEmitLSDA = hasEHFunclets;
shouldEmitPersonality = false; shouldEmitPersonality = false;
return; return;

View File

@ -935,6 +935,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
STI.isTarget64BitILP32() STI.isTarget64BitILP32()
? getX86SubSuperRegister(FramePtr, 64) : FramePtr; ? getX86SubSuperRegister(FramePtr, 64) : FramePtr;
unsigned BasePtr = TRI->getBaseRegister(); unsigned BasePtr = TRI->getBaseRegister();
bool HasWinCFI = false;
// Debug location must be unknown since the first debug location is used // Debug location must be unknown since the first debug location is used
// to determine the end of the prologue. // to determine the end of the prologue.
@ -1063,6 +1064,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
} }
if (NeedsWinCFI) { if (NeedsWinCFI) {
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg)) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg))
.addImm(FramePtr) .addImm(FramePtr)
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
@ -1124,6 +1126,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
} }
if (NeedsWinCFI) { if (NeedsWinCFI) {
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg)).addImm(Reg).setMIFlag( BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg)).addImm(Reg).setMIFlag(
MachineInstr::FrameSetup); MachineInstr::FrameSetup);
} }
@ -1209,10 +1212,12 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
emitSPUpdate(MBB, MBBI, -(int64_t)NumBytes, /*InEpilogue=*/false); emitSPUpdate(MBB, MBBI, -(int64_t)NumBytes, /*InEpilogue=*/false);
} }
if (NeedsWinCFI && NumBytes) if (NeedsWinCFI && NumBytes) {
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_StackAlloc)) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_StackAlloc))
.addImm(NumBytes) .addImm(NumBytes)
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
}
int SEHFrameOffset = 0; int SEHFrameOffset = 0;
unsigned SPOrEstablisher; unsigned SPOrEstablisher;
@ -1259,6 +1264,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
// If this is not a funclet, emit the CFI describing our frame pointer. // If this is not a funclet, emit the CFI describing our frame pointer.
if (NeedsWinCFI && !IsFunclet) { if (NeedsWinCFI && !IsFunclet) {
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SetFrame)) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SetFrame))
.addImm(FramePtr) .addImm(FramePtr)
.addImm(SEHFrameOffset) .addImm(SEHFrameOffset)
@ -1295,6 +1301,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
int Offset = getFrameIndexReference(MF, FI, IgnoredFrameReg); int Offset = getFrameIndexReference(MF, FI, IgnoredFrameReg);
Offset += SEHFrameOffset; Offset += SEHFrameOffset;
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SaveXMM)) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SaveXMM))
.addImm(Reg) .addImm(Reg)
.addImm(Offset) .addImm(Offset)
@ -1304,7 +1311,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
} }
} }
if (NeedsWinCFI) if (NeedsWinCFI && HasWinCFI)
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_EndPrologue)) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_EndPrologue))
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
@ -1396,6 +1403,9 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
if (Fn->getCallingConv() == CallingConv::X86_INTR) if (Fn->getCallingConv() == CallingConv::X86_INTR)
BuildMI(MBB, MBBI, DL, TII.get(X86::CLD)) BuildMI(MBB, MBBI, DL, TII.get(X86::CLD))
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
// At this point we know if the function has WinCFI or not.
MF.setHasWinCFI(HasWinCFI);
} }
bool X86FrameLowering::canUseLEAForSPInEpilogue( bool X86FrameLowering::canUseLEAForSPInEpilogue(
@ -1630,7 +1640,7 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF,
// into the epilogue. To cope with that, we insert an epilogue marker here, // into the epilogue. To cope with that, we insert an epilogue marker here,
// then replace it with a 'nop' if it ends up immediately after a CALL in the // then replace it with a 'nop' if it ends up immediately after a CALL in the
// final emitted code. // final emitted code.
if (NeedsWinCFI) if (NeedsWinCFI && MF.hasWinCFI())
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_Epilogue)); BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_Epilogue));
if (!RetOpcode || !isTailCallOpcode(*RetOpcode)) { if (!RetOpcode || !isTailCallOpcode(*RetOpcode)) {

View File

@ -1420,37 +1420,45 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
return; return;
case X86::SEH_PushReg: case X86::SEH_PushReg:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFIPushReg(RI->getSEHRegNum(MI->getOperand(0).getImm())); OutStreamer->EmitWinCFIPushReg(RI->getSEHRegNum(MI->getOperand(0).getImm()));
return; return;
case X86::SEH_SaveReg: case X86::SEH_SaveReg:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFISaveReg(RI->getSEHRegNum(MI->getOperand(0).getImm()), OutStreamer->EmitWinCFISaveReg(RI->getSEHRegNum(MI->getOperand(0).getImm()),
MI->getOperand(1).getImm()); MI->getOperand(1).getImm());
return; return;
case X86::SEH_SaveXMM: case X86::SEH_SaveXMM:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFISaveXMM(RI->getSEHRegNum(MI->getOperand(0).getImm()), OutStreamer->EmitWinCFISaveXMM(RI->getSEHRegNum(MI->getOperand(0).getImm()),
MI->getOperand(1).getImm()); MI->getOperand(1).getImm());
return; return;
case X86::SEH_StackAlloc: case X86::SEH_StackAlloc:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFIAllocStack(MI->getOperand(0).getImm()); OutStreamer->EmitWinCFIAllocStack(MI->getOperand(0).getImm());
return; return;
case X86::SEH_SetFrame: case X86::SEH_SetFrame:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFISetFrame(RI->getSEHRegNum(MI->getOperand(0).getImm()), OutStreamer->EmitWinCFISetFrame(RI->getSEHRegNum(MI->getOperand(0).getImm()),
MI->getOperand(1).getImm()); MI->getOperand(1).getImm());
return; return;
case X86::SEH_PushFrame: case X86::SEH_PushFrame:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFIPushFrame(MI->getOperand(0).getImm()); OutStreamer->EmitWinCFIPushFrame(MI->getOperand(0).getImm());
return; return;
case X86::SEH_EndPrologue: case X86::SEH_EndPrologue:
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
OutStreamer->EmitWinCFIEndProlog(); OutStreamer->EmitWinCFIEndProlog();
return; return;
case X86::SEH_Epilogue: { case X86::SEH_Epilogue: {
assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?");
MachineBasicBlock::const_iterator MBBI(MI); MachineBasicBlock::const_iterator MBBI(MI);
// Check if preceded by a call and emit nop if so. // Check if preceded by a call and emit nop if so.
for (MBBI = PrevCrossBBInst(MBBI); for (MBBI = PrevCrossBBInst(MBBI);

View File

@ -11,6 +11,6 @@ entry:
} }
; CHECK-LABEL: test1{{$}} ; CHECK-LABEL: test1{{$}}
; CHECK: .seh_proc test1{{$}} ; CHECK-NOT: .seh_proc test1
; CHECK: rex64 jmpq *fnptr(%rip) ; CHECK: rex64 jmpq *fnptr(%rip)
; CHECK: .seh_endproc ; CHECK-NOT: .seh_endproc

View File

@ -31,7 +31,6 @@ define void @g() {
unreachable unreachable
} }
; CHECK-LABEL: g: ; CHECK-LABEL: g:
; CHECK: .seh_proc g ; CHECK: ud2
; CHECK: .seh_endproc
attributes #0 = { nounwind } attributes #0 = { nounwind }

View File

@ -171,10 +171,7 @@ entry:
} }
; CHECK: "?filt$0@0@main@@": # @"\01?filt$0@0@main@@" ; CHECK: "?filt$0@0@main@@": # @"\01?filt$0@0@main@@"
; CHECK: .seh_proc "?filt$0@0@main@@"
; CHECK: .seh_endprologue
; CHECK: jmp filt # TAILCALL ; CHECK: jmp filt # TAILCALL
; CHECK: .seh_handlerdata
declare i32 @filt() #1 declare i32 @filt() #1

View File

@ -8,10 +8,10 @@ entry:
ret void ret void
} }
; WIN64-LABEL: foo0: ; WIN64-LABEL: foo0:
; WIN64: .seh_proc foo0 ; WIN64-NOT: .seh_proc foo0
; WIN64: .seh_endprologue ; WIN64-NOT: .seh_endprologue
; WIN64: ret ; WIN64: ret
; WIN64: .seh_endproc ; WIN64-NOT: .seh_endproc
; Checks a small stack allocation ; Checks a small stack allocation
define void @foo1() uwtable { define void @foo1() uwtable {

View File

@ -0,0 +1,31 @@
; RUN: llc < %s -O1 -mtriple=x86_64-pc-win32 | FileCheck %s -check-prefix=ASM
; RUN: llc < %s -O1 -mtriple=x86_64-pc-win32 -filetype=obj -o %t
; RUN: llvm-readobj -unwind %t | FileCheck %s -check-prefix=READOBJ
declare void @g(i32)
define i32 @not_leaf(i32) uwtable {
entry:
call void @g(i32 42)
ret i32 42
; ASM-LABEL: not_leaf:
; ASM: .seh
; READOBJ: RuntimeFunction {
; READOBJ-NEXT: StartAddress: not_leaf
; READOBJ-NEXT: EndAddress: not_leaf
}
define void @leaf_func(i32) uwtable {
entry:
tail call void @g(i32 42)
ret void
; A Win64 "leaf" function gets no .seh directives in the asm.
; ASM-LABEL: leaf_func:
; ASM-NOT: .seh
; and no unwind info in the object file.
; READOBJ-NOT: leaf_func
}