LLVM 22.0.0git
MCSFrame.cpp
Go to the documentation of this file.
1//===- lib/MC/MCSFrame.cpp - MCSFrame implementation ----------------------===//
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 "llvm/MC/MCSFrame.h"
11#include "llvm/MC/MCAsmInfo.h"
12#include "llvm/MC/MCContext.h"
15#include "llvm/MC/MCSection.h"
16#include "llvm/MC/MCSymbol.h"
18
19using namespace llvm;
20using namespace sframe;
21
22namespace {
23
24// High-level structure to track info needed to emit a
25// sframe_frame_row_entry_addrX. On disk these have both a fixed portion of type
26// sframe_frame_row_entry_addrX and trailing data of X * S bytes, where X is the
27// datum size, and S is 1, 2, or 3 depending on which of CFA, SP, and FP are
28// being tracked.
29struct SFrameFRE {
30 // An FRE describes how to find the registers when the PC is at this
31 // Label from function start.
32 const MCSymbol *Label = nullptr;
33 size_t CFAOffset = 0;
34 size_t FPOffset = 0;
35 size_t RAOffset = 0;
36 bool FromFP = false;
37 bool CFARegSet = false;
38
39 SFrameFRE(const MCSymbol *Start) : Label(Start) {}
40};
41
42// High-level structure to track info needed to emit a sframe_func_desc_entry
43// and its associated FREs.
44struct SFrameFDE {
45 // Reference to the original dwarf frame to avoid copying.
46 const MCDwarfFrameInfo &DFrame;
47 // Label where this FDE's FREs start.
48 MCSymbol *FREStart;
49 // Unwinding fres
51
52 SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
53 : DFrame(DF), FREStart(FRES) {}
54
55 void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) {
56 MCContext &C = S.getContext();
57
58 // sfde_func_start_address
59 const MCExpr *V = C.getAsmInfo()->getExprForFDESymbol(
60 &(*DFrame.Begin), C.getObjectFileInfo()->getFDEEncoding(), S);
61 S.emitValue(V, sizeof(int32_t));
62
63 // sfde_func_size
64 S.emitAbsoluteSymbolDiff(DFrame.End, DFrame.Begin, sizeof(uint32_t));
65
66 // sfde_func_start_fre_off
67 auto *F = S.getCurrentFragment();
68 const MCExpr *Diff = MCBinaryExpr::createSub(
69 MCSymbolRefExpr::create(FREStart, C),
70 MCSymbolRefExpr::create(FRESubSectionStart, C), C);
71
72 F->addFixup(MCFixup::create(F->getContents().size(), Diff,
74 S.emitInt32(0);
75
76 // sfde_func_num_fres
77 // TODO: When we actually emit fres, replace 0 with FREs.size()
78 S.emitInt32(0);
79
80 // sfde_func_info word
81 FDEInfo<endianness::native> I;
82 I.setFuncInfo(0 /* No pauth key */, FDEType::PCInc, FREType::Addr1);
83 S.emitInt8(I.Info);
84
85 // sfde_func_rep_size. Not relevant in non-PCMASK fdes.
86 S.emitInt8(0);
87
88 // sfde_func_padding2
89 S.emitInt16(0);
90 }
91};
92
93// Emitting these field-by-field, instead of constructing the actual structures
94// lets Streamer do target endian-fixups for free.
95
96class SFrameEmitterImpl {
97 MCObjectStreamer &Streamer;
99 ABI SFrameABI;
100 // Target-specific convenience variables to detect when a CFI instruction
101 // references these registers. Unlike in dwarf frame descriptions, they never
102 // escape into the sframe section itself.
103 unsigned SPReg;
104 unsigned FPReg;
105 unsigned RAReg;
106 MCSymbol *FDESubSectionStart;
107 MCSymbol *FRESubSectionStart;
108 MCSymbol *FRESubSectionEnd;
109
110 bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) {
111 if (I.getRegister() == SPReg) {
112 FRE.CFARegSet = true;
113 FRE.FromFP = false;
114 return true;
115 }
116 if (I.getRegister() == FPReg) {
117 FRE.CFARegSet = true;
118 FRE.FromFP = true;
119 return true;
120 }
121 Streamer.getContext().reportWarning(
122 I.getLoc(), "canonical Frame Address not in stack- or frame-pointer. "
123 "Omitting SFrame unwind info for this function");
124 return false;
125 }
126
127 bool setCFAOffset(SFrameFRE &FRE, const SMLoc &Loc, size_t Offset) {
128 if (!FRE.CFARegSet) {
129 Streamer.getContext().reportWarning(
130 Loc, "adjusting CFA offset without a base register. "
131 "Omitting SFrame unwind info for this function");
132 return false;
133 }
134 FRE.CFAOffset = Offset;
135 return true;
136 }
137
138 // Add the effects of CFI to the current FDE, creating a new FRE when
139 // necessary.
140 bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) {
141 switch (CFI.getOperation()) {
143 return setCFARegister(FRE, CFI);
146 if (!setCFARegister(FRE, CFI))
147 return false;
148 return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset());
150 if (CFI.getRegister() == FPReg)
151 FRE.FPOffset = CFI.getOffset();
152 else if (CFI.getRegister() == RAReg)
153 FRE.RAOffset = CFI.getOffset();
154 return true;
156 if (CFI.getRegister() == FPReg)
157 FRE.FPOffset += CFI.getOffset();
158 else if (CFI.getRegister() == RAReg)
159 FRE.RAOffset += CFI.getOffset();
160 return true;
162 return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset());
164 return setCFAOffset(FRE, CFI.getLoc(), FRE.CFAOffset + CFI.getOffset());
166 // TODO: Implement. Will use FDE.
167 return true;
169 // TODO: Implement. Will use FDE.
170 return true;
172 // TODO: Implement. Will use FDE.
173 return true;
175 // TODO: Implement. Will use FDE.
176 return true;
177 default:
178 // Instructions that don't affect the CFA, RA, and SP can be safely
179 // ignored.
180 return true;
181 }
182 }
183
184public:
185 SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) {
186 assert(Streamer.getContext()
187 .getObjectFileInfo()
188 ->getSFrameABIArch()
189 .has_value());
190 FDEs.reserve(Streamer.getDwarfFrameInfos().size());
191 SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
192 switch (SFrameABI) {
193 case ABI::AArch64EndianBig:
194 case ABI::AArch64EndianLittle:
195 SPReg = 31;
196 RAReg = 29;
197 FPReg = 30;
198 break;
199 case ABI::AMD64EndianLittle:
200 SPReg = 7;
201 // RARegister untracked in this abi. Value chosen to match
202 // MCDwarfFrameInfo constructor.
203 RAReg = static_cast<unsigned>(INT_MAX);
204 FPReg = 6;
205 break;
206 }
207
208 FDESubSectionStart = Streamer.getContext().createTempSymbol();
209 FRESubSectionStart = Streamer.getContext().createTempSymbol();
210 FRESubSectionEnd = Streamer.getContext().createTempSymbol();
211 }
212
213 bool atSameLocation(const MCSymbol *Left, const MCSymbol *Right) {
214 return Left != nullptr && Right != nullptr &&
215 Left->getFragment() == Right->getFragment() &&
216 Left->getOffset() == Right->getOffset();
217 }
218
219 bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) {
220 return Left.CFAOffset == Right.CFAOffset &&
221 Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset &&
222 Left.FromFP == Right.FromFP && Left.CFARegSet == Right.CFARegSet;
223 }
224
225 void buildSFDE(const MCDwarfFrameInfo &DF) {
226 bool Valid = true;
227 SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol());
228 // This would have been set via ".cfi_return_column", but
229 // MCObjectStreamer doesn't emit an MCCFIInstruction for that. It just
230 // sets the DF.RAReg.
231 // FIXME: This also prevents providing a proper location for the error.
232 // LLVM doesn't change the return column itself, so this was
233 // hand-written assembly.
234 if (DF.RAReg != RAReg) {
235 Streamer.getContext().reportWarning(
236 SMLoc(), "non-default RA register in .cfi_return_column " +
237 Twine(DF.RAReg) +
238 ". Omitting SFrame unwind info for this function");
239 Valid = false;
240 }
241 MCSymbol *LastLabel = DF.Begin;
242 SFrameFRE BaseFRE(LastLabel);
243 if (!DF.IsSimple) {
244 for (const auto &CFI :
245 Streamer.getContext().getAsmInfo()->getInitialFrameState())
246 if (!handleCFI(FDE, BaseFRE, CFI))
247 Valid = false;
248 }
249 FDE.FREs.push_back(BaseFRE);
250
251 for (const auto &CFI : DF.Instructions) {
252 // Instructions from InitialFrameState may not have a label, but if these
253 // instructions don't, then they are in dead code or otherwise unused.
254 // TODO: This check follows MCDwarf.cpp
255 // FrameEmitterImplementation::emitCFIInstructions, but nothing in the
256 // testsuite triggers it. We should see if it can be removed in both
257 // places, or alternately, add a test to exercise it.
258 auto *L = CFI.getLabel();
259 if (L && !L->isDefined())
260 continue;
261
262 SFrameFRE FRE = FDE.FREs.back();
263 if (!handleCFI(FDE, FRE, CFI))
264 Valid = false;
265
266 // If nothing relevant but the location changed, don't add the FRE.
267 if (equalIgnoringLocation(FRE, FDE.FREs.back()))
268 continue;
269
270 // If the location stayed the same, then update the current
271 // row. Otherwise, add a new one.
272 if (atSameLocation(LastLabel, L))
273 FDE.FREs.back() = FRE;
274 else {
275 FDE.FREs.push_back(FRE);
276 FDE.FREs.back().Label = L;
277 LastLabel = L;
278 }
279 }
280 if (Valid)
281 FDEs.push_back(FDE);
282 }
283
284 void emitPreamble() {
285 Streamer.emitInt16(Magic);
286 Streamer.emitInt8(static_cast<uint8_t>(Version::V2));
287 Streamer.emitInt8(static_cast<uint8_t>(Flags::FDEFuncStartPCRel));
288 }
289
290 void emitHeader() {
291 emitPreamble();
292 // sfh_abi_arch
293 Streamer.emitInt8(static_cast<uint8_t>(SFrameABI));
294 // sfh_cfa_fixed_fp_offset
295 Streamer.emitInt8(0);
296 // sfh_cfa_fixed_ra_offset
297 Streamer.emitInt8(0);
298 // sfh_auxhdr_len
299 Streamer.emitInt8(0);
300 // shf_num_fdes
301 Streamer.emitInt32(FDEs.size());
302 // shf_num_fres
303 uint32_t TotalFREs = 0;
304 Streamer.emitInt32(TotalFREs);
305
306 // shf_fre_len
307 Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
308 sizeof(int32_t));
309 // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
310 Streamer.emitInt32(0);
311 // shf_freoff
312 Streamer.emitAbsoluteSymbolDiff(FRESubSectionStart, FDESubSectionStart,
313 sizeof(uint32_t));
314 }
315
316 void emitFDEs() {
317 Streamer.emitLabel(FDESubSectionStart);
318 for (auto &FDE : FDEs) {
319 FDE.emit(Streamer, FRESubSectionStart);
320 }
321 }
322
323 void emitFREs() {
324 Streamer.emitLabel(FRESubSectionStart);
325 for (auto &FDE : FDEs)
326 Streamer.emitLabel(FDE.FREStart);
327 Streamer.emitLabel(FRESubSectionEnd);
328 }
329};
330
331} // end anonymous namespace
332
334 MCContext &Context = Streamer.getContext();
335 // If this target doesn't support sframes, return now. Gas doesn't warn in
336 // this case, but if we want to, it should be done at option-parsing time,
337 // rather than here.
338 if (!Streamer.getContext()
341 .has_value())
342 return;
343
344 SFrameEmitterImpl Emitter(Streamer);
345 ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos();
346
347 // Both the header itself and the FDEs include various offsets and counts.
348 // Therefore, all of this must be precomputed.
349 for (const auto &DFrame : FrameArray)
350 Emitter.buildSFDE(DFrame);
351
352 MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
353 // Not strictly necessary, but gas always aligns to 8, so match that.
354 Section->ensureMinAlignment(Align(8));
355 Streamer.switchSection(Section);
356 MCSymbol *SectionStart = Context.createTempSymbol();
357 Streamer.emitLabel(SectionStart);
358 Emitter.emitHeader();
359 Emitter.emitFDEs();
360 Emitter.emitFREs();
361}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
dxil DXContainer Global Emitter
static RegisterPass< DebugifyFunctionPass > DF("debugify-function", "Attach debug info to a function")
#define F(x, y, z)
Definition MD5.cpp:55
#define I(x, y, z)
Definition MD5.cpp:58
This file contains data-structure definitions and constants to support unwinding based on ....
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
static const MCBinaryExpr * createSub(const MCExpr *LHS, const MCExpr *RHS, MCContext &Ctx)
Definition MCExpr.h:428
MCSymbol * getLabel() const
Definition MCDwarf.h:721
unsigned getRegister() const
Definition MCDwarf.h:723
SMLoc getLoc() const
Definition MCDwarf.h:766
OpType getOperation() const
Definition MCDwarf.h:720
int64_t getOffset() const
Definition MCDwarf.h:745
Context object for machine code objects.
Definition MCContext.h:83
const MCObjectFileInfo * getObjectFileInfo() const
Definition MCContext.h:416
static MCFixupKind getDataKindForSize(unsigned Size)
Return the generic fixup kind for a value with the given size.
Definition MCFixup.h:110
static MCFixup create(uint32_t Offset, const MCExpr *Value, MCFixupKind Kind, bool PCRel=false)
Consider bit fields if we need more flags.
Definition MCFixup.h:86
std::optional< sframe::ABI > getSFrameABIArch() const
Streaming object file generation interface.
void emitLabel(MCSymbol *Symbol, SMLoc Loc=SMLoc()) override
Emit a label for Symbol into the current section.
void emitAbsoluteSymbolDiff(const MCSymbol *Hi, const MCSymbol *Lo, unsigned Size) override
Emit the absolute difference between two symbols if possible.
static void emit(MCObjectStreamer &Streamer)
Definition MCSFrame.cpp:333
Instances of this class represent a uniqued identifier for a section in the current translation unit.
Definition MCSection.h:496
MCFragment * getCurrentFragment() const
Definition MCStreamer.h:432
MCContext & getContext() const
Definition MCStreamer.h:314
void emitValue(const MCExpr *Value, unsigned Size, SMLoc Loc=SMLoc())
void emitInt16(uint64_t Value)
Definition MCStreamer.h:749
ArrayRef< MCDwarfFrameInfo > getDwarfFrameInfos() const
virtual void switchSection(MCSection *Section, uint32_t Subsec=0)
Set the current section where code is being emitted to Section.
void emitInt32(uint64_t Value)
Definition MCStreamer.h:750
void emitInt8(uint64_t Value)
Definition MCStreamer.h:748
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:214
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ FRE
Reciprocal estimate instructions (unary FP ops).
constexpr uint16_t Magic
Definition SFrame.h:32
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:477
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39