1 //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
21 class ARMWinCOFFStreamer
: public MCWinCOFFStreamer
{
22 Win64EH::ARMUnwindEmitter EHStreamer
;
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
)) {}
30 void emitWinEHHandlerData(SMLoc Loc
) override
;
31 void emitWindowsUnwindTables() override
;
32 void emitWindowsUnwindTables(WinEH::FrameInfo
*Frame
) override
;
34 void emitThumbFunc(MCSymbol
*Symbol
) override
;
35 void finishImpl() override
;
38 void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc
) {
39 MCStreamer::emitWinEHHandlerData(Loc
);
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);
47 void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo
*Frame
) {
48 EHStreamer
.EmitUnwindInfo(*this, Frame
, /* HandlerData = */ false);
51 void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
52 if (!getNumWinFrameInfos())
54 EHStreamer
.Emit(*this);
57 void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol
*Symbol
) {
58 getAssembler().setIsThumbFunc(Symbol
);
61 void ARMWinCOFFStreamer::finishImpl() {
63 emitWindowsUnwindTables();
65 MCWinCOFFStreamer::finishImpl();
70 llvm::createARMWinCOFFStreamer(MCContext
&Context
,
71 std::unique_ptr
<MCAsmBackend
> &&MAB
,
72 std::unique_ptr
<MCObjectWriter
> &&OW
,
73 std::unique_ptr
<MCCodeEmitter
> &&Emitter
) {
74 return new ARMWinCOFFStreamer(Context
, std::move(MAB
), std::move(Emitter
),
79 class ARMTargetWinCOFFStreamer
: public llvm::ARMTargetStreamer
{
81 // True if we are processing SEH directives in an epilogue.
82 bool InEpilogCFI
= false;
84 // Symbol of the current epilog for which we are processing SEH directives.
85 MCSymbol
*CurrentEpilog
= nullptr;
88 ARMTargetWinCOFFStreamer(llvm::MCStreamer
&S
) : ARMTargetStreamer(S
) {}
90 // The unwind codes on ARM Windows are documented at
91 // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
92 void emitARMWinCFIAllocStack(unsigned Size
, bool Wide
) override
;
93 void emitARMWinCFISaveRegMask(unsigned Mask
, bool Wide
) override
;
94 void emitARMWinCFISaveSP(unsigned Reg
) override
;
95 void emitARMWinCFISaveFRegs(unsigned First
, unsigned Last
) override
;
96 void emitARMWinCFISaveLR(unsigned Offset
) override
;
97 void emitARMWinCFIPrologEnd(bool Fragment
) override
;
98 void emitARMWinCFINop(bool Wide
) override
;
99 void emitARMWinCFIEpilogStart(unsigned Condition
) override
;
100 void emitARMWinCFIEpilogEnd() override
;
101 void emitARMWinCFICustom(unsigned Opcode
) override
;
104 void emitARMWinUnwindCode(unsigned UnwindCode
, int Reg
, int Offset
);
107 // Helper function to common out unwind code setup for those codes that can
108 // belong to both prolog and epilog.
109 void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode
,
110 int Reg
, int Offset
) {
111 auto &S
= getStreamer();
112 WinEH::FrameInfo
*CurFrame
= S
.EnsureValidWinFrameInfo(SMLoc());
115 MCSymbol
*Label
= S
.emitCFILabel();
116 auto Inst
= WinEH::Instruction(UnwindCode
, Label
, Reg
, Offset
);
118 CurFrame
->EpilogMap
[CurrentEpilog
].Instructions
.push_back(Inst
);
120 CurFrame
->Instructions
.push_back(Inst
);
123 void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size
,
125 unsigned Op
= Win64EH::UOP_AllocSmall
;
127 if (Size
/ 4 > 0xffff)
128 Op
= Win64EH::UOP_AllocHuge
;
129 else if (Size
/ 4 > 0x7f)
130 Op
= Win64EH::UOP_AllocLarge
;
132 Op
= Win64EH::UOP_WideAllocMedium
;
133 if (Size
/ 4 > 0xffff)
134 Op
= Win64EH::UOP_WideAllocHuge
;
135 else if (Size
/ 4 > 0x3ff)
136 Op
= Win64EH::UOP_WideAllocLarge
;
138 emitARMWinUnwindCode(Op
, -1, Size
);
141 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask
,
144 int Lr
= (Mask
& 0x4000) ? 1 : 0;
147 assert((Mask
& ~0x1fff) == 0);
149 assert((Mask
& ~0x00ff) == 0);
150 if (Mask
&& ((Mask
+ (1 << 4)) & Mask
) == 0) {
151 if (Wide
&& (Mask
& 0x1000) == 0 && (Mask
& 0xff) == 0xf0) {
152 // One continuous range from r4 to r8-r11
153 for (int I
= 11; I
>= 8; I
--) {
154 if (Mask
& (1 << I
)) {
155 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR
, I
, Lr
);
159 // If it actually was from r4 to r4-r7, continue below.
161 // One continuous range from r4 to r4-r7
162 for (int I
= 7; I
>= 4; I
--) {
163 if (Mask
& (1 << I
)) {
164 emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR
, I
, Lr
);
168 llvm_unreachable("logic error");
173 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask
, Mask
, 0);
175 emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask
, Mask
, 0);
178 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg
) {
179 emitARMWinUnwindCode(Win64EH::UOP_SaveSP
, Reg
, 0);
182 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First
,
184 assert(First
<= Last
);
185 assert(First
>= 16 || Last
< 16);
186 assert(First
<= 31 && Last
<= 31);
188 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15
, Last
, 0);
189 else if (First
<= 15)
190 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15
, First
, Last
);
192 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31
, First
, Last
);
195 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset
) {
196 emitARMWinUnwindCode(Win64EH::UOP_SaveLR
, 0, Offset
);
199 void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide
) {
201 emitARMWinUnwindCode(Win64EH::UOP_WideNop
, -1, 0);
203 emitARMWinUnwindCode(Win64EH::UOP_Nop
, -1, 0);
206 void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment
) {
207 auto &S
= getStreamer();
208 WinEH::FrameInfo
*CurFrame
= S
.EnsureValidWinFrameInfo(SMLoc());
212 MCSymbol
*Label
= S
.emitCFILabel();
213 CurFrame
->PrologEnd
= Label
;
214 WinEH::Instruction Inst
=
215 WinEH::Instruction(Win64EH::UOP_End
, /*Label=*/nullptr, -1, 0);
216 auto it
= CurFrame
->Instructions
.begin();
217 CurFrame
->Instructions
.insert(it
, Inst
);
218 CurFrame
->Fragment
= Fragment
;
221 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition
) {
222 auto &S
= getStreamer();
223 WinEH::FrameInfo
*CurFrame
= S
.EnsureValidWinFrameInfo(SMLoc());
228 CurrentEpilog
= S
.emitCFILabel();
229 CurFrame
->EpilogMap
[CurrentEpilog
].Condition
= Condition
;
232 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
233 auto &S
= getStreamer();
234 WinEH::FrameInfo
*CurFrame
= S
.EnsureValidWinFrameInfo(SMLoc());
238 if (!CurrentEpilog
) {
239 S
.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
240 CurFrame
->Function
->getName());
244 std::vector
<WinEH::Instruction
> &Epilog
=
245 CurFrame
->EpilogMap
[CurrentEpilog
].Instructions
;
247 unsigned UnwindCode
= Win64EH::UOP_End
;
248 if (!Epilog
.empty()) {
249 WinEH::Instruction EndInstr
= Epilog
.back();
250 if (EndInstr
.Operation
== Win64EH::UOP_Nop
) {
251 UnwindCode
= Win64EH::UOP_EndNop
;
253 } else if (EndInstr
.Operation
== Win64EH::UOP_WideNop
) {
254 UnwindCode
= Win64EH::UOP_WideEndNop
;
260 WinEH::Instruction Inst
= WinEH::Instruction(UnwindCode
, nullptr, -1, 0);
261 CurFrame
->EpilogMap
[CurrentEpilog
].Instructions
.push_back(Inst
);
262 MCSymbol
*Label
= S
.emitCFILabel();
263 CurFrame
->EpilogMap
[CurrentEpilog
].End
= Label
;
264 CurrentEpilog
= nullptr;
267 void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode
) {
268 emitARMWinUnwindCode(Win64EH::UOP_Custom
, 0, Opcode
);
271 } // end anonymous namespace
273 MCTargetStreamer
*llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer
&S
) {
274 return new ARMTargetWinCOFFStreamer(S
);