@@ -21,13 +21,33 @@ using namespace sframe;
2121
2222namespace {
2323
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.
29+ struct 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+
2442// High-level structure to track info needed to emit a sframe_func_desc_entry
2543// and its associated FREs.
2644struct SFrameFDE {
2745 // Reference to the original dwarf frame to avoid copying.
2846 const MCDwarfFrameInfo &DFrame;
2947 // Label where this FDE's FREs start.
3048 MCSymbol *FREStart;
49+ // Unwinding fres
50+ SmallVector<SFrameFRE> FREs;
3151
3252 SFrameFDE (const MCDwarfFrameInfo &DF, MCSymbol *FRES)
3353 : DFrame(DF), FREStart(FRES) {}
@@ -53,7 +73,8 @@ struct SFrameFDE {
5373 MCFixup::getDataKindForSize (4 )));
5474 S.emitInt32 (0 );
5575
56- // sfde_func_start_num_fres
76+ // sfde_func_num_fres
77+ // TODO: When we actually emit fres, replace 0 with FREs.size()
5778 S.emitInt32 (0 );
5879
5980 // sfde_func_info word
@@ -76,10 +97,90 @@ class SFrameEmitterImpl {
7697 MCObjectStreamer &Streamer;
7798 SmallVector<SFrameFDE> FDEs;
7899 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;
79106 MCSymbol *FDESubSectionStart;
80107 MCSymbol *FRESubSectionStart;
81108 MCSymbol *FRESubSectionEnd;
82109
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 ()) {
142+ case MCCFIInstruction::OpDefCfaRegister:
143+ return setCFARegister (FRE, CFI);
144+ case MCCFIInstruction::OpDefCfa:
145+ case MCCFIInstruction::OpLLVMDefAspaceCfa:
146+ if (!setCFARegister (FRE, CFI))
147+ return false ;
148+ return setCFAOffset (FRE, CFI.getLoc (), CFI.getOffset ());
149+ case MCCFIInstruction::OpOffset:
150+ if (CFI.getRegister () == FPReg)
151+ FRE.FPOffset = CFI.getOffset ();
152+ else if (CFI.getRegister () == RAReg)
153+ FRE.RAOffset = CFI.getOffset ();
154+ return true ;
155+ case MCCFIInstruction::OpRelOffset:
156+ if (CFI.getRegister () == FPReg)
157+ FRE.FPOffset += CFI.getOffset ();
158+ else if (CFI.getRegister () == RAReg)
159+ FRE.RAOffset += CFI.getOffset ();
160+ return true ;
161+ case MCCFIInstruction::OpDefCfaOffset:
162+ return setCFAOffset (FRE, CFI.getLoc (), CFI.getOffset ());
163+ case MCCFIInstruction::OpAdjustCfaOffset:
164+ return setCFAOffset (FRE, CFI.getLoc (), FRE.CFAOffset + CFI.getOffset ());
165+ case MCCFIInstruction::OpRememberState:
166+ // TODO: Implement. Will use FDE.
167+ return true ;
168+ case MCCFIInstruction::OpRestore:
169+ // TODO: Implement. Will use FDE.
170+ return true ;
171+ case MCCFIInstruction::OpRestoreState:
172+ // TODO: Implement. Will use FDE.
173+ return true ;
174+ case MCCFIInstruction::OpEscape:
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+
83184public:
84185 SFrameEmitterImpl (MCObjectStreamer &Streamer) : Streamer(Streamer) {
85186 assert (Streamer.getContext ()
@@ -88,13 +189,96 @@ class SFrameEmitterImpl {
88189 .has_value ());
89190 FDEs.reserve (Streamer.getDwarfFrameInfos ().size ());
90191 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+
91208 FDESubSectionStart = Streamer.getContext ().createTempSymbol ();
92209 FRESubSectionStart = Streamer.getContext ().createTempSymbol ();
93210 FRESubSectionEnd = Streamer.getContext ().createTempSymbol ();
94211 }
95212
96- void BuildSFDE (const MCDwarfFrameInfo &DF) {
97- FDEs.emplace_back (DF, Streamer.getContext ().createTempSymbol ());
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);
98282 }
99283
100284 void emitPreamble () {
@@ -116,7 +300,9 @@ class SFrameEmitterImpl {
116300 // shf_num_fdes
117301 Streamer.emitInt32 (FDEs.size ());
118302 // shf_num_fres
119- Streamer.emitInt32 (0 );
303+ uint32_t TotalFREs = 0 ;
304+ Streamer.emitInt32 (TotalFREs);
305+
120306 // shf_fre_len
121307 Streamer.emitAbsoluteSymbolDiff (FRESubSectionEnd, FRESubSectionStart,
122308 sizeof (int32_t ));
@@ -161,7 +347,7 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
161347 // Both the header itself and the FDEs include various offsets and counts.
162348 // Therefore, all of this must be precomputed.
163349 for (const auto &DFrame : FrameArray)
164- Emitter.BuildSFDE (DFrame);
350+ Emitter.buildSFDE (DFrame);
165351
166352 MCSection *Section = Context.getObjectFileInfo ()->getSFrameSection ();
167353 // Not strictly necessary, but gas always aligns to 8, so match that.
0 commit comments