llvm-project/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp
Martin Storsjö 6f792041a5 Reapply "[CodeGen] [WinException] Only produce handler data at the end of the function if needed"
This reapplies 36c64af9d7f97414d48681b74352c9684077259b in updated
form.

Emit the xdata for each function at .seh_endproc. This keeps the
exact same output header order for most code generated by the LLVM
CodeGen layer. (Sections still change order for code built from
assembly where functions lack an explicit .seh_handlerdata
directive, and functions with chained unwind info.)

The practical effect should be that assembly output lacks
superfluous ".seh_handlerdata; .text" pairs at the end of functions
that don't handle exceptions, which allows such functions to use
the AArch64 packed unwind format again.

Differential Revision: https://reviews.llvm.org/D87448
2020-11-23 23:17:03 +02:00

237 lines
8.3 KiB
C++

//===-- AArch64WinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "AArch64WinCOFFStreamer.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCWin64EH.h"
#include "llvm/MC/MCWinCOFFStreamer.h"
using namespace llvm;
namespace {
class AArch64WinCOFFStreamer : public MCWinCOFFStreamer {
Win64EH::ARM64UnwindEmitter EHStreamer;
public:
AArch64WinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
std::unique_ptr<MCCodeEmitter> CE,
std::unique_ptr<MCObjectWriter> OW)
: MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
void EmitWinEHHandlerData(SMLoc Loc) override;
void EmitWindowsUnwindTables() override;
void EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
void finishImpl() override;
};
void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) {
MCStreamer::EmitWinEHHandlerData(Loc);
// We have to emit the unwind info now, because this directive
// actually switches to the .xdata section!
EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
/* HandlerData = */ true);
}
void AArch64WinCOFFStreamer::EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
}
void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() {
if (!getNumWinFrameInfos())
return;
EHStreamer.Emit(*this);
}
void AArch64WinCOFFStreamer::finishImpl() {
emitFrames(nullptr);
EmitWindowsUnwindTables();
MCWinCOFFStreamer::finishImpl();
}
} // end anonymous namespace
namespace llvm {
// Helper function to common out unwind code setup for those codes that can
// belong to both prolog and epilog.
// There are three types of Windows ARM64 SEH codes. They can
// 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd
// 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X
// 3) take a register and an offset/size: all others
void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode,
int Reg,
int Offset) {
auto &S = getStreamer();
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
if (!CurFrame)
return;
MCSymbol *Label = S.emitCFILabel();
auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
if (InEpilogCFI)
CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
else
CurFrame->Instructions.push_back(Inst);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) {
unsigned Op = Win64EH::UOP_AllocSmall;
if (Size >= 16384)
Op = Win64EH::UOP_AllocLarge;
else if (Size >= 512)
Op = Win64EH::UOP_AllocMedium;
EmitARM64WinUnwindCode(Op, -1, Size);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveR19R20X(int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg,
int Offset) {
assert(Offset >= 0 && Offset <= 504 &&
"Offset for save reg should be >= 0 && <= 504");
EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveLRPair(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg,
int Offset) {
assert(Offset >= 0 && Offset <= 504 &&
"Offset for save reg should be >= 0 && <= 504");
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg,
int Offset) {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() {
EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) {
assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() {
EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveNext() {
EmitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0);
}
// The functions below handle opcodes that can end up in either a prolog or
// an epilog, but not both.
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() {
auto &S = getStreamer();
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
if (!CurFrame)
return;
MCSymbol *Label = S.emitCFILabel();
CurFrame->PrologEnd = Label;
WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
auto it = CurFrame->Instructions.begin();
CurFrame->Instructions.insert(it, Inst);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() {
auto &S = getStreamer();
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
if (!CurFrame)
return;
InEpilogCFI = true;
CurrentEpilog = S.emitCFILabel();
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() {
auto &S = getStreamer();
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
if (!CurFrame)
return;
InEpilogCFI = false;
MCSymbol *Label = S.emitCFILabel();
WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
CurrentEpilog = nullptr;
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFITrapFrame() {
EmitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIMachineFrame() {
EmitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIContext() {
EmitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
}
void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIClearUnwoundToCall() {
EmitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
}
MCWinCOFFStreamer *createAArch64WinCOFFStreamer(
MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
bool RelaxAll, bool IncrementalLinkerCompatible) {
auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
std::move(Emitter), std::move(OW));
S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
return S;
}
} // end llvm namespace