ARMWinCOFFStreamer.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "ARMMCTargetDesc.h"
  9. #include "llvm/MC/MCAsmBackend.h"
  10. #include "llvm/MC/MCAssembler.h"
  11. #include "llvm/MC/MCCodeEmitter.h"
  12. #include "llvm/MC/MCContext.h"
  13. #include "llvm/MC/MCObjectWriter.h"
  14. #include "llvm/MC/MCWin64EH.h"
  15. #include "llvm/MC/MCWinCOFFStreamer.h"
  16. using namespace llvm;
  17. namespace {
  18. class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
  19. Win64EH::ARMUnwindEmitter EHStreamer;
  20. public:
  21. ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
  22. std::unique_ptr<MCCodeEmitter> CE,
  23. std::unique_ptr<MCObjectWriter> OW)
  24. : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
  25. void emitWinEHHandlerData(SMLoc Loc) override;
  26. void emitWindowsUnwindTables() override;
  27. void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
  28. void emitThumbFunc(MCSymbol *Symbol) override;
  29. void finishImpl() override;
  30. };
  31. void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
  32. MCStreamer::emitWinEHHandlerData(Loc);
  33. // We have to emit the unwind info now, because this directive
  34. // actually switches to the .xdata section!
  35. EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
  36. /* HandlerData = */ true);
  37. }
  38. void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
  39. EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
  40. }
  41. void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
  42. if (!getNumWinFrameInfos())
  43. return;
  44. EHStreamer.Emit(*this);
  45. }
  46. void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
  47. getAssembler().setIsThumbFunc(Symbol);
  48. }
  49. void ARMWinCOFFStreamer::finishImpl() {
  50. emitFrames(nullptr);
  51. emitWindowsUnwindTables();
  52. MCWinCOFFStreamer::finishImpl();
  53. }
  54. }
  55. MCStreamer *llvm::createARMWinCOFFStreamer(
  56. MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
  57. std::unique_ptr<MCObjectWriter> &&OW,
  58. std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
  59. bool IncrementalLinkerCompatible) {
  60. auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
  61. std::move(OW));
  62. S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
  63. return S;
  64. }
  65. namespace {
  66. class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
  67. private:
  68. // True if we are processing SEH directives in an epilogue.
  69. bool InEpilogCFI = false;
  70. // Symbol of the current epilog for which we are processing SEH directives.
  71. MCSymbol *CurrentEpilog = nullptr;
  72. public:
  73. ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
  74. // The unwind codes on ARM Windows are documented at
  75. // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
  76. void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
  77. void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
  78. void emitARMWinCFISaveSP(unsigned Reg) override;
  79. void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
  80. void emitARMWinCFISaveLR(unsigned Offset) override;
  81. void emitARMWinCFIPrologEnd(bool Fragment) override;
  82. void emitARMWinCFINop(bool Wide) override;
  83. void emitARMWinCFIEpilogStart(unsigned Condition) override;
  84. void emitARMWinCFIEpilogEnd() override;
  85. void emitARMWinCFICustom(unsigned Opcode) override;
  86. private:
  87. void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
  88. };
  89. // Helper function to common out unwind code setup for those codes that can
  90. // belong to both prolog and epilog.
  91. void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
  92. int Reg, int Offset) {
  93. auto &S = getStreamer();
  94. WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
  95. if (!CurFrame)
  96. return;
  97. MCSymbol *Label = S.emitCFILabel();
  98. auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
  99. if (InEpilogCFI)
  100. CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
  101. else
  102. CurFrame->Instructions.push_back(Inst);
  103. }
  104. void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
  105. bool Wide) {
  106. unsigned Op = Win64EH::UOP_AllocSmall;
  107. if (!Wide) {
  108. if (Size / 4 > 0xffff)
  109. Op = Win64EH::UOP_AllocHuge;
  110. else if (Size / 4 > 0x7f)
  111. Op = Win64EH::UOP_AllocLarge;
  112. } else {
  113. Op = Win64EH::UOP_WideAllocMedium;
  114. if (Size / 4 > 0xffff)
  115. Op = Win64EH::UOP_WideAllocHuge;
  116. else if (Size / 4 > 0x3ff)
  117. Op = Win64EH::UOP_WideAllocLarge;
  118. }
  119. emitARMWinUnwindCode(Op, -1, Size);
  120. }
  121. void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
  122. bool Wide) {
  123. assert(Mask != 0);
  124. int Lr = (Mask & 0x4000) ? 1 : 0;
  125. Mask &= ~0x4000;
  126. if (Wide)
  127. assert((Mask & ~0x1fff) == 0);
  128. else
  129. assert((Mask & ~0x00ff) == 0);
  130. if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
  131. if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
  132. // One continuous range from r4 to r8-r11
  133. for (int I = 11; I >= 8; I--) {
  134. if (Mask & (1 << I)) {
  135. emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
  136. return;
  137. }
  138. }
  139. // If it actually was from r4 to r4-r7, continue below.
  140. } else if (!Wide) {
  141. // One continuous range from r4 to r4-r7
  142. for (int I = 7; I >= 4; I--) {
  143. if (Mask & (1 << I)) {
  144. emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
  145. return;
  146. }
  147. }
  148. llvm_unreachable("logic error");
  149. }
  150. }
  151. Mask |= Lr << 14;
  152. if (Wide)
  153. emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
  154. else
  155. emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
  156. }
  157. void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
  158. emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
  159. }
  160. void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
  161. unsigned Last) {
  162. assert(First <= Last);
  163. assert(First >= 16 || Last < 16);
  164. assert(First <= 31 && Last <= 31);
  165. if (First == 8)
  166. emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
  167. else if (First <= 15)
  168. emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
  169. else
  170. emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
  171. }
  172. void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
  173. emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
  174. }
  175. void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
  176. if (Wide)
  177. emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
  178. else
  179. emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
  180. }
  181. void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
  182. auto &S = getStreamer();
  183. WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
  184. if (!CurFrame)
  185. return;
  186. MCSymbol *Label = S.emitCFILabel();
  187. CurFrame->PrologEnd = Label;
  188. WinEH::Instruction Inst =
  189. WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
  190. auto it = CurFrame->Instructions.begin();
  191. CurFrame->Instructions.insert(it, Inst);
  192. CurFrame->Fragment = Fragment;
  193. }
  194. void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
  195. auto &S = getStreamer();
  196. WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
  197. if (!CurFrame)
  198. return;
  199. InEpilogCFI = true;
  200. CurrentEpilog = S.emitCFILabel();
  201. CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
  202. }
  203. void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
  204. auto &S = getStreamer();
  205. WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
  206. if (!CurFrame)
  207. return;
  208. if (!CurrentEpilog) {
  209. S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
  210. CurFrame->Function->getName());
  211. return;
  212. }
  213. std::vector<WinEH::Instruction> &Epilog =
  214. CurFrame->EpilogMap[CurrentEpilog].Instructions;
  215. unsigned UnwindCode = Win64EH::UOP_End;
  216. if (!Epilog.empty()) {
  217. WinEH::Instruction EndInstr = Epilog.back();
  218. if (EndInstr.Operation == Win64EH::UOP_Nop) {
  219. UnwindCode = Win64EH::UOP_EndNop;
  220. Epilog.pop_back();
  221. } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
  222. UnwindCode = Win64EH::UOP_WideEndNop;
  223. Epilog.pop_back();
  224. }
  225. }
  226. InEpilogCFI = false;
  227. WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
  228. CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
  229. MCSymbol *Label = S.emitCFILabel();
  230. CurFrame->EpilogMap[CurrentEpilog].End = Label;
  231. CurrentEpilog = nullptr;
  232. }
  233. void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
  234. emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
  235. }
  236. } // end anonymous namespace
  237. MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
  238. return new ARMTargetWinCOFFStreamer(S);
  239. }