1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX UnwindInfo XX
9XX XX
10XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12*/
13
14#include "jitpch.h"
15#ifdef _MSC_VER
16#pragma hdrstop
17#endif
18
19#if FEATURE_EH_FUNCLETS
20
21//------------------------------------------------------------------------
22// Compiler::unwindGetFuncLocations: Get the start/end emitter locations for this
23// function or funclet. If 'getHotSectionData' is true, get the start/end locations
24// for the hot section. Otherwise, get the data for the cold section.
25//
26// Note that we grab these locations before the prolog and epilogs are generated, so the
27// locations must remain correct after the prolog and epilogs are generated.
28//
29// For the prolog, instructions are put in the special, preallocated, prolog instruction group.
30// We don't want to expose the emitPrologIG unnecessarily (locations are actually pointers to
31// emitter instruction groups). Since we know the offset of the start of the function/funclet,
32// where the prolog is, will be zero, we use a nullptr start location to indicate that.
33//
34// There is no instruction group beyond the end of the end of the function, so there is no
35// location to indicate that. Once again, use nullptr for that.
36//
37// Intermediate locations point at the first instruction group of a funclet, which is a
38// placeholder IG. These are converted to real IGs, not deleted and replaced, so the location
39// remains valid.
40//
41// Arguments:
42// func - main function or funclet to get locations for.
43// getHotSectionData - 'true' to get the hot section data, 'false' to get the cold section data.
44// ppStartLoc - OUT parameter. Set to the start emitter location.
45// ppEndLoc - OUT parameter. Set to the end emitter location (the location immediately
46// the range; the 'end' location is not inclusive).
47//
48// Notes:
49// A start location of nullptr means the beginning of the code.
50// An end location of nullptr means the end of the code.
51//
52void Compiler::unwindGetFuncLocations(FuncInfoDsc* func,
53 bool getHotSectionData,
54 /* OUT */ emitLocation** ppStartLoc,
55 /* OUT */ emitLocation** ppEndLoc)
56{
57 if (func->funKind == FUNC_ROOT)
58 {
59 // Since all funclets are pulled out of line, the main code size is everything
60 // up to the first handler. If the function is hot/cold split, we need to get the
61 // appropriate sub-range.
62
63 if (getHotSectionData)
64 {
65 *ppStartLoc = nullptr; // nullptr emit location means the beginning of the code. This is to handle the first
66 // fragment prolog.
67
68 if (fgFirstColdBlock != nullptr)
69 {
70 // The hot section only goes up to the cold section
71 assert(fgFirstFuncletBB == nullptr);
72
73 *ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstColdBlock));
74 }
75 else
76 {
77 if (fgFirstFuncletBB != nullptr)
78 {
79 *ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstFuncletBB));
80 }
81 else
82 {
83 *ppEndLoc = nullptr; // nullptr end location means the end of the code
84 }
85 }
86 }
87 else
88 {
89 assert(fgFirstFuncletBB == nullptr); // TODO-CQ: support hot/cold splitting in functions with EH
90 assert(fgFirstColdBlock != nullptr); // There better be a cold section!
91
92 *ppStartLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstColdBlock));
93 *ppEndLoc = nullptr; // nullptr end location means the end of the code
94 }
95 }
96 else
97 {
98 assert(getHotSectionData); // TODO-CQ: support funclets in cold section
99
100 EHblkDsc* HBtab = ehGetDsc(func->funEHIndex);
101
102 if (func->funKind == FUNC_FILTER)
103 {
104 assert(HBtab->HasFilter());
105 *ppStartLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(HBtab->ebdFilter));
106 *ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(HBtab->ebdHndBeg));
107 }
108 else
109 {
110 assert(func->funKind == FUNC_HANDLER);
111 *ppStartLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(HBtab->ebdHndBeg));
112 *ppEndLoc = (HBtab->ebdHndLast->bbNext == nullptr)
113 ? nullptr
114 : new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(HBtab->ebdHndLast->bbNext));
115 }
116 }
117}
118
119#endif // FEATURE_EH_FUNCLETS
120
121#if defined(_TARGET_UNIX_)
122
123void Compiler::createCfiCode(FuncInfoDsc* func, UCHAR codeOffset, UCHAR cfiOpcode, USHORT dwarfReg, INT offset)
124{
125 CFI_CODE cfiEntry(codeOffset, cfiOpcode, dwarfReg, offset);
126 func->cfiCodes->push_back(cfiEntry);
127}
128
129void Compiler::unwindPushPopCFI(regNumber reg)
130{
131 assert(compGeneratingProlog);
132
133 FuncInfoDsc* func = funCurrentFunc();
134 unsigned int cbProlog = unwindGetCurrentOffset(func);
135 noway_assert((BYTE)cbProlog == cbProlog);
136
137 regMaskTP relOffsetMask = RBM_CALLEE_SAVED
138#if defined(UNIX_AMD64_ABI) && ETW_EBP_FRAMED
139 // In case of ETW_EBP_FRAMED defined the REG_FPBASE (RBP)
140 // is excluded from the callee-save register list.
141 // Make sure the register gets PUSH unwind info in this case,
142 // since it is pushed as a frame register.
143 | RBM_FPBASE
144#endif
145#if defined(_TARGET_ARM_)
146 | RBM_R11 | RBM_LR | RBM_PC
147#endif
148 ;
149
150 if (relOffsetMask & genRegMask(reg))
151 {
152 createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg));
153 }
154 else
155 {
156 createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES);
157 }
158}
159
160typedef jitstd::vector<CFI_CODE> CFICodeVector;
161
162void Compiler::unwindBegPrologCFI()
163{
164 assert(compGeneratingProlog);
165
166#if FEATURE_EH_FUNCLETS
167 FuncInfoDsc* func = funCurrentFunc();
168
169 // There is only one prolog for a function/funclet, and it comes first. So now is
170 // a good time to initialize all the unwind data structures.
171
172 unwindGetFuncLocations(func, true, &func->startLoc, &func->endLoc);
173
174 if (fgFirstColdBlock != nullptr)
175 {
176 unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc);
177 }
178
179 func->cfiCodes = new (getAllocator()) CFICodeVector(getAllocator());
180#endif // FEATURE_EH_FUNCLETS
181}
182
183void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat)
184{
185 regMaskTP regBit = isFloat ? genRegMask(REG_FP_FIRST) : 1;
186
187 for (regNumber regNum = isFloat ? REG_FP_FIRST : REG_FIRST; regNum < REG_COUNT;
188 regNum = REG_NEXT(regNum), regBit <<= 1)
189 {
190 if (regBit > regMask)
191 {
192 break;
193 }
194
195 if (regBit & regMask)
196 {
197 unwindPushPopCFI(regNum);
198 }
199 }
200}
201
202void Compiler::unwindAllocStackCFI(unsigned size)
203{
204 assert(compGeneratingProlog);
205 FuncInfoDsc* func = funCurrentFunc();
206 unsigned int cbProlog = 0;
207 if (compGeneratingProlog)
208 {
209 cbProlog = unwindGetCurrentOffset(func);
210 noway_assert((BYTE)cbProlog == cbProlog);
211 }
212 createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, size);
213}
214
215//------------------------------------------------------------------------
216// Compiler::unwindSetFrameRegCFI: Record a cfi info for a frame register set.
217//
218// Arguments:
219// reg - The register being set as the frame register.
220// offset - The offset from the current stack pointer that the frame pointer will point at.
221//
222void Compiler::unwindSetFrameRegCFI(regNumber reg, unsigned offset)
223{
224 assert(compGeneratingProlog);
225 FuncInfoDsc* func = funCurrentFunc();
226 unsigned int cbProlog = unwindGetCurrentOffset(func);
227 noway_assert((BYTE)cbProlog == cbProlog);
228
229 createCfiCode(func, cbProlog, CFI_DEF_CFA_REGISTER, mapRegNumToDwarfReg(reg));
230 if (offset != 0)
231 {
232 // before: cfa = rsp + old_cfa_offset;
233 // rbp = rsp + offset;
234 // after: cfa should be based on rbp, but points to the old address:
235 // rsp + old_cfa_offset == rbp + old_cfa_offset + adjust;
236 // adjust = -offset;
237 int adjust = -(int)offset;
238 createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, adjust);
239 }
240}
241
242void Compiler::unwindEmitFuncCFI(FuncInfoDsc* func, void* pHotCode, void* pColdCode)
243{
244 UNATIVE_OFFSET startOffset;
245 UNATIVE_OFFSET endOffset;
246 DWORD unwindCodeBytes = 0;
247 BYTE* pUnwindBlock = nullptr;
248
249 if (func->startLoc == nullptr)
250 {
251 startOffset = 0;
252 }
253 else
254 {
255 startOffset = func->startLoc->CodeOffset(genEmitter);
256 }
257
258 if (func->endLoc == nullptr)
259 {
260 endOffset = info.compNativeCodeSize;
261 }
262 else
263 {
264 endOffset = func->endLoc->CodeOffset(genEmitter);
265 }
266
267 DWORD size = (DWORD)func->cfiCodes->size();
268 if (size > 0)
269 {
270 unwindCodeBytes = size * sizeof(CFI_CODE);
271 pUnwindBlock = (BYTE*)&(*func->cfiCodes)[0];
272 }
273
274#ifdef DEBUG
275 if (opts.dspUnwind)
276 {
277 DumpCfiInfo(true /*isHotCode*/, startOffset, endOffset, unwindCodeBytes, (const CFI_CODE* const)pUnwindBlock);
278 }
279#endif // DEBUG
280
281 assert(endOffset <= info.compTotalHotCodeSize);
282
283 eeAllocUnwindInfo((BYTE*)pHotCode, nullptr /* pColdCode */, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
284 (CorJitFuncKind)func->funKind);
285
286 if (pColdCode != nullptr)
287 {
288 assert(fgFirstColdBlock != nullptr);
289 assert(func->funKind == FUNC_ROOT); // No splitting of funclets.
290
291 unwindCodeBytes = 0;
292 pUnwindBlock = nullptr;
293
294 if (func->coldStartLoc == nullptr)
295 {
296 startOffset = 0;
297 }
298 else
299 {
300 startOffset = func->coldStartLoc->CodeOffset(genEmitter);
301 }
302
303 if (func->coldEndLoc == nullptr)
304 {
305 endOffset = info.compNativeCodeSize;
306 }
307 else
308 {
309 endOffset = func->coldEndLoc->CodeOffset(genEmitter);
310 }
311
312#ifdef DEBUG
313 if (opts.dspUnwind)
314 {
315 DumpCfiInfo(false /*isHotCode*/, startOffset, endOffset, unwindCodeBytes,
316 (const CFI_CODE* const)pUnwindBlock);
317 }
318#endif // DEBUG
319
320 assert(startOffset >= info.compTotalHotCodeSize);
321 startOffset -= info.compTotalHotCodeSize;
322 endOffset -= info.compTotalHotCodeSize;
323
324 eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
325 (CorJitFuncKind)func->funKind);
326 }
327}
328
329#ifdef DEBUG
330//------------------------------------------------------------------------
331// DumpCfiInfo: Dump the Cfi data.
332//
333// Arguments:
334// isHotCode - true if this cfi data is for the hot section, false otherwise.
335// startOffset - byte offset of the code start that this cfi data represents.
336// endOffset - byte offset of the code end that this cfi data represents.
337// pcFiCode - pointer to the cfi data blob.
338//
339void Compiler::DumpCfiInfo(bool isHotCode,
340 UNATIVE_OFFSET startOffset,
341 UNATIVE_OFFSET endOffset,
342 DWORD cfiCodeBytes,
343 const CFI_CODE* const pCfiCode)
344{
345 printf("Cfi Info%s:\n", isHotCode ? "" : " COLD");
346 printf(" >> Start offset : 0x%06x \n", dspOffset(startOffset));
347 printf(" >> End offset : 0x%06x \n", dspOffset(endOffset));
348
349 for (int i = 0; i < (int)(cfiCodeBytes / sizeof(CFI_CODE)); i++)
350 {
351 const CFI_CODE* const pCode = &(pCfiCode[i]);
352
353 UCHAR codeOffset = pCode->CodeOffset;
354 SHORT dwarfReg = pCode->DwarfReg;
355 INT offset = pCode->Offset;
356
357 switch (pCode->CfiOpCode)
358 {
359 case CFI_REL_OFFSET:
360 printf(" CodeOffset: 0x%02X Op: RelOffset DwarfReg:0x%x Offset:0x%X\n", codeOffset, dwarfReg,
361 offset);
362 break;
363 case CFI_DEF_CFA_REGISTER:
364 assert(offset == 0);
365 printf(" CodeOffset: 0x%02X Op: DefCfaRegister DwarfReg:0x%X\n", codeOffset, dwarfReg);
366 break;
367 case CFI_ADJUST_CFA_OFFSET:
368 assert(dwarfReg == DWARF_REG_ILLEGAL);
369 printf(" CodeOffset: 0x%02X Op: AdjustCfaOffset Offset:0x%X\n", codeOffset, offset);
370 break;
371 default:
372 printf(" Unrecognized CFI_CODE: 0x%IX\n", *(UINT64*)pCode);
373 break;
374 }
375 }
376}
377#endif // DEBUG
378
379#endif // _TARGET_UNIX_
380
381//------------------------------------------------------------------------
382// Compiler::unwindGetCurrentOffset: Calculate the current byte offset of the
383// prolog being generated.
384//
385// Arguments:
386// func - The main function or funclet of interest.
387//
388// Return Value:
389// The byte offset of the prolog currently being generated.
390//
391UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func)
392{
393 assert(compGeneratingProlog);
394 UNATIVE_OFFSET offset;
395 if (func->funKind == FUNC_ROOT)
396 {
397 offset = genEmitter->emitGetPrologOffsetEstimate();
398 }
399 else
400 {
401#if defined(_TARGET_AMD64_) || (defined(_TARGET_UNIX_) && (defined(_TARGET_ARMARCH_) || defined(_TARGET_X86_)))
402 assert(func->startLoc != nullptr);
403 offset = func->startLoc->GetFuncletPrologOffset(genEmitter);
404#else
405 offset = 0; // TODO ???
406#endif
407 }
408
409 return offset;
410}
411
412#if defined(_TARGET_AMD64_)
413
414// See unwindAmd64.cpp
415
416#elif defined(_TARGET_ARM64_)
417
418// See unwindArm64.cpp
419
420#elif defined(_TARGET_ARM_)
421
422// See unwindArm.cpp
423
424#elif defined(_TARGET_X86_)
425
426// See unwindX86.cpp
427
428#else // _TARGET_*
429
430#error Unsupported or unset target architecture
431
432#endif // _TARGET_*
433