Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / unwind.cpp
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
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX                              UnwindInfo                                   XX
9 XX                                                                           XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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 //
52 void 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
123 void 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
129 void Compiler::unwindPushPopCFI(regNumber reg)
130 {
131 #if defined(_TARGET_ARM_)
132     assert(compGeneratingEpilog);
133 #else
134     assert(compGeneratingProlog);
135 #endif
136
137     FuncInfoDsc* func     = funCurrentFunc();
138     unsigned int cbProlog = 0;
139     if (compGeneratingProlog)
140     {
141         cbProlog = unwindGetCurrentOffset(func);
142         noway_assert((BYTE)cbProlog == cbProlog);
143
144         createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES == 8 ? 8 : 4);
145     }
146
147     if ((RBM_CALLEE_SAVED & genRegMask(reg))
148 #if defined(UNIX_AMD64_ABI)
149 #if ETW_EBP_FRAMED
150         // In case of ETW_EBP_FRAMED defined the REG_FPBASE (RBP)
151         // is excluded from the callee-save register list.
152         // Make sure the register gets PUSH unwind info in this case,
153         // since it is pushed as a frame register.
154         || (reg == REG_FPBASE)
155 #endif // ETW_EBP_FRAMED
156 #endif
157             )
158     {
159         createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg));
160     }
161 }
162
163 template <typename T>
164 inline static T* allocate_any(jitstd::allocator<void>& alloc, size_t count = 5)
165 {
166     return jitstd::allocator<T>(alloc).allocate(count);
167 }
168
169 typedef jitstd::vector<CFI_CODE> CFICodeVector;
170
171 void Compiler::unwindBegPrologCFI()
172 {
173     assert(compGeneratingProlog);
174
175 #if FEATURE_EH_FUNCLETS
176     FuncInfoDsc* func = funCurrentFunc();
177
178     // There is only one prolog for a function/funclet, and it comes first. So now is
179     // a good time to initialize all the unwind data structures.
180
181     unwindGetFuncLocations(func, true, &func->startLoc, &func->endLoc);
182
183     if (fgFirstColdBlock != nullptr)
184     {
185         unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc);
186     }
187
188     jitstd::allocator<void> allocator(getAllocator());
189
190     func->cfiCodes = new (allocate_any<CFICodeVector>(allocator), jitstd::placement_t()) CFICodeVector(allocator);
191 #endif // FEATURE_EH_FUNCLETS
192 }
193
194 void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat)
195 {
196     regMaskTP regBit = isFloat ? genRegMask(REG_FP_FIRST) : 1;
197
198     for (regNumber regNum = isFloat ? REG_FP_FIRST : REG_FIRST; regNum < REG_COUNT;
199          regNum           = REG_NEXT(regNum), regBit <<= 1)
200     {
201         if (regBit > regMask)
202         {
203             break;
204         }
205
206         if (regBit & regMask)
207         {
208             unwindPushPopCFI(regNum);
209         }
210     }
211 }
212
213 void Compiler::unwindAllocStackCFI(unsigned size)
214 {
215 #if defined(_TARGET_ARM_)
216     assert(compGeneratingEpilog);
217 #else
218     assert(compGeneratingProlog);
219 #endif
220     FuncInfoDsc* func     = funCurrentFunc();
221     unsigned int cbProlog = 0;
222     if (compGeneratingProlog)
223     {
224         cbProlog = unwindGetCurrentOffset(func);
225         noway_assert((BYTE)cbProlog == cbProlog);
226     }
227     createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, size);
228 }
229
230 //------------------------------------------------------------------------
231 // Compiler::unwindSetFrameRegCFI: Record a cfi info for a frame register set.
232 //
233 // Arguments:
234 //    reg    - The register being set as the frame register.
235 //    offset - The offset from the current stack pointer that the frame pointer will point at.
236 //
237 void Compiler::unwindSetFrameRegCFI(regNumber reg, unsigned offset)
238 {
239 #if defined(_TARGET_ARM_)
240     assert(compGeneratingEpilog);
241 #else
242     assert(compGeneratingProlog);
243 #endif
244     FuncInfoDsc* func     = funCurrentFunc();
245     unsigned int cbProlog = 0;
246     if (compGeneratingProlog)
247     {
248         cbProlog = unwindGetCurrentOffset(func);
249         noway_assert((BYTE)cbProlog == cbProlog);
250     }
251
252     createCfiCode(func, cbProlog, CFI_DEF_CFA_REGISTER, mapRegNumToDwarfReg(reg));
253     if (offset != 0)
254     {
255         // before: cfa = rsp + old_cfa_offset;
256         //         rbp = rsp + offset;
257         // after: cfa should be based on rbp, but points to the old address:
258         //         rsp + old_cfa_offset == rbp + old_cfa_offset + adjust;
259         // adjust = -offset;
260         int adjust = -(int)offset;
261         createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, adjust);
262     }
263 }
264
265 void Compiler::unwindEmitFuncCFI(FuncInfoDsc* func, void* pHotCode, void* pColdCode)
266 {
267     UNATIVE_OFFSET startOffset;
268     UNATIVE_OFFSET endOffset;
269     DWORD          unwindCodeBytes = 0;
270     BYTE*          pUnwindBlock    = nullptr;
271
272     if (func->startLoc == nullptr)
273     {
274         startOffset = 0;
275     }
276     else
277     {
278         startOffset = func->startLoc->CodeOffset(genEmitter);
279     }
280
281     if (func->endLoc == nullptr)
282     {
283         endOffset = info.compNativeCodeSize;
284     }
285     else
286     {
287         endOffset = func->endLoc->CodeOffset(genEmitter);
288     }
289
290     DWORD size = (DWORD)func->cfiCodes->size();
291     if (size > 0)
292     {
293         unwindCodeBytes = size * sizeof(CFI_CODE);
294         pUnwindBlock    = (BYTE*)&(*func->cfiCodes)[0];
295     }
296
297 #ifdef DEBUG
298     if (opts.dspUnwind)
299     {
300         DumpCfiInfo(true /*isHotCode*/, startOffset, endOffset, unwindCodeBytes, (const CFI_CODE* const)pUnwindBlock);
301     }
302 #endif // DEBUG
303
304     assert(endOffset <= info.compTotalHotCodeSize);
305
306     eeAllocUnwindInfo((BYTE*)pHotCode, nullptr /* pColdCode */, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
307                       (CorJitFuncKind)func->funKind);
308
309     if (pColdCode != nullptr)
310     {
311         assert(fgFirstColdBlock != nullptr);
312         assert(func->funKind == FUNC_ROOT); // No splitting of funclets.
313
314         unwindCodeBytes = 0;
315         pUnwindBlock    = nullptr;
316
317         if (func->coldStartLoc == nullptr)
318         {
319             startOffset = 0;
320         }
321         else
322         {
323             startOffset = func->coldStartLoc->CodeOffset(genEmitter);
324         }
325
326         if (func->coldEndLoc == nullptr)
327         {
328             endOffset = info.compNativeCodeSize;
329         }
330         else
331         {
332             endOffset = func->coldEndLoc->CodeOffset(genEmitter);
333         }
334
335 #ifdef DEBUG
336         if (opts.dspUnwind)
337         {
338             DumpCfiInfo(false /*isHotCode*/, startOffset, endOffset, unwindCodeBytes,
339                         (const CFI_CODE* const)pUnwindBlock);
340         }
341 #endif // DEBUG
342
343         assert(startOffset >= info.compTotalHotCodeSize);
344         startOffset -= info.compTotalHotCodeSize;
345         endOffset -= info.compTotalHotCodeSize;
346
347         eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
348                           (CorJitFuncKind)func->funKind);
349     }
350 }
351
352 #ifdef DEBUG
353 //------------------------------------------------------------------------
354 // DumpCfiInfo: Dump the Cfi data.
355 //
356 // Arguments:
357 //    isHotCode   - true if this cfi data is for the hot section, false otherwise.
358 //    startOffset - byte offset of the code start that this cfi data represents.
359 //    endOffset   - byte offset of the code end   that this cfi data represents.
360 //    pcFiCode    - pointer to the cfi data blob.
361 //
362 void Compiler::DumpCfiInfo(bool                  isHotCode,
363                            UNATIVE_OFFSET        startOffset,
364                            UNATIVE_OFFSET        endOffset,
365                            DWORD                 cfiCodeBytes,
366                            const CFI_CODE* const pCfiCode)
367 {
368     printf("Cfi Info%s:\n", isHotCode ? "" : " COLD");
369     printf("  >> Start offset   : 0x%06x \n", dspOffset(startOffset));
370     printf("  >>   End offset   : 0x%06x \n", dspOffset(endOffset));
371
372     for (int i = 0; i < (int)(cfiCodeBytes / sizeof(CFI_CODE)); i++)
373     {
374         const CFI_CODE* const pCode = &(pCfiCode[i]);
375
376         UCHAR codeOffset = pCode->CodeOffset;
377         SHORT dwarfReg   = pCode->DwarfReg;
378         INT   offset     = pCode->Offset;
379
380         switch (pCode->CfiOpCode)
381         {
382             case CFI_REL_OFFSET:
383                 printf("    CodeOffset: 0x%02X Op: RelOffset DwarfReg:0x%x Offset:0x%X\n", codeOffset, dwarfReg,
384                        offset);
385                 break;
386             case CFI_DEF_CFA_REGISTER:
387                 assert(offset == 0);
388                 printf("    CodeOffset: 0x%02X Op: DefCfaRegister DwarfReg:0x%X\n", codeOffset, dwarfReg);
389                 break;
390             case CFI_ADJUST_CFA_OFFSET:
391                 assert(dwarfReg == DWARF_REG_ILLEGAL);
392                 printf("    CodeOffset: 0x%02X Op: AdjustCfaOffset Offset:0x%X\n", codeOffset, offset);
393                 break;
394             default:
395                 printf("    Unrecognized CFI_CODE: 0x%IX\n", *(UINT64*)pCode);
396                 break;
397         }
398     }
399 }
400 #endif // DEBUG
401
402 #endif // _TARGET_UNIX_
403
404 //------------------------------------------------------------------------
405 // Compiler::unwindGetCurrentOffset: Calculate the current byte offset of the
406 // prolog being generated.
407 //
408 // Arguments:
409 //    func - The main function or funclet of interest.
410 //
411 // Return Value:
412 //    The byte offset of the prolog currently being generated.
413 //
414 UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func)
415 {
416     assert(compGeneratingProlog);
417     UNATIVE_OFFSET offset;
418     if (func->funKind == FUNC_ROOT)
419     {
420         offset = genEmitter->emitGetPrologOffsetEstimate();
421     }
422     else
423     {
424 #if defined(_TARGET_AMD64_)
425         assert(func->startLoc != nullptr);
426         offset = func->startLoc->GetFuncletPrologOffset(genEmitter);
427 #else
428         offset = 0; // TODO ???
429 #endif
430     }
431
432     return offset;
433 }
434
435 #if defined(_TARGET_AMD64_)
436
437 // See unwindAmd64.cpp
438
439 #elif defined(_TARGET_ARM64_)
440
441 // See unwindArm64.cpp
442
443 #elif defined(_TARGET_ARM_)
444
445 // See unwindArm.cpp
446
447 #elif defined(_TARGET_X86_)
448
449 // See unwindX86.cpp
450
451 #else // _TARGET_*
452
453 #error Unsupported or unset target architecture
454
455 #endif // _TARGET_*