267 lines · cpp
1//===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ARMMCTargetDesc.h"10#include "llvm/MC/MCAsmBackend.h"11#include "llvm/MC/MCAssembler.h"12#include "llvm/MC/MCCodeEmitter.h"13#include "llvm/MC/MCContext.h"14#include "llvm/MC/MCObjectWriter.h"15#include "llvm/MC/MCWin64EH.h"16#include "llvm/MC/MCWinCOFFStreamer.h"17 18using namespace llvm;19 20namespace {21class ARMWinCOFFStreamer : public MCWinCOFFStreamer {22 Win64EH::ARMUnwindEmitter EHStreamer;23 24public:25 ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,26 std::unique_ptr<MCCodeEmitter> CE,27 std::unique_ptr<MCObjectWriter> OW)28 : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}29 30 void emitWinEHHandlerData(SMLoc Loc) override;31 void emitWindowsUnwindTables() override;32 void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;33 34 void finishImpl() override;35};36 37void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {38 MCStreamer::emitWinEHHandlerData(Loc);39 40 // We have to emit the unwind info now, because this directive41 // actually switches to the .xdata section!42 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),43 /* HandlerData = */ true);44}45 46void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {47 EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);48}49 50void ARMWinCOFFStreamer::emitWindowsUnwindTables() {51 if (!getNumWinFrameInfos())52 return;53 EHStreamer.Emit(*this);54}55 56void ARMWinCOFFStreamer::finishImpl() {57 emitFrames();58 emitWindowsUnwindTables();59 60 MCWinCOFFStreamer::finishImpl();61}62}63 64MCStreamer *65llvm::createARMWinCOFFStreamer(MCContext &Context,66 std::unique_ptr<MCAsmBackend> &&MAB,67 std::unique_ptr<MCObjectWriter> &&OW,68 std::unique_ptr<MCCodeEmitter> &&Emitter) {69 return new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),70 std::move(OW));71}72 73namespace {74class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {75public:76 ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}77 78 ARMWinCOFFStreamer &getStreamer() {79 return static_cast<ARMWinCOFFStreamer &>(Streamer);80 }81 void emitThumbFunc(MCSymbol *Symbol) override;82 83 // The unwind codes on ARM Windows are documented at84 // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling85 void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;86 void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;87 void emitARMWinCFISaveSP(unsigned Reg) override;88 void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;89 void emitARMWinCFISaveLR(unsigned Offset) override;90 void emitARMWinCFIPrologEnd(bool Fragment) override;91 void emitARMWinCFINop(bool Wide) override;92 void emitARMWinCFIEpilogStart(unsigned Condition) override;93 void emitARMWinCFIEpilogEnd() override;94 void emitARMWinCFICustom(unsigned Opcode) override;95 96private:97 void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);98};99 100void ARMTargetWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {101 getStreamer().getAssembler().setIsThumbFunc(Symbol);102}103 104// Helper function to common out unwind code setup for those codes that can105// belong to both prolog and epilog.106void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,107 int Reg, int Offset) {108 auto &S = getStreamer();109 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());110 if (!CurFrame)111 return;112 MCSymbol *Label = S.emitCFILabel();113 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);114 if (S.isInEpilogCFI())115 S.getCurrentWinEpilog()->Instructions.push_back(Inst);116 else117 CurFrame->Instructions.push_back(Inst);118}119 120void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,121 bool Wide) {122 unsigned Op = Win64EH::UOP_AllocSmall;123 if (!Wide) {124 if (Size / 4 > 0xffff)125 Op = Win64EH::UOP_AllocHuge;126 else if (Size / 4 > 0x7f)127 Op = Win64EH::UOP_AllocLarge;128 } else {129 Op = Win64EH::UOP_WideAllocMedium;130 if (Size / 4 > 0xffff)131 Op = Win64EH::UOP_WideAllocHuge;132 else if (Size / 4 > 0x3ff)133 Op = Win64EH::UOP_WideAllocLarge;134 }135 emitARMWinUnwindCode(Op, -1, Size);136}137 138void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,139 bool Wide) {140 assert(Mask != 0);141 int Lr = (Mask & 0x4000) ? 1 : 0;142 Mask &= ~0x4000;143 if (Wide)144 assert((Mask & ~0x1fff) == 0);145 else146 assert((Mask & ~0x00ff) == 0);147 if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {148 if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {149 // One continuous range from r4 to r8-r11150 for (int I = 11; I >= 8; I--) {151 if (Mask & (1 << I)) {152 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);153 return;154 }155 }156 // If it actually was from r4 to r4-r7, continue below.157 } else if (!Wide) {158 // One continuous range from r4 to r4-r7159 for (int I = 7; I >= 4; I--) {160 if (Mask & (1 << I)) {161 emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);162 return;163 }164 }165 llvm_unreachable("logic error");166 }167 }168 Mask |= Lr << 14;169 if (Wide)170 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);171 else172 emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);173}174 175void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {176 emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);177}178 179void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,180 unsigned Last) {181 assert(First <= Last);182 assert(First >= 16 || Last < 16);183 assert(First <= 31 && Last <= 31);184 if (First == 8)185 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);186 else if (First <= 15)187 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);188 else189 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);190}191 192void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {193 emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);194}195 196void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {197 if (Wide)198 emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);199 else200 emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);201}202 203void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {204 auto &S = getStreamer();205 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());206 if (!CurFrame)207 return;208 209 MCSymbol *Label = S.emitCFILabel();210 CurFrame->PrologEnd = Label;211 WinEH::Instruction Inst =212 WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);213 auto it = CurFrame->Instructions.begin();214 CurFrame->Instructions.insert(it, Inst);215 CurFrame->Fragment = Fragment;216}217 218void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {219 auto &S = getStreamer();220 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());221 if (!CurFrame)222 return;223 224 S.emitWinCFIBeginEpilogue();225 if (S.isInEpilogCFI()) {226 S.getCurrentWinEpilog()->Condition = Condition;227 }228}229 230void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {231 auto &S = getStreamer();232 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());233 if (!CurFrame)234 return;235 236 if (S.isInEpilogCFI()) {237 std::vector<WinEH::Instruction> &Epilog =238 S.getCurrentWinEpilog()->Instructions;239 240 unsigned UnwindCode = Win64EH::UOP_End;241 if (!Epilog.empty()) {242 WinEH::Instruction EndInstr = Epilog.back();243 if (EndInstr.Operation == Win64EH::UOP_Nop) {244 UnwindCode = Win64EH::UOP_EndNop;245 Epilog.pop_back();246 } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {247 UnwindCode = Win64EH::UOP_WideEndNop;248 Epilog.pop_back();249 }250 }251 252 WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);253 S.getCurrentWinEpilog()->Instructions.push_back(Inst);254 }255 S.emitWinCFIEndEpilogue();256}257 258void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {259 emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);260}261 262} // end anonymous namespace263 264MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {265 return new ARMTargetWinCOFFStreamer(S);266}267