10b57cec5SDimitry Andric //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "ARMMCTargetDesc.h" 100b57cec5SDimitry Andric #include "llvm/MC/MCAsmBackend.h" 1181ad6265SDimitry Andric #include "llvm/MC/MCAssembler.h" 120b57cec5SDimitry Andric #include "llvm/MC/MCCodeEmitter.h" 1381ad6265SDimitry Andric #include "llvm/MC/MCContext.h" 140b57cec5SDimitry Andric #include "llvm/MC/MCObjectWriter.h" 1581ad6265SDimitry Andric #include "llvm/MC/MCWin64EH.h" 160b57cec5SDimitry Andric #include "llvm/MC/MCWinCOFFStreamer.h" 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric using namespace llvm; 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric namespace { 210b57cec5SDimitry Andric class ARMWinCOFFStreamer : public MCWinCOFFStreamer { 2281ad6265SDimitry Andric Win64EH::ARMUnwindEmitter EHStreamer; 2381ad6265SDimitry Andric 240b57cec5SDimitry Andric public: 250b57cec5SDimitry Andric ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, 260b57cec5SDimitry Andric std::unique_ptr<MCCodeEmitter> CE, 270b57cec5SDimitry Andric std::unique_ptr<MCObjectWriter> OW) 280b57cec5SDimitry Andric : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} 290b57cec5SDimitry Andric 3081ad6265SDimitry Andric void emitWinEHHandlerData(SMLoc Loc) override; 3181ad6265SDimitry Andric void emitWindowsUnwindTables() override; 3281ad6265SDimitry Andric void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override; 3381ad6265SDimitry Andric 345ffd83dbSDimitry Andric void emitThumbFunc(MCSymbol *Symbol) override; 355ffd83dbSDimitry Andric void finishImpl() override; 360b57cec5SDimitry Andric }; 370b57cec5SDimitry Andric 3881ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { 3981ad6265SDimitry Andric MCStreamer::emitWinEHHandlerData(Loc); 4081ad6265SDimitry Andric 4181ad6265SDimitry Andric // We have to emit the unwind info now, because this directive 4281ad6265SDimitry Andric // actually switches to the .xdata section! 4381ad6265SDimitry Andric EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(), 4481ad6265SDimitry Andric /* HandlerData = */ true); 4581ad6265SDimitry Andric } 4681ad6265SDimitry Andric 4781ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { 4881ad6265SDimitry Andric EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false); 4981ad6265SDimitry Andric } 5081ad6265SDimitry Andric 5181ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWindowsUnwindTables() { 5281ad6265SDimitry Andric if (!getNumWinFrameInfos()) 5381ad6265SDimitry Andric return; 5481ad6265SDimitry Andric EHStreamer.Emit(*this); 5581ad6265SDimitry Andric } 5681ad6265SDimitry Andric 575ffd83dbSDimitry Andric void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) { 580b57cec5SDimitry Andric getAssembler().setIsThumbFunc(Symbol); 590b57cec5SDimitry Andric } 600b57cec5SDimitry Andric 615ffd83dbSDimitry Andric void ARMWinCOFFStreamer::finishImpl() { 625ffd83dbSDimitry Andric emitFrames(nullptr); 6381ad6265SDimitry Andric emitWindowsUnwindTables(); 640b57cec5SDimitry Andric 655ffd83dbSDimitry Andric MCWinCOFFStreamer::finishImpl(); 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 69*0fca6ea1SDimitry Andric MCStreamer * 70*0fca6ea1SDimitry Andric llvm::createARMWinCOFFStreamer(MCContext &Context, 71*0fca6ea1SDimitry Andric std::unique_ptr<MCAsmBackend> &&MAB, 720b57cec5SDimitry Andric std::unique_ptr<MCObjectWriter> &&OW, 73*0fca6ea1SDimitry Andric std::unique_ptr<MCCodeEmitter> &&Emitter) { 74*0fca6ea1SDimitry Andric return new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter), 750b57cec5SDimitry Andric std::move(OW)); 760b57cec5SDimitry Andric } 770b57cec5SDimitry Andric 7881ad6265SDimitry Andric namespace { 7981ad6265SDimitry Andric class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer { 8081ad6265SDimitry Andric private: 8181ad6265SDimitry Andric // True if we are processing SEH directives in an epilogue. 8281ad6265SDimitry Andric bool InEpilogCFI = false; 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric // Symbol of the current epilog for which we are processing SEH directives. 8581ad6265SDimitry Andric MCSymbol *CurrentEpilog = nullptr; 8681ad6265SDimitry Andric 8781ad6265SDimitry Andric public: 8881ad6265SDimitry Andric ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {} 8981ad6265SDimitry Andric 9081ad6265SDimitry Andric // The unwind codes on ARM Windows are documented at 9181ad6265SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling 9281ad6265SDimitry Andric void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override; 9381ad6265SDimitry Andric void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override; 9481ad6265SDimitry Andric void emitARMWinCFISaveSP(unsigned Reg) override; 9581ad6265SDimitry Andric void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override; 9681ad6265SDimitry Andric void emitARMWinCFISaveLR(unsigned Offset) override; 9781ad6265SDimitry Andric void emitARMWinCFIPrologEnd(bool Fragment) override; 9881ad6265SDimitry Andric void emitARMWinCFINop(bool Wide) override; 9981ad6265SDimitry Andric void emitARMWinCFIEpilogStart(unsigned Condition) override; 10081ad6265SDimitry Andric void emitARMWinCFIEpilogEnd() override; 10181ad6265SDimitry Andric void emitARMWinCFICustom(unsigned Opcode) override; 10281ad6265SDimitry Andric 10381ad6265SDimitry Andric private: 10481ad6265SDimitry Andric void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset); 10581ad6265SDimitry Andric }; 10681ad6265SDimitry Andric 10781ad6265SDimitry Andric // Helper function to common out unwind code setup for those codes that can 10881ad6265SDimitry Andric // belong to both prolog and epilog. 10981ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode, 11081ad6265SDimitry Andric int Reg, int Offset) { 11181ad6265SDimitry Andric auto &S = getStreamer(); 11281ad6265SDimitry Andric WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 11381ad6265SDimitry Andric if (!CurFrame) 11481ad6265SDimitry Andric return; 11581ad6265SDimitry Andric MCSymbol *Label = S.emitCFILabel(); 11681ad6265SDimitry Andric auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 11781ad6265SDimitry Andric if (InEpilogCFI) 11881ad6265SDimitry Andric CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 11981ad6265SDimitry Andric else 12081ad6265SDimitry Andric CurFrame->Instructions.push_back(Inst); 12181ad6265SDimitry Andric } 12281ad6265SDimitry Andric 12381ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size, 12481ad6265SDimitry Andric bool Wide) { 12581ad6265SDimitry Andric unsigned Op = Win64EH::UOP_AllocSmall; 12681ad6265SDimitry Andric if (!Wide) { 12781ad6265SDimitry Andric if (Size / 4 > 0xffff) 12881ad6265SDimitry Andric Op = Win64EH::UOP_AllocHuge; 12981ad6265SDimitry Andric else if (Size / 4 > 0x7f) 13081ad6265SDimitry Andric Op = Win64EH::UOP_AllocLarge; 13181ad6265SDimitry Andric } else { 13281ad6265SDimitry Andric Op = Win64EH::UOP_WideAllocMedium; 13381ad6265SDimitry Andric if (Size / 4 > 0xffff) 13481ad6265SDimitry Andric Op = Win64EH::UOP_WideAllocHuge; 13581ad6265SDimitry Andric else if (Size / 4 > 0x3ff) 13681ad6265SDimitry Andric Op = Win64EH::UOP_WideAllocLarge; 13781ad6265SDimitry Andric } 13881ad6265SDimitry Andric emitARMWinUnwindCode(Op, -1, Size); 13981ad6265SDimitry Andric } 14081ad6265SDimitry Andric 14181ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask, 14281ad6265SDimitry Andric bool Wide) { 14381ad6265SDimitry Andric assert(Mask != 0); 14481ad6265SDimitry Andric int Lr = (Mask & 0x4000) ? 1 : 0; 14581ad6265SDimitry Andric Mask &= ~0x4000; 14681ad6265SDimitry Andric if (Wide) 14781ad6265SDimitry Andric assert((Mask & ~0x1fff) == 0); 14881ad6265SDimitry Andric else 14981ad6265SDimitry Andric assert((Mask & ~0x00ff) == 0); 15081ad6265SDimitry Andric if (Mask && ((Mask + (1 << 4)) & Mask) == 0) { 15181ad6265SDimitry Andric if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) { 15281ad6265SDimitry Andric // One continuous range from r4 to r8-r11 15381ad6265SDimitry Andric for (int I = 11; I >= 8; I--) { 15481ad6265SDimitry Andric if (Mask & (1 << I)) { 15581ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr); 15681ad6265SDimitry Andric return; 15781ad6265SDimitry Andric } 15881ad6265SDimitry Andric } 15981ad6265SDimitry Andric // If it actually was from r4 to r4-r7, continue below. 16081ad6265SDimitry Andric } else if (!Wide) { 16181ad6265SDimitry Andric // One continuous range from r4 to r4-r7 16281ad6265SDimitry Andric for (int I = 7; I >= 4; I--) { 16381ad6265SDimitry Andric if (Mask & (1 << I)) { 16481ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr); 16581ad6265SDimitry Andric return; 16681ad6265SDimitry Andric } 16781ad6265SDimitry Andric } 16881ad6265SDimitry Andric llvm_unreachable("logic error"); 16981ad6265SDimitry Andric } 17081ad6265SDimitry Andric } 17181ad6265SDimitry Andric Mask |= Lr << 14; 17281ad6265SDimitry Andric if (Wide) 17381ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0); 17481ad6265SDimitry Andric else 17581ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0); 17681ad6265SDimitry Andric } 17781ad6265SDimitry Andric 17881ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) { 17981ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0); 18081ad6265SDimitry Andric } 18181ad6265SDimitry Andric 18281ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First, 18381ad6265SDimitry Andric unsigned Last) { 18481ad6265SDimitry Andric assert(First <= Last); 18581ad6265SDimitry Andric assert(First >= 16 || Last < 16); 18681ad6265SDimitry Andric assert(First <= 31 && Last <= 31); 18781ad6265SDimitry Andric if (First == 8) 18881ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0); 18981ad6265SDimitry Andric else if (First <= 15) 19081ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last); 19181ad6265SDimitry Andric else 19281ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last); 19381ad6265SDimitry Andric } 19481ad6265SDimitry Andric 19581ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) { 19681ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset); 19781ad6265SDimitry Andric } 19881ad6265SDimitry Andric 19981ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) { 20081ad6265SDimitry Andric if (Wide) 20181ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0); 20281ad6265SDimitry Andric else 20381ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0); 20481ad6265SDimitry Andric } 20581ad6265SDimitry Andric 20681ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) { 20781ad6265SDimitry Andric auto &S = getStreamer(); 20881ad6265SDimitry Andric WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 20981ad6265SDimitry Andric if (!CurFrame) 21081ad6265SDimitry Andric return; 21181ad6265SDimitry Andric 21281ad6265SDimitry Andric MCSymbol *Label = S.emitCFILabel(); 21381ad6265SDimitry Andric CurFrame->PrologEnd = Label; 21481ad6265SDimitry Andric WinEH::Instruction Inst = 21581ad6265SDimitry Andric WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); 21681ad6265SDimitry Andric auto it = CurFrame->Instructions.begin(); 21781ad6265SDimitry Andric CurFrame->Instructions.insert(it, Inst); 21881ad6265SDimitry Andric CurFrame->Fragment = Fragment; 21981ad6265SDimitry Andric } 22081ad6265SDimitry Andric 22181ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) { 22281ad6265SDimitry Andric auto &S = getStreamer(); 22381ad6265SDimitry Andric WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 22481ad6265SDimitry Andric if (!CurFrame) 22581ad6265SDimitry Andric return; 22681ad6265SDimitry Andric 22781ad6265SDimitry Andric InEpilogCFI = true; 22881ad6265SDimitry Andric CurrentEpilog = S.emitCFILabel(); 22981ad6265SDimitry Andric CurFrame->EpilogMap[CurrentEpilog].Condition = Condition; 23081ad6265SDimitry Andric } 23181ad6265SDimitry Andric 23281ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() { 23381ad6265SDimitry Andric auto &S = getStreamer(); 23481ad6265SDimitry Andric WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 23581ad6265SDimitry Andric if (!CurFrame) 23681ad6265SDimitry Andric return; 23781ad6265SDimitry Andric 23881ad6265SDimitry Andric if (!CurrentEpilog) { 23981ad6265SDimitry Andric S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " + 24081ad6265SDimitry Andric CurFrame->Function->getName()); 24181ad6265SDimitry Andric return; 24281ad6265SDimitry Andric } 24381ad6265SDimitry Andric 24481ad6265SDimitry Andric std::vector<WinEH::Instruction> &Epilog = 24581ad6265SDimitry Andric CurFrame->EpilogMap[CurrentEpilog].Instructions; 24681ad6265SDimitry Andric 24781ad6265SDimitry Andric unsigned UnwindCode = Win64EH::UOP_End; 24881ad6265SDimitry Andric if (!Epilog.empty()) { 24981ad6265SDimitry Andric WinEH::Instruction EndInstr = Epilog.back(); 25081ad6265SDimitry Andric if (EndInstr.Operation == Win64EH::UOP_Nop) { 25181ad6265SDimitry Andric UnwindCode = Win64EH::UOP_EndNop; 25281ad6265SDimitry Andric Epilog.pop_back(); 25381ad6265SDimitry Andric } else if (EndInstr.Operation == Win64EH::UOP_WideNop) { 25481ad6265SDimitry Andric UnwindCode = Win64EH::UOP_WideEndNop; 25581ad6265SDimitry Andric Epilog.pop_back(); 25681ad6265SDimitry Andric } 25781ad6265SDimitry Andric } 25881ad6265SDimitry Andric 25981ad6265SDimitry Andric InEpilogCFI = false; 26081ad6265SDimitry Andric WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0); 26181ad6265SDimitry Andric CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 26281ad6265SDimitry Andric MCSymbol *Label = S.emitCFILabel(); 26381ad6265SDimitry Andric CurFrame->EpilogMap[CurrentEpilog].End = Label; 26481ad6265SDimitry Andric CurrentEpilog = nullptr; 26581ad6265SDimitry Andric } 26681ad6265SDimitry Andric 26781ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) { 26881ad6265SDimitry Andric emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode); 26981ad6265SDimitry Andric } 27081ad6265SDimitry Andric 27181ad6265SDimitry Andric } // end anonymous namespace 27281ad6265SDimitry Andric 27381ad6265SDimitry Andric MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) { 27481ad6265SDimitry Andric return new ARMTargetWinCOFFStreamer(S); 27581ad6265SDimitry Andric } 276