1 //===-- AArch64WinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "AArch64WinCOFFStreamer.h" 11 #include "llvm/MC/MCAsmBackend.h" 12 #include "llvm/MC/MCCodeEmitter.h" 13 #include "llvm/MC/MCObjectWriter.h" 14 #include "llvm/MC/MCWin64EH.h" 15 #include "llvm/MC/MCWinCOFFStreamer.h" 16 17 using namespace llvm; 18 19 namespace { 20 21 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { 22 Win64EH::ARM64UnwindEmitter EHStreamer; 23 24 public: 25 AArch64WinCOFFStreamer(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 FinishImpl() override; 33 }; 34 35 void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { 36 MCStreamer::EmitWinEHHandlerData(Loc); 37 38 // We have to emit the unwind info now, because this directive 39 // actually switches to the .xdata section! 40 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo()); 41 } 42 43 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { 44 if (!getNumWinFrameInfos()) 45 return; 46 EHStreamer.Emit(*this); 47 } 48 49 void AArch64WinCOFFStreamer::FinishImpl() { 50 EmitFrames(nullptr); 51 EmitWindowsUnwindTables(); 52 53 MCWinCOFFStreamer::FinishImpl(); 54 } 55 } // end anonymous namespace 56 57 namespace llvm { 58 59 // Helper function to common out unwind code setup for those codes that can 60 // belong to both prolog and epilog. 61 // There are three types of Windows ARM64 SEH codes. They can 62 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd 63 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X 64 // 3) take a register and an offset/size: all others 65 void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode, 66 int Reg, 67 int Offset) { 68 auto &S = getStreamer(); 69 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 70 if (!CurFrame) 71 return; 72 MCSymbol *Label = S.EmitCFILabel(); 73 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 74 if (InEpilogCFI) 75 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 76 else 77 CurFrame->Instructions.push_back(Inst); 78 } 79 80 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { 81 unsigned Op = Win64EH::UOP_AllocSmall; 82 if (Size >= 16384) 83 Op = Win64EH::UOP_AllocLarge; 84 else if (Size >= 512) 85 Op = Win64EH::UOP_AllocMedium; 86 EmitARM64WinUnwindCode(Op, -1, Size); 87 } 88 89 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { 90 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); 91 } 92 93 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { 94 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); 95 } 96 97 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, 98 int Offset) { 99 assert(Offset >= 0 && Offset <= 504 && 100 "Offset for save reg should be >= 0 && <= 504"); 101 EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); 102 } 103 104 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, 105 int Offset) { 106 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); 107 } 108 109 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, 110 int Offset) { 111 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); 112 } 113 114 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, 115 int Offset) { 116 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); 117 } 118 119 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, 120 int Offset) { 121 assert(Offset >= 0 && Offset <= 504 && 122 "Offset for save reg should be >= 0 && <= 504"); 123 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); 124 } 125 126 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, 127 int Offset) { 128 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); 129 } 130 131 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, 132 int Offset) { 133 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); 134 } 135 136 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, 137 int Offset) { 138 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); 139 } 140 141 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { 142 EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); 143 } 144 145 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { 146 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); 147 EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); 148 } 149 150 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { 151 EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); 152 } 153 154 // The functions below handle opcodes that can end up in either a prolog or 155 // an epilog, but not both. 156 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { 157 auto &S = getStreamer(); 158 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 159 if (!CurFrame) 160 return; 161 162 MCSymbol *Label = S.EmitCFILabel(); 163 CurFrame->PrologEnd = Label; 164 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 165 auto it = CurFrame->Instructions.begin(); 166 CurFrame->Instructions.insert(it, Inst); 167 } 168 169 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { 170 auto &S = getStreamer(); 171 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 172 if (!CurFrame) 173 return; 174 175 InEpilogCFI = true; 176 CurrentEpilog = S.EmitCFILabel(); 177 } 178 179 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { 180 auto &S = getStreamer(); 181 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 182 if (!CurFrame) 183 return; 184 185 InEpilogCFI = false; 186 MCSymbol *Label = S.EmitCFILabel(); 187 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 188 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 189 CurrentEpilog = nullptr; 190 } 191 192 MCWinCOFFStreamer *createAArch64WinCOFFStreamer( 193 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, 194 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, 195 bool RelaxAll, bool IncrementalLinkerCompatible) { 196 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), 197 std::move(Emitter), std::move(OW)); 198 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 199 return S; 200 } 201 202 } // end llvm namespace 203