@@ -21,13 +21,33 @@ using namespace sframe;
21
21
22
22
namespace {
23
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.
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
+
24
42
// High-level structure to track info needed to emit a sframe_func_desc_entry
25
43
// and its associated FREs.
26
44
struct SFrameFDE {
27
45
// Reference to the original dwarf frame to avoid copying.
28
46
const MCDwarfFrameInfo &DFrame;
29
47
// Label where this FDE's FREs start.
30
48
MCSymbol *FREStart;
49
+ // Unwinding fres
50
+ SmallVector<SFrameFRE> FREs;
31
51
32
52
SFrameFDE (const MCDwarfFrameInfo &DF, MCSymbol *FRES)
33
53
: DFrame(DF), FREStart(FRES) {}
@@ -53,7 +73,8 @@ struct SFrameFDE {
53
73
MCFixup::getDataKindForSize (4 )));
54
74
S.emitInt32 (0 );
55
75
56
- // sfde_func_start_num_fres
76
+ // sfde_func_num_fres
77
+ // TODO: When we actually emit fres, replace 0 with FREs.size()
57
78
S.emitInt32 (0 );
58
79
59
80
// sfde_func_info word
@@ -76,10 +97,90 @@ class SFrameEmitterImpl {
76
97
MCObjectStreamer &Streamer;
77
98
SmallVector<SFrameFDE> FDEs;
78
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;
79
106
MCSymbol *FDESubSectionStart;
80
107
MCSymbol *FRESubSectionStart;
81
108
MCSymbol *FRESubSectionEnd;
82
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 ()) {
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
+
83
184
public:
84
185
SFrameEmitterImpl (MCObjectStreamer &Streamer) : Streamer(Streamer) {
85
186
assert (Streamer.getContext ()
@@ -88,13 +189,96 @@ class SFrameEmitterImpl {
88
189
.has_value ());
89
190
FDEs.reserve (Streamer.getDwarfFrameInfos ().size ());
90
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
+
91
208
FDESubSectionStart = Streamer.getContext ().createTempSymbol ();
92
209
FRESubSectionStart = Streamer.getContext ().createTempSymbol ();
93
210
FRESubSectionEnd = Streamer.getContext ().createTempSymbol ();
94
211
}
95
212
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);
98
282
}
99
283
100
284
void emitPreamble () {
@@ -116,7 +300,9 @@ class SFrameEmitterImpl {
116
300
// shf_num_fdes
117
301
Streamer.emitInt32 (FDEs.size ());
118
302
// shf_num_fres
119
- Streamer.emitInt32 (0 );
303
+ uint32_t TotalFREs = 0 ;
304
+ Streamer.emitInt32 (TotalFREs);
305
+
120
306
// shf_fre_len
121
307
Streamer.emitAbsoluteSymbolDiff (FRESubSectionEnd, FRESubSectionStart,
122
308
sizeof (int32_t ));
@@ -161,7 +347,7 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
161
347
// Both the header itself and the FDEs include various offsets and counts.
162
348
// Therefore, all of this must be precomputed.
163
349
for (const auto &DFrame : FrameArray)
164
- Emitter.BuildSFDE (DFrame);
350
+ Emitter.buildSFDE (DFrame);
165
351
166
352
MCSection *Section = Context.getObjectFileInfo ()->getSFrameSection ();
167
353
// Not strictly necessary, but gas always aligns to 8, so match that.
0 commit comments