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