277 lines
9 KiB
C++
277 lines
9 KiB
C++
//===-- ARMWinCOFFStreamer.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 "ARMMCTargetDesc.h"
|
|
#include "llvm/MC/MCAsmBackend.h"
|
|
#include "llvm/MC/MCAssembler.h"
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
#include "llvm/MC/MCWin64EH.h"
|
|
#include "llvm/MC/MCWinCOFFStreamer.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
|
|
Win64EH::ARMUnwindEmitter EHStreamer;
|
|
|
|
public:
|
|
ARMWinCOFFStreamer(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 emitThumbFunc(MCSymbol *Symbol) override;
|
|
void finishImpl() override;
|
|
};
|
|
|
|
void ARMWinCOFFStreamer::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 ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
|
|
EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
|
|
}
|
|
|
|
void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
|
|
if (!getNumWinFrameInfos())
|
|
return;
|
|
EHStreamer.Emit(*this);
|
|
}
|
|
|
|
void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
|
|
getAssembler().setIsThumbFunc(Symbol);
|
|
}
|
|
|
|
void ARMWinCOFFStreamer::finishImpl() {
|
|
emitFrames(nullptr);
|
|
emitWindowsUnwindTables();
|
|
|
|
MCWinCOFFStreamer::finishImpl();
|
|
}
|
|
}
|
|
|
|
MCStreamer *llvm::createARMWinCOFFStreamer(
|
|
MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
|
|
std::unique_ptr<MCObjectWriter> &&OW,
|
|
std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
|
|
bool IncrementalLinkerCompatible) {
|
|
auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
|
|
std::move(OW));
|
|
S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
|
|
return S;
|
|
}
|
|
|
|
namespace {
|
|
class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
|
|
private:
|
|
// True if we are processing SEH directives in an epilogue.
|
|
bool InEpilogCFI = false;
|
|
|
|
// Symbol of the current epilog for which we are processing SEH directives.
|
|
MCSymbol *CurrentEpilog = nullptr;
|
|
|
|
public:
|
|
ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
|
|
|
|
// The unwind codes on ARM Windows are documented at
|
|
// https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
|
|
void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
|
|
void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
|
|
void emitARMWinCFISaveSP(unsigned Reg) override;
|
|
void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
|
|
void emitARMWinCFISaveLR(unsigned Offset) override;
|
|
void emitARMWinCFIPrologEnd(bool Fragment) override;
|
|
void emitARMWinCFINop(bool Wide) override;
|
|
void emitARMWinCFIEpilogStart(unsigned Condition) override;
|
|
void emitARMWinCFIEpilogEnd() override;
|
|
void emitARMWinCFICustom(unsigned Opcode) override;
|
|
|
|
private:
|
|
void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
|
|
};
|
|
|
|
// Helper function to common out unwind code setup for those codes that can
|
|
// belong to both prolog and epilog.
|
|
void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(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].Instructions.push_back(Inst);
|
|
else
|
|
CurFrame->Instructions.push_back(Inst);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
|
|
bool Wide) {
|
|
unsigned Op = Win64EH::UOP_AllocSmall;
|
|
if (!Wide) {
|
|
if (Size / 4 > 0xffff)
|
|
Op = Win64EH::UOP_AllocHuge;
|
|
else if (Size / 4 > 0x7f)
|
|
Op = Win64EH::UOP_AllocLarge;
|
|
} else {
|
|
Op = Win64EH::UOP_WideAllocMedium;
|
|
if (Size / 4 > 0xffff)
|
|
Op = Win64EH::UOP_WideAllocHuge;
|
|
else if (Size / 4 > 0x3ff)
|
|
Op = Win64EH::UOP_WideAllocLarge;
|
|
}
|
|
emitARMWinUnwindCode(Op, -1, Size);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
|
|
bool Wide) {
|
|
assert(Mask != 0);
|
|
int Lr = (Mask & 0x4000) ? 1 : 0;
|
|
Mask &= ~0x4000;
|
|
if (Wide)
|
|
assert((Mask & ~0x1fff) == 0);
|
|
else
|
|
assert((Mask & ~0x00ff) == 0);
|
|
if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
|
|
if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
|
|
// One continuous range from r4 to r8-r11
|
|
for (int I = 11; I >= 8; I--) {
|
|
if (Mask & (1 << I)) {
|
|
emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
|
|
return;
|
|
}
|
|
}
|
|
// If it actually was from r4 to r4-r7, continue below.
|
|
} else if (!Wide) {
|
|
// One continuous range from r4 to r4-r7
|
|
for (int I = 7; I >= 4; I--) {
|
|
if (Mask & (1 << I)) {
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
|
|
return;
|
|
}
|
|
}
|
|
llvm_unreachable("logic error");
|
|
}
|
|
}
|
|
Mask |= Lr << 14;
|
|
if (Wide)
|
|
emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
|
|
else
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
|
|
unsigned Last) {
|
|
assert(First <= Last);
|
|
assert(First >= 16 || Last < 16);
|
|
assert(First <= 31 && Last <= 31);
|
|
if (First == 8)
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
|
|
else if (First <= 15)
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
|
|
else
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
|
|
emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
|
|
if (Wide)
|
|
emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
|
|
else
|
|
emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
|
|
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=*/nullptr, -1, 0);
|
|
auto it = CurFrame->Instructions.begin();
|
|
CurFrame->Instructions.insert(it, Inst);
|
|
CurFrame->Fragment = Fragment;
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
|
|
auto &S = getStreamer();
|
|
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
|
|
if (!CurFrame)
|
|
return;
|
|
|
|
InEpilogCFI = true;
|
|
CurrentEpilog = S.emitCFILabel();
|
|
CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
|
|
auto &S = getStreamer();
|
|
WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
|
|
if (!CurFrame)
|
|
return;
|
|
|
|
if (!CurrentEpilog) {
|
|
S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
|
|
CurFrame->Function->getName());
|
|
return;
|
|
}
|
|
|
|
std::vector<WinEH::Instruction> &Epilog =
|
|
CurFrame->EpilogMap[CurrentEpilog].Instructions;
|
|
|
|
unsigned UnwindCode = Win64EH::UOP_End;
|
|
if (!Epilog.empty()) {
|
|
WinEH::Instruction EndInstr = Epilog.back();
|
|
if (EndInstr.Operation == Win64EH::UOP_Nop) {
|
|
UnwindCode = Win64EH::UOP_EndNop;
|
|
Epilog.pop_back();
|
|
} else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
|
|
UnwindCode = Win64EH::UOP_WideEndNop;
|
|
Epilog.pop_back();
|
|
}
|
|
}
|
|
|
|
InEpilogCFI = false;
|
|
WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
|
|
CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
|
|
MCSymbol *Label = S.emitCFILabel();
|
|
CurFrame->EpilogMap[CurrentEpilog].End = Label;
|
|
CurrentEpilog = nullptr;
|
|
}
|
|
|
|
void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
|
|
emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
|
|
return new ARMTargetWinCOFFStreamer(S);
|
|
}
|