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"
17#include "llvm/Support/Endian.h"
19
20using namespace llvm;
21using namespace sframe;
22
23namespace {
24
25// High-level structure to track info needed to emit a
26// sframe_frame_row_entry_addrX. On disk these have both a fixed portion of type
27// sframe_frame_row_entry_addrX and trailing data of X * S bytes, where X is the
28// datum size, and S is 1, 2, or 3 depending on which of CFA, SP, and FP are
29// being tracked.
30struct SFrameFRE {
31 // An FRE describes how to find the registers when the PC is at this
32 // Label from function start.
33 const MCSymbol *Label = nullptr;
34 size_t CFAOffset = 0;
35 size_t FPOffset = 0;
36 size_t RAOffset = 0;
37 FREInfo<endianness::native> Info;
38 bool CFARegSet = false;
39
40 SFrameFRE(const MCSymbol *Start) : Label(Start) { Info.Info = 0; }
41
42 void emitOffset(MCObjectStreamer &S, FREOffset OffsetSize, size_t Offset) {
43 switch (OffsetSize) {
44 case (FREOffset::B1):
46 return;
47 case (FREOffset::B2):
49 return;
50 case (FREOffset::B4):
52 return;
53 }
54 }
55
56 void emit(MCObjectStreamer &S, const MCSymbol *FuncBegin,
57 MCFragment *FDEFrag) {
58 S.emitSFrameCalculateFuncOffset(FuncBegin, Label, FDEFrag, SMLoc());
59
60 // fre_cfa_base_reg_id already set during parsing
61
62 // fre_offset_count
63 unsigned RegsTracked = 1; // always track the cfa.
64 if (FPOffset != 0)
65 ++RegsTracked;
66 if (RAOffset != 0)
67 ++RegsTracked;
68 Info.setOffsetCount(RegsTracked);
69
70 // fre_offset_size
71 if (isInt<8>(CFAOffset) && isInt<8>(FPOffset) && isInt<8>(RAOffset))
72 Info.setOffsetSize(FREOffset::B1);
73 else if (isInt<16>(CFAOffset) && isInt<16>(FPOffset) && isInt<16>(RAOffset))
74 Info.setOffsetSize(FREOffset::B2);
75 else {
76 assert(isInt<32>(CFAOffset) && isInt<32>(FPOffset) &&
77 isInt<32>(RAOffset) && "Offset too big for sframe");
78 Info.setOffsetSize(FREOffset::B4);
79 }
80
81 // No support for fre_mangled_ra_p yet.
82 Info.setReturnAddressSigned(false);
83
84 // sframe_fre_info_word
85 S.emitInt8(Info.getFREInfo());
86
87 // FRE Offsets
88 [[maybe_unused]] unsigned OffsetsEmitted = 1;
89 emitOffset(S, Info.getOffsetSize(), CFAOffset);
90 if (FPOffset) {
91 ++OffsetsEmitted;
92 emitOffset(S, Info.getOffsetSize(), FPOffset);
93 }
94 if (RAOffset) {
95 ++OffsetsEmitted;
96 emitOffset(S, Info.getOffsetSize(), RAOffset);
97 }
98 assert(OffsetsEmitted == RegsTracked &&
99 "Didn't emit the right number of offsets");
100 }
101};
102
103// High-level structure to track info needed to emit a sframe_func_desc_entry
104// and its associated FREs.
105struct SFrameFDE {
106 // Reference to the original dwarf frame to avoid copying.
107 const MCDwarfFrameInfo &DFrame;
108 // Label where this FDE's FREs start.
109 MCSymbol *FREStart;
110 // Frag where this FDE is emitted.
111 MCFragment *Frag;
112 // Unwinding fres
114 // .cfi_remember_state stack
115 SmallVector<SFrameFRE> SaveState;
116
117 SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
118 : DFrame(DF), FREStart(FRES), Frag(nullptr) {}
119
120 void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) {
121 MCContext &C = S.getContext();
122
123 // sfde_func_start_address
124 const MCExpr *V = C.getAsmInfo()->getExprForFDESymbol(
125 &(*DFrame.Begin), C.getObjectFileInfo()->getFDEEncoding(), S);
126 S.emitValue(V, sizeof(int32_t));
127
128 // sfde_func_size
129 S.emitAbsoluteSymbolDiff(DFrame.End, DFrame.Begin, sizeof(uint32_t));
130
131 // sfde_func_start_fre_off
132 auto *F = S.getCurrentFragment();
133 const MCExpr *Diff = MCBinaryExpr::createSub(
134 MCSymbolRefExpr::create(FREStart, C),
135 MCSymbolRefExpr::create(FRESubSectionStart, C), C);
136
137 F->addFixup(MCFixup::create(F->getContents().size(), Diff,
139 S.emitInt32(0);
140
141 // sfde_func_num_fres
142 S.emitInt32(FREs.size());
143
144 // sfde_func_info word
145
146 // All FREs within an FDE share the same sframe::FREType::AddrX. The value
147 // of 'X' is determined by the FRE with the largest offset, which is the
148 // last. This offset isn't known until relax time, so emit a frag which can
149 // calculate that now.
150 //
151 // At relax time, this FDE frag calculates the proper AddrX value (as well
152 // as the rest of the FDE FuncInfo word). Subsequent FRE frags will read it
153 // from this frag and emit the proper number of bytes.
154 Frag = S.getCurrentFragment();
155 S.emitSFrameCalculateFuncOffset(DFrame.Begin, FREs.back().Label, nullptr,
156 SMLoc());
157
158 // sfde_func_rep_size. Not relevant in non-PCMASK fdes.
159 S.emitInt8(0);
160
161 // sfde_func_padding2
162 S.emitInt16(0);
163 }
164};
165
166// Emitting these field-by-field, instead of constructing the actual structures
167// lets Streamer do target endian-fixups for free.
168
169class SFrameEmitterImpl {
170 MCObjectStreamer &Streamer;
172 uint32_t TotalFREs;
173 ABI SFrameABI;
174 // Target-specific convenience variables to detect when a CFI instruction
175 // references these registers. Unlike in dwarf frame descriptions, they never
176 // escape into the sframe section itself. TODO: These should be retrieved from
177 // the target.
178 unsigned SPReg;
179 unsigned FPReg;
180 unsigned RAReg;
181 int8_t FixedRAOffset;
182 MCSymbol *FDESubSectionStart;
183 MCSymbol *FRESubSectionStart;
184 MCSymbol *FRESubSectionEnd;
185
186 bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) {
187 if (I.getRegister() == SPReg) {
188 FRE.CFARegSet = true;
189 FRE.Info.setBaseRegister(BaseReg::SP);
190 return true;
191 }
192 if (I.getRegister() == FPReg) {
193 FRE.CFARegSet = true;
194 FRE.Info.setBaseRegister(BaseReg::FP);
195 return true;
196 }
197 Streamer.getContext().reportWarning(
198 I.getLoc(), "canonical Frame Address not in stack- or frame-pointer. "
199 "Omitting SFrame unwind info for this function");
200 return false;
201 }
202
203 bool setCFAOffset(SFrameFRE &FRE, SMLoc Loc, size_t Offset) {
204 if (!FRE.CFARegSet) {
205 Streamer.getContext().reportWarning(
206 Loc, "adjusting CFA offset without a base register. "
207 "Omitting SFrame unwind info for this function");
208 return false;
209 }
210 FRE.CFAOffset = Offset;
211 return true;
212 }
213
214 // Add the effects of CFI to the current FDE, creating a new FRE when
215 // necessary.
216 bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) {
217 switch (CFI.getOperation()) {
219 return setCFARegister(FRE, CFI);
222 if (!setCFARegister(FRE, CFI))
223 return false;
224 return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset());
226 if (CFI.getRegister() == FPReg)
227 FRE.FPOffset = CFI.getOffset();
228 else if (CFI.getRegister() == RAReg)
229 FRE.RAOffset = CFI.getOffset();
230 return true;
232 if (CFI.getRegister() == FPReg)
233 FRE.FPOffset += CFI.getOffset();
234 else if (CFI.getRegister() == RAReg)
235 FRE.RAOffset += CFI.getOffset();
236 return true;
238 return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset());
240 return setCFAOffset(FRE, CFI.getLoc(), FRE.CFAOffset + CFI.getOffset());
242 if (FDE.FREs.size() == 1) {
243 // Error for gas compatibility: If the initial FRE isn't complete,
244 // then any state is incomplete. FIXME: Dwarf doesn't error here.
245 // Why should sframe?
246 Streamer.getContext().reportWarning(
247 CFI.getLoc(), "skipping SFrame FDE; .cfi_remember_state without "
248 "prior SFrame FRE state");
249 return false;
250 }
251 FDE.SaveState.push_back(FRE);
252 return true;
254 // The first FRE generated has the original state.
255 if (CFI.getRegister() == FPReg)
256 FRE.FPOffset = FDE.FREs.front().FPOffset;
257 else if (CFI.getRegister() == RAReg)
258 FRE.RAOffset = FDE.FREs.front().RAOffset;
259 return true;
261 // The cfi parser will have caught unbalanced directives earlier, so a
262 // mismatch here is an implementation error.
263 assert(!FDE.SaveState.empty() &&
264 "cfi_restore_state without cfi_save_state");
265 FRE = FDE.SaveState.pop_back_val();
266 return true;
268 // TODO: Implement. Will use FDE.
269 return true;
270 default:
271 // Instructions that don't affect the CFA, RA, and SP can be safely
272 // ignored.
273 return true;
274 }
275 }
276
277public:
278 SFrameEmitterImpl(MCObjectStreamer &Streamer)
279 : Streamer(Streamer), TotalFREs(0) {
280 assert(Streamer.getContext()
281 .getObjectFileInfo()
282 ->getSFrameABIArch()
283 .has_value());
284 FDEs.reserve(Streamer.getDwarfFrameInfos().size());
285 SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
286 switch (SFrameABI) {
287 case ABI::AArch64EndianBig:
288 case ABI::AArch64EndianLittle:
289 SPReg = 31;
290 RAReg = 29;
291 FPReg = 30;
292 FixedRAOffset = 0;
293 break;
294 case ABI::AMD64EndianLittle:
295 SPReg = 7;
296 // RARegister untracked in this abi. Value chosen to match
297 // MCDwarfFrameInfo constructor.
298 RAReg = static_cast<unsigned>(INT_MAX);
299 FPReg = 6;
300 FixedRAOffset = -8;
301 break;
302 }
303
304 FDESubSectionStart = Streamer.getContext().createTempSymbol();
305 FRESubSectionStart = Streamer.getContext().createTempSymbol();
306 FRESubSectionEnd = Streamer.getContext().createTempSymbol();
307 }
308
309 bool atSameLocation(const MCSymbol *Left, const MCSymbol *Right) {
310 return Left != nullptr && Right != nullptr &&
311 Left->getFragment() == Right->getFragment() &&
312 Left->getOffset() == Right->getOffset();
313 }
314
315 bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) {
316 return Left.CFAOffset == Right.CFAOffset &&
317 Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset &&
318 Left.Info.getFREInfo() == Right.Info.getFREInfo() &&
319 Left.CFARegSet == Right.CFARegSet;
320 }
321
322 void buildSFDE(const MCDwarfFrameInfo &DF) {
323 // Functions with zero size can happen with assembler macros and
324 // machine-generated code. They don't need unwind info at all, so
325 // no need to warn.
326 if (atSameLocation(DF.Begin, DF.End))
327 return;
328 bool Valid = true;
329 SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol());
330 // This would have been set via ".cfi_return_column", but
331 // MCObjectStreamer doesn't emit an MCCFIInstruction for that. It just
332 // sets the DF.RAReg.
333 // FIXME: This also prevents providing a proper location for the error.
334 // LLVM doesn't change the return column itself, so this was
335 // hand-written assembly.
336 if (DF.RAReg != RAReg) {
337 Streamer.getContext().reportWarning(
338 SMLoc(), "non-default RA register in .cfi_return_column " +
339 Twine(DF.RAReg) +
340 ". Omitting SFrame unwind info for this function");
341 Valid = false;
342 }
343 MCSymbol *LastLabel = DF.Begin;
344 SFrameFRE BaseFRE(LastLabel);
345 if (!DF.IsSimple) {
346 for (const auto &CFI :
347 Streamer.getContext().getAsmInfo()->getInitialFrameState())
348 if (!handleCFI(FDE, BaseFRE, CFI))
349 Valid = false;
350 }
351 FDE.FREs.push_back(BaseFRE);
352
353 for (const auto &CFI : DF.Instructions) {
354 // Instructions from InitialFrameState may not have a label, but if these
355 // instructions don't, then they are in dead code or otherwise unused.
356 // TODO: This check follows MCDwarf.cpp
357 // FrameEmitterImplementation::emitCFIInstructions, but nothing in the
358 // testsuite triggers it. We should see if it can be removed in both
359 // places, or alternately, add a test to exercise it.
360 auto *L = CFI.getLabel();
361 if (L && !L->isDefined())
362 continue;
363
364 SFrameFRE FRE = FDE.FREs.back();
365 if (!handleCFI(FDE, FRE, CFI))
366 Valid = false;
367
368 // If nothing relevant but the location changed, don't add the FRE.
369 if (equalIgnoringLocation(FRE, FDE.FREs.back()))
370 continue;
371
372 // If the location stayed the same, then update the current
373 // row. Otherwise, add a new one.
374 if (atSameLocation(LastLabel, L))
375 FDE.FREs.back() = FRE;
376 else {
377 FDE.FREs.push_back(FRE);
378 FDE.FREs.back().Label = L;
379 LastLabel = L;
380 }
381 }
382
383 if (Valid) {
384 FDEs.push_back(FDE);
385 TotalFREs += FDE.FREs.size();
386 }
387 }
388
389 void emitPreamble() {
390 Streamer.emitInt16(Magic);
391 Streamer.emitInt8(static_cast<uint8_t>(Version::V2));
392 Streamer.emitInt8(static_cast<uint8_t>(Flags::FDEFuncStartPCRel));
393 }
394
395 void emitHeader() {
396 emitPreamble();
397 // sfh_abi_arch
398 Streamer.emitInt8(static_cast<uint8_t>(SFrameABI));
399 // sfh_cfa_fixed_fp_offset
400 Streamer.emitInt8(0);
401 // sfh_cfa_fixed_ra_offset
402 Streamer.emitInt8(FixedRAOffset);
403 // sfh_auxhdr_len
404 Streamer.emitInt8(0);
405 // shf_num_fdes
406 Streamer.emitInt32(FDEs.size());
407 // shf_num_fres
408 Streamer.emitInt32(TotalFREs);
409
410 // shf_fre_len
411 Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
412 sizeof(int32_t));
413 // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
414 Streamer.emitInt32(0);
415 // shf_freoff
416 Streamer.emitInt32(FDEs.size() *
417 sizeof(sframe::FuncDescEntry<endianness::native>));
418 }
419
420 void emitFDEs() {
421 Streamer.emitLabel(FDESubSectionStart);
422 for (auto &FDE : FDEs) {
423 FDE.emit(Streamer, FRESubSectionStart);
424 }
425 }
426
427 void emitFREs() {
428 Streamer.emitLabel(FRESubSectionStart);
429 for (auto &FDE : FDEs) {
430 Streamer.emitLabel(FDE.FREStart);
431 for (auto &FRE : FDE.FREs)
432 FRE.emit(Streamer, FDE.DFrame.Begin, FDE.Frag);
433 }
434 Streamer.emitLabel(FRESubSectionEnd);
435 }
436};
437
438} // end anonymous namespace
439
441 MCContext &Context = Streamer.getContext();
442 // If this target doesn't support sframes, return now. Gas doesn't warn in
443 // this case, but if we want to, it should be done at option-parsing time,
444 // rather than here.
445 if (!Streamer.getContext()
448 .has_value())
449 return;
450
451 SFrameEmitterImpl Emitter(Streamer);
452 ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos();
453
454 // Both the header itself and the FDEs include various offsets and counts.
455 // Therefore, all of this must be precomputed.
456 for (const auto &DFrame : FrameArray)
457 Emitter.buildSFDE(DFrame);
458
459 MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
460 // Not strictly necessary, but gas always aligns to 8, so match that.
461 Section->ensureMinAlignment(Align(8));
462 Streamer.switchSection(Section);
463 MCSymbol *SectionStart = Context.createTempSymbol();
464 Streamer.emitLabel(SectionStart);
465 Emitter.emitHeader();
466 Emitter.emitFDEs();
467 Emitter.emitFREs();
468}
469
472 MCFragment *FDEFrag) {
473 // If encoding into the FDE Frag itself, generate the sfde_func_info.
474 if (FDEFrag == nullptr) {
475 // sfde_func_info
476
477 // Offset is the difference between the function start label and the final
478 // FRE's offset, which is the max offset for this FDE.
480 I.Info = 0;
481 if (isUInt<8>(Offset))
482 I.setFREType(FREType::Addr1);
483 else if (isUInt<16>(Offset))
484 I.setFREType(FREType::Addr2);
485 else {
487 I.setFREType(FREType::Addr4);
488 }
489 I.setFDEType(FDEType::PCInc);
490 // TODO: When we support pauth keys, this will need to be retrieved
491 // from the frag itself.
492 I.setPAuthKey(0);
493
494 Out.push_back(I.getFuncInfo());
495 return;
496 }
497
498 const auto &FDEData = FDEFrag->getVarContents();
500 I.Info = FDEData.back();
501 FREType T = I.getFREType();
502 llvm::endianness E = C.getAsmInfo()->isLittleEndian()
505 // sfre_start_address
506 switch (T) {
507 case FREType::Addr1:
508 assert(isUInt<8>(Offset) && "Miscalculated Sframe FREType");
510 break;
511 case FREType::Addr2:
512 assert(isUInt<16>(Offset) && "Miscalculated Sframe FREType");
514 break;
515 case FREType::Addr4:
516 assert(isUInt<32>(Offset) && "Miscalculated Sframe FREType");
518 break;
519 }
520}
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
#define T
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
MutableArrayRef< char > getVarContents()
Definition MCSection.h:647
std::optional< sframe::ABI > getSFrameABIArch() const
Streaming object file generation interface.
void emitSFrameCalculateFuncOffset(const MCSymbol *FunCabsel, const MCSymbol *FREBegin, MCFragment *FDEFrag, SMLoc Loc)
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:440
static void encodeFuncOffset(MCContext &C, uint64_t Offset, SmallVectorImpl< char > &Out, MCFragment *FDEFrag)
Definition MCSFrame.cpp:470
Instances of this class represent a uniqued identifier for a section in the current translation unit.
Definition MCSection.h:521
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
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ FRE
Reciprocal estimate instructions (unary FP ops).
FREOffset
Size of stack offsets. Bits 6-7 of FREInfo.Info.
Definition SFrame.h:71
constexpr uint16_t Magic
Definition SFrame.h:32
FREType
SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info.
Definition SFrame.h:52
void write(void *memory, value_type value, endianness endian)
Write a value to memory with a particular endianness.
Definition Endian.h:92
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:477
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:174
constexpr bool isUInt(uint64_t x)
Checks if an unsigned integer fits into the given bit width.
Definition MathExtras.h:198
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
endianness
Definition bit.h:71
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39