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 defined(_TARGET_ARM64_) |
20 | |
21 | #if defined(_TARGET_UNIX_) |
22 | int Compiler::mapRegNumToDwarfReg(regNumber reg) |
23 | { |
24 | int dwarfReg = DWARF_REG_ILLEGAL; |
25 | |
26 | NYI("CFI codes" ); |
27 | |
28 | return dwarfReg; |
29 | } |
30 | #endif // _TARGET_ARM_ |
31 | |
32 | void Compiler::unwindPush(regNumber reg) |
33 | { |
34 | unreached(); // use one of the unwindSaveReg* functions instead. |
35 | } |
36 | |
37 | void Compiler::unwindAllocStack(unsigned size) |
38 | { |
39 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
40 | |
41 | assert(size % 16 == 0); |
42 | unsigned x = size / 16; |
43 | |
44 | if (x <= 0x1F) |
45 | { |
46 | // alloc_s: 000xxxxx: allocate small stack with size < 128 (2^5 * 16) |
47 | // TODO-Review: should say size < 512 |
48 | |
49 | pu->AddCode((BYTE)x); |
50 | } |
51 | else if (x <= 0x7FF) |
52 | { |
53 | // alloc_m: 11000xxx | xxxxxxxx: allocate large stack with size < 16k (2^11 * 16) |
54 | // TODO-Review: should say size < 32K |
55 | |
56 | pu->AddCode(0xC0 | (BYTE)(x >> 8), (BYTE)x); |
57 | } |
58 | else |
59 | { |
60 | // alloc_l: 11100000 | xxxxxxxx | xxxxxxxx | xxxxxxxx : allocate large stack with size < 256M (2^24 * 16) |
61 | // |
62 | // For large stack size, the most significant bits |
63 | // are stored first (and next to the opCode) per the unwind spec. |
64 | |
65 | pu->AddCode(0xE0, (BYTE)(x >> 16), (BYTE)(x >> 8), (BYTE)x); |
66 | } |
67 | } |
68 | |
69 | void Compiler::unwindSetFrameReg(regNumber reg, unsigned offset) |
70 | { |
71 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
72 | |
73 | if (offset == 0) |
74 | { |
75 | assert(reg == REG_FP); |
76 | |
77 | // set_fp: 11100001 : set up r29 : with : mov r29, sp |
78 | pu->AddCode(0xE1); |
79 | } |
80 | else |
81 | { |
82 | // add_fp: 11100010 | xxxxxxxx : set up r29 with : add r29, sp, #x * 8 |
83 | |
84 | assert(reg == REG_FP); |
85 | assert((offset % 8) == 0); |
86 | |
87 | unsigned x = offset / 8; |
88 | assert(x <= 0xFF); |
89 | |
90 | pu->AddCode(0xE2, (BYTE)x); |
91 | } |
92 | } |
93 | |
94 | void Compiler::unwindSaveReg(regNumber reg, unsigned offset) |
95 | { |
96 | unreached(); |
97 | } |
98 | |
99 | void Compiler::unwindNop() |
100 | { |
101 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
102 | |
103 | #ifdef DEBUG |
104 | if (verbose) |
105 | { |
106 | printf("unwindNop: adding NOP\n" ); |
107 | } |
108 | #endif |
109 | |
110 | INDEBUG(pu->uwiAddingNOP = true); |
111 | |
112 | // nop: 11100011: no unwind operation is required. |
113 | pu->AddCode(0xE3); |
114 | |
115 | INDEBUG(pu->uwiAddingNOP = false); |
116 | } |
117 | |
118 | // unwindSaveRegPair: save a register pair to the stack at the specified byte offset (which must be positive, |
119 | // a multiple of 8 from 0 to 504). Note that for ARM64 unwind codes, reg2 must be exactly one register higher than reg1, |
120 | // except for the case of a pair including LR, in which case reg1 must be either FP or R19/R21/R23/R25/R27 (note that it |
121 | // can't be even, such as R20, because that would mean R19 was saved separately, instead of saving <R19,R20> as a pair, |
122 | // which we should do instead). |
123 | void Compiler::unwindSaveRegPair(regNumber reg1, regNumber reg2, int offset) |
124 | { |
125 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
126 | |
127 | // stp reg1, reg2, [sp, #offset] |
128 | |
129 | // offset for store pair in prolog must be positive and a multiple of 8. |
130 | assert(0 <= offset && offset <= 504); |
131 | assert((offset % 8) == 0); |
132 | |
133 | int z = offset / 8; |
134 | assert(0 <= z && z <= 0x3F); |
135 | |
136 | if (reg1 == REG_FP) |
137 | { |
138 | // save_fplr: 01zzzzzz: save <r29,lr> pair at [sp+#Z*8], offset <= 504 |
139 | |
140 | assert(reg2 == REG_LR); |
141 | |
142 | pu->AddCode(0x40 | (BYTE)z); |
143 | } |
144 | else if (reg2 == REG_LR) |
145 | { |
146 | // save_lrpair: 1101011x | xxzzzzzz: save pair <r19 + 2 * #X, lr> at [sp + #Z * 8], offset <= 504 |
147 | |
148 | assert(REG_R19 <= reg1 && // first legal pair: R19, LR |
149 | reg1 <= REG_R27); // last legal pair: R27, LR |
150 | |
151 | BYTE x = (BYTE)(reg1 - REG_R19); |
152 | assert((x % 2) == 0); // only legal reg1: R19, R21, R23, R25, R27 |
153 | x /= 2; |
154 | assert(0 <= x && x <= 0x7); |
155 | |
156 | pu->AddCode(0xD6 | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
157 | } |
158 | else if (emitter::isGeneralRegister(reg1)) |
159 | { |
160 | // save_regp: 110010xx | xxzzzzzz: save r(19 + #X) pair at [sp + #Z * 8], offset <= 504 |
161 | |
162 | assert(REG_NEXT(reg1) == reg2); |
163 | assert(REG_R19 <= reg1 && // first legal pair: R19, R20 |
164 | reg1 <= REG_R27); // last legal pair: R27, R28 (FP is never saved without LR) |
165 | |
166 | BYTE x = (BYTE)(reg1 - REG_R19); |
167 | assert(0 <= x && x <= 0xF); |
168 | |
169 | pu->AddCode(0xC8 | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
170 | } |
171 | else |
172 | { |
173 | // save_fregp: 1101100x | xxzzzzzz : save pair d(8 + #X) at [sp + #Z * 8], offset <= 504 |
174 | |
175 | assert(REG_NEXT(reg1) == reg2); |
176 | assert(REG_V8 <= reg1 && // first legal pair: V8, V9 |
177 | reg1 <= REG_V14); // last legal pair: V14, V15 |
178 | |
179 | BYTE x = (BYTE)(reg1 - REG_V8); |
180 | assert(0 <= x && x <= 0x7); |
181 | |
182 | pu->AddCode(0xD8 | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
183 | } |
184 | } |
185 | |
186 | // unwindSaveRegPairPreindexed: save a register pair to the stack at the specified byte offset (which must be negative, |
187 | // a multiple of 8 from -512 to -8). Note that for ARM64 unwind codes, reg2 must be exactly one register higher than |
188 | // reg1. |
189 | void Compiler::unwindSaveRegPairPreindexed(regNumber reg1, regNumber reg2, int offset) |
190 | { |
191 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
192 | |
193 | // stp reg1, reg2, [sp, #offset]! |
194 | |
195 | // pre-indexed offset in prolog must be negative and a multiple of 8. |
196 | assert(offset < 0); |
197 | assert((offset % 8) == 0); |
198 | |
199 | if (reg1 == REG_FP) |
200 | { |
201 | // save_fplr_x: 10zzzzzz: save <r29,lr> pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 |
202 | |
203 | assert(-512 <= offset); |
204 | int z = (-offset) / 8 - 1; |
205 | assert(0 <= z && z <= 0x3F); |
206 | |
207 | assert(reg2 == REG_LR); |
208 | |
209 | pu->AddCode(0x80 | (BYTE)z); |
210 | } |
211 | else if ((reg1 == REG_R19) && |
212 | (-256 <= offset)) // If the offset is between -512 and -256, we use the save_regp_x unwind code. |
213 | { |
214 | // save_r19r20_x: 001zzzzz: save <r19,r20> pair at [sp-#Z*8]!, pre-indexed offset >= -248 |
215 | // NOTE: I'm not sure why we allow Z==0 here; seems useless, and the calculation of offset is different from the |
216 | // other cases. |
217 | |
218 | int z = (-offset) / 8; |
219 | assert(0 <= z && z <= 0x1F); |
220 | |
221 | assert(reg2 == REG_R20); |
222 | |
223 | pu->AddCode(0x20 | (BYTE)z); |
224 | } |
225 | else if (emitter::isGeneralRegister(reg1)) |
226 | { |
227 | // save_regp_x: 110011xx | xxzzzzzz: save pair r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 |
228 | |
229 | assert(-512 <= offset); |
230 | int z = (-offset) / 8 - 1; |
231 | assert(0 <= z && z <= 0x3F); |
232 | |
233 | assert(REG_NEXT(reg1) == reg2); |
234 | assert(REG_R19 <= reg1 && // first legal pair: R19, R20 |
235 | reg1 <= REG_R27); // last legal pair: R27, R28 (FP is never saved without LR) |
236 | |
237 | BYTE x = (BYTE)(reg1 - REG_R19); |
238 | assert(0 <= x && x <= 0xF); |
239 | |
240 | pu->AddCode(0xCC | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
241 | } |
242 | else |
243 | { |
244 | // save_fregp_x: 1101101x | xxzzzzzz : save pair d(8 + #X), at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 |
245 | |
246 | assert(-512 <= offset); |
247 | int z = (-offset) / 8 - 1; |
248 | assert(0 <= z && z <= 0x3F); |
249 | |
250 | assert(REG_NEXT(reg1) == reg2); |
251 | assert(REG_V8 <= reg1 && // first legal pair: V8, V9 |
252 | reg1 <= REG_V14); // last legal pair: V14, V15 |
253 | |
254 | BYTE x = (BYTE)(reg1 - REG_V8); |
255 | assert(0 <= x && x <= 0x7); |
256 | |
257 | pu->AddCode(0xDA | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
258 | } |
259 | } |
260 | |
261 | void Compiler::unwindSaveReg(regNumber reg, int offset) |
262 | { |
263 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
264 | |
265 | // str reg, [sp, #offset] |
266 | |
267 | // offset for store in prolog must be positive and a multiple of 8. |
268 | assert(0 <= offset && offset <= 504); |
269 | assert((offset % 8) == 0); |
270 | |
271 | int z = offset / 8; |
272 | assert(0 <= z && z <= 0x3F); |
273 | |
274 | if (emitter::isGeneralRegister(reg)) |
275 | { |
276 | // save_reg: 110100xx | xxzzzzzz: save reg r(19 + #X) at [sp + #Z * 8], offset <= 504 |
277 | |
278 | assert(REG_R19 <= reg && // first legal register: R19 |
279 | reg <= REG_LR); // last legal register: LR |
280 | |
281 | BYTE x = (BYTE)(reg - REG_R19); |
282 | assert(0 <= x && x <= 0xF); |
283 | |
284 | pu->AddCode(0xD0 | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
285 | } |
286 | else |
287 | { |
288 | // save_freg: 1101110x | xxzzzzzz : save reg d(8 + #X) at [sp + #Z * 8], offset <= 504 |
289 | |
290 | assert(REG_V8 <= reg && // first legal register: V8 |
291 | reg <= REG_V15); // last legal register: V15 |
292 | |
293 | BYTE x = (BYTE)(reg - REG_V8); |
294 | assert(0 <= x && x <= 0x7); |
295 | |
296 | pu->AddCode(0xDC | (BYTE)(x >> 2), (BYTE)(x << 6) | (BYTE)z); |
297 | } |
298 | } |
299 | |
300 | void Compiler::unwindSaveRegPreindexed(regNumber reg, int offset) |
301 | { |
302 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
303 | |
304 | // str reg, [sp, #offset]! |
305 | |
306 | // pre-indexed offset in prolog must be negative and a multiple of 8. |
307 | assert(-256 <= offset && offset < 0); |
308 | assert((offset % 8) == 0); |
309 | |
310 | int z = (-offset) / 8 - 1; |
311 | assert(0 <= z && z <= 0x1F); |
312 | |
313 | if (emitter::isGeneralRegister(reg)) |
314 | { |
315 | // save_reg_x: 1101010x | xxxzzzzz: save reg r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -256 |
316 | |
317 | assert(REG_R19 <= reg && // first legal register: R19 |
318 | reg <= REG_LR); // last legal register: LR |
319 | |
320 | BYTE x = (BYTE)(reg - REG_R19); |
321 | assert(0 <= x && x <= 0xF); |
322 | |
323 | pu->AddCode(0xD4 | (BYTE)(x >> 3), (BYTE)(x << 5) | (BYTE)z); |
324 | } |
325 | else |
326 | { |
327 | // save_freg_x: 11011110 | xxxzzzzz : save reg d(8 + #X) at [sp - (#Z + 1) * 8]!, pre - indexed offset >= -256 |
328 | |
329 | assert(REG_V8 <= reg && // first legal register: V8 |
330 | reg <= REG_V15); // last legal register: V15 |
331 | |
332 | BYTE x = (BYTE)(reg - REG_V8); |
333 | assert(0 <= x && x <= 0x7); |
334 | |
335 | pu->AddCode(0xDE, (BYTE)(x << 5) | (BYTE)z); |
336 | } |
337 | } |
338 | |
339 | void Compiler::unwindSaveNext() |
340 | { |
341 | UnwindInfo* pu = &funCurrentFunc()->uwi; |
342 | |
343 | // We're saving the next register pair. The caller is responsible for ensuring this is correct! |
344 | |
345 | // save_next: 11100110 : save next non - volatile Int or FP register pair. |
346 | pu->AddCode(0xE6); |
347 | } |
348 | |
349 | void Compiler::unwindReturn(regNumber reg) |
350 | { |
351 | // Nothing to do; we will always have at least one trailing "end" opcode in our padding. |
352 | } |
353 | |
354 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
355 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
356 | XX XX |
357 | XX Unwind Info Debug helpers XX |
358 | XX XX |
359 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
360 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
361 | */ |
362 | |
363 | #ifdef DEBUG |
364 | |
365 | // Return the size of the unwind code (from 1 to 4 bytes), given the first byte of the unwind bytes |
366 | |
367 | unsigned (BYTE b1) |
368 | { |
369 | static BYTE s_UnwindSize[256] = { |
370 | // array of unwind sizes, in bytes (as specified in the ARM unwind specification) |
371 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00-0F |
372 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10-1F |
373 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20-2F |
374 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30-3F |
375 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40-4F |
376 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50-5F |
377 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60-6F |
378 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70-7F |
379 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80-8F |
380 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90-9F |
381 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0-AF |
382 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0-BF |
383 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0-CF |
384 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, // D0-DF |
385 | 4, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E0-EF |
386 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F0-FF |
387 | }; |
388 | |
389 | unsigned size = s_UnwindSize[b1]; |
390 | assert(1 <= size && size <= 4); |
391 | return size; |
392 | } |
393 | |
394 | #endif // DEBUG |
395 | |
396 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
397 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
398 | XX XX |
399 | XX Unwind Info Support Classes XX |
400 | XX XX |
401 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
402 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
403 | */ |
404 | |
405 | /////////////////////////////////////////////////////////////////////////////// |
406 | // |
407 | // UnwindCodesBase |
408 | // |
409 | /////////////////////////////////////////////////////////////////////////////// |
410 | |
411 | #ifdef DEBUG |
412 | |
413 | // Walk the prolog codes and calculate the size of the prolog or epilog, in bytes. |
414 | unsigned UnwindCodesBase::GetCodeSizeFromUnwindCodes(bool isProlog) |
415 | { |
416 | BYTE* pCodesStart = GetCodes(); |
417 | BYTE* pCodes = pCodesStart; |
418 | unsigned size = 0; |
419 | for (;;) |
420 | { |
421 | BYTE b1 = *pCodes; |
422 | if (IsEndCode(b1)) |
423 | { |
424 | break; // We hit an "end" code; we're done |
425 | } |
426 | size += 4; // All codes represent 4 byte instructions. |
427 | pCodes += GetUnwindSizeFromUnwindHeader(b1); |
428 | assert(pCodes - pCodesStart < 256); // 255 is the absolute maximum number of code bytes allowed |
429 | } |
430 | return size; |
431 | } |
432 | |
433 | #endif // DEBUG |
434 | |
435 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
436 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
437 | XX XX |
438 | XX Debug dumpers XX |
439 | XX XX |
440 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
441 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
442 | */ |
443 | |
444 | #ifdef DEBUG |
445 | |
446 | // start is 0-based index from LSB, length is number of bits |
447 | DWORD (DWORD dw, DWORD start, DWORD length) |
448 | { |
449 | return (dw >> start) & ((1 << length) - 1); |
450 | } |
451 | |
452 | // Dump the unwind data. |
453 | // Arguments: |
454 | // isHotCode: true if this unwind data is for the hot section |
455 | // startOffset: byte offset of the code start that this unwind data represents |
456 | // endOffset: byte offset of the code end that this unwind data represents |
457 | // pHeader: pointer to the unwind data blob |
458 | // unwindBlockSize: size in bytes of the unwind data blob |
459 | |
460 | void DumpUnwindInfo(Compiler* comp, |
461 | bool isHotCode, |
462 | UNATIVE_OFFSET startOffset, |
463 | UNATIVE_OFFSET endOffset, |
464 | const BYTE* const , |
465 | ULONG unwindBlockSize) |
466 | { |
467 | printf("Unwind Info%s:\n" , isHotCode ? "" : " COLD" ); |
468 | |
469 | // pHeader is not guaranteed to be aligned. We put four 0xFF end codes at the end |
470 | // to provide padding, and round down to get a multiple of 4 bytes in size. |
471 | DWORD UNALIGNED* pdw = (DWORD UNALIGNED*)pHeader; |
472 | DWORD dw; |
473 | |
474 | dw = *pdw++; |
475 | |
476 | DWORD codeWords = ExtractBits(dw, 27, 5); |
477 | DWORD epilogCount = ExtractBits(dw, 22, 5); |
478 | DWORD EBit = ExtractBits(dw, 21, 1); |
479 | DWORD XBit = ExtractBits(dw, 20, 1); |
480 | DWORD Vers = ExtractBits(dw, 18, 2); |
481 | DWORD functionLength = ExtractBits(dw, 0, 18); |
482 | |
483 | printf(" >> Start offset : 0x%06x (not in unwind data)\n" , comp->dspOffset(startOffset)); |
484 | printf(" >> End offset : 0x%06x (not in unwind data)\n" , comp->dspOffset(endOffset)); |
485 | printf(" Code Words : %u\n" , codeWords); |
486 | printf(" Epilog Count : %u\n" , epilogCount); |
487 | printf(" E bit : %u\n" , EBit); |
488 | printf(" X bit : %u\n" , XBit); |
489 | printf(" Vers : %u\n" , Vers); |
490 | printf(" Function Length : %u (0x%05x) Actual length = %u (0x%06x)\n" , functionLength, functionLength, |
491 | functionLength * 4, functionLength * 4); |
492 | |
493 | assert(functionLength * 4 == endOffset - startOffset); |
494 | |
495 | if (codeWords == 0 && epilogCount == 0) |
496 | { |
497 | // We have an extension word specifying a larger number of Code Words or Epilog Counts |
498 | // than can be specified in the header word. |
499 | |
500 | dw = *pdw++; |
501 | |
502 | codeWords = ExtractBits(dw, 16, 8); |
503 | epilogCount = ExtractBits(dw, 0, 16); |
504 | assert((dw & 0xF0000000) == 0); // reserved field should be zero |
505 | |
506 | printf(" ---- Extension word ----\n" ); |
507 | printf(" Extended Code Words : %u\n" , codeWords); |
508 | printf(" Extended Epilog Count : %u\n" , epilogCount); |
509 | } |
510 | |
511 | bool epilogStartAt[1024] = {}; // One byte per possible epilog start index; initialized to false |
512 | |
513 | if (EBit == 0) |
514 | { |
515 | // We have an array of epilog scopes |
516 | |
517 | printf(" ---- Epilog scopes ----\n" ); |
518 | if (epilogCount == 0) |
519 | { |
520 | printf(" No epilogs\n" ); |
521 | } |
522 | else |
523 | { |
524 | for (DWORD scope = 0; scope < epilogCount; scope++) |
525 | { |
526 | dw = *pdw++; |
527 | |
528 | DWORD epilogStartOffset = ExtractBits(dw, 0, 18); |
529 | DWORD res = ExtractBits(dw, 18, 4); |
530 | DWORD epilogStartIndex = ExtractBits(dw, 22, 10); |
531 | |
532 | // Note that epilogStartOffset for a funclet is the offset from the beginning |
533 | // of the current funclet, not the offset from the beginning of the main function. |
534 | // To help find it when looking through JitDump output, also show the offset from |
535 | // the beginning of the main function. |
536 | DWORD epilogStartOffsetFromMainFunctionBegin = epilogStartOffset * 4 + startOffset; |
537 | |
538 | assert(res == 0); |
539 | |
540 | printf(" ---- Scope %d\n" , scope); |
541 | printf(" Epilog Start Offset : %u (0x%05x) Actual offset = %u (0x%06x) Offset from main " |
542 | "function begin = %u (0x%06x)\n" , |
543 | comp->dspOffset(epilogStartOffset), comp->dspOffset(epilogStartOffset), |
544 | comp->dspOffset(epilogStartOffset * 4), comp->dspOffset(epilogStartOffset * 4), |
545 | comp->dspOffset(epilogStartOffsetFromMainFunctionBegin), |
546 | comp->dspOffset(epilogStartOffsetFromMainFunctionBegin)); |
547 | printf(" Epilog Start Index : %u (0x%02x)\n" , epilogStartIndex, epilogStartIndex); |
548 | |
549 | epilogStartAt[epilogStartIndex] = true; // an epilog starts at this offset in the unwind codes |
550 | } |
551 | } |
552 | } |
553 | else |
554 | { |
555 | printf(" --- One epilog, unwind codes at %u\n" , epilogCount); |
556 | assert(epilogCount < ArrLen(epilogStartAt)); |
557 | epilogStartAt[epilogCount] = true; // the one and only epilog starts its unwind codes at this offset |
558 | } |
559 | |
560 | // Dump the unwind codes |
561 | |
562 | printf(" ---- Unwind codes ----\n" ); |
563 | |
564 | DWORD countOfUnwindCodes = codeWords * 4; |
565 | PBYTE pUnwindCode = (PBYTE)pdw; |
566 | BYTE b1, b2, b3, b4; |
567 | DWORD x, z; |
568 | for (DWORD i = 0; i < countOfUnwindCodes; i++) |
569 | { |
570 | // Does this byte start an epilog sequence? If so, note that fact. |
571 | if (epilogStartAt[i]) |
572 | { |
573 | printf(" ---- Epilog start at index %u ----\n" , i); |
574 | } |
575 | |
576 | b1 = *pUnwindCode++; |
577 | |
578 | if ((b1 & 0xE0) == 0) |
579 | { |
580 | // alloc_s: 000xxxxx: allocate small stack with size < 128 (2^5 * 16) |
581 | // TODO-Review:should say size < 512 |
582 | x = b1 & 0x1F; |
583 | printf(" %02X alloc_s #%u (0x%02X); sub sp, sp, #%u (0x%03X)\n" , b1, x, x, x * 16, x * 16); |
584 | } |
585 | else if ((b1 & 0xE0) == 0x20) |
586 | { |
587 | // save_r19r20_x: 001zzzzz: save <r19,r20> pair at [sp-#Z*8]!, pre-indexed offset >= -248 |
588 | z = b1 & 0x1F; |
589 | printf(" %02X save_r19r20_x #%u (0x%02X); stp %s, %s, [sp, #-%u]!\n" , b1, z, z, |
590 | getRegName(REG_R19), getRegName(REG_R20), z * 8); |
591 | } |
592 | else if ((b1 & 0xC0) == 0x40) |
593 | { |
594 | // save_fplr: 01zzzzzz: save <r29,lr> pair at [sp+#Z*8], offset <= 504 |
595 | z = b1 & 0x3F; |
596 | printf(" %02X save_fplr #%u (0x%02X); stp %s, %s, [sp, #%u]\n" , b1, z, z, getRegName(REG_FP), |
597 | getRegName(REG_LR), z * 8); |
598 | } |
599 | else if ((b1 & 0xC0) == 0x80) |
600 | { |
601 | // save_fplr_x: 10zzzzzz: save <r29,lr> pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 |
602 | z = b1 & 0x3F; |
603 | printf(" %02X save_fplr_x #%u (0x%02X); stp %s, %s, [sp, #-%u]!\n" , b1, z, z, |
604 | getRegName(REG_FP), getRegName(REG_LR), (z + 1) * 8); |
605 | } |
606 | else if ((b1 & 0xF8) == 0xC0) |
607 | { |
608 | // alloc_m: 11000xxx | xxxxxxxx: allocate large stack with size < 16k (2^11 * 16) |
609 | // TODO-Review: should save size < 32K |
610 | assert(i + 1 < countOfUnwindCodes); |
611 | b2 = *pUnwindCode++; |
612 | i++; |
613 | |
614 | x = ((DWORD)(b1 & 0x7) << 8) | (DWORD)b2; |
615 | |
616 | printf(" %02X %02X alloc_m #%u (0x%03X); sub sp, sp, #%u (0x%04X)\n" , b1, b2, x, x, x * 16, |
617 | x * 16); |
618 | } |
619 | else if ((b1 & 0xFC) == 0xC8) |
620 | { |
621 | // save_regp: 110010xx | xxzzzzzz: save r(19 + #X) pair at [sp + #Z * 8], offset <= 504 |
622 | assert(i + 1 < countOfUnwindCodes); |
623 | b2 = *pUnwindCode++; |
624 | i++; |
625 | |
626 | x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); |
627 | z = (DWORD)(b2 & 0x3F); |
628 | |
629 | printf(" %02X %02X save_regp X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n" , b1, b2, x, z, z, |
630 | getRegName(REG_R19 + x), getRegName(REG_R19 + x + 1), z * 8); |
631 | } |
632 | else if ((b1 & 0xFC) == 0xCC) |
633 | { |
634 | // save_regp_x: 110011xx | xxzzzzzz: save pair r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= |
635 | // -512 |
636 | assert(i + 1 < countOfUnwindCodes); |
637 | b2 = *pUnwindCode++; |
638 | i++; |
639 | |
640 | x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); |
641 | z = (DWORD)(b2 & 0x3F); |
642 | |
643 | printf(" %02X %02X save_regp_x X#%u Z#%u (0x%02X); stp %s, %s, [sp, #-%u]!\n" , b1, b2, x, z, z, |
644 | getRegName(REG_R19 + x), getRegName(REG_R19 + x + 1), (z + 1) * 8); |
645 | } |
646 | else if ((b1 & 0xFC) == 0xD0) |
647 | { |
648 | // save_reg: 110100xx | xxzzzzzz: save reg r(19 + #X) at [sp + #Z * 8], offset <= 504 |
649 | assert(i + 1 < countOfUnwindCodes); |
650 | b2 = *pUnwindCode++; |
651 | i++; |
652 | |
653 | x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); |
654 | z = (DWORD)(b2 & 0x3F); |
655 | |
656 | printf(" %02X %02X save_reg X#%u Z#%u (0x%02X); str %s, [sp, #%u]\n" , b1, b2, x, z, z, |
657 | getRegName(REG_R19 + x), z * 8); |
658 | } |
659 | else if ((b1 & 0xFE) == 0xD4) |
660 | { |
661 | // save_reg_x: 1101010x | xxxzzzzz: save reg r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -256 |
662 | assert(i + 1 < countOfUnwindCodes); |
663 | b2 = *pUnwindCode++; |
664 | i++; |
665 | |
666 | x = ((DWORD)(b1 & 0x1) << 3) | (DWORD)(b2 >> 5); |
667 | z = (DWORD)(b2 & 0x1F); |
668 | |
669 | printf(" %02X %02X save_reg_x X#%u Z#%u (0x%02X); str %s, [sp, #-%u]!\n" , b1, b2, x, z, z, |
670 | getRegName(REG_R19 + x), (z + 1) * 8); |
671 | } |
672 | else if ((b1 & 0xFE) == 0xD6) |
673 | { |
674 | // save_lrpair: 1101011x | xxzzzzzz: save pair <r19 + 2 * #X, lr> at [sp + #Z * 8], offset <= 504 |
675 | assert(i + 1 < countOfUnwindCodes); |
676 | b2 = *pUnwindCode++; |
677 | i++; |
678 | |
679 | x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); |
680 | z = (DWORD)(b2 & 0x3F); |
681 | |
682 | printf(" %02X %02X save_lrpair X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n" , b1, b2, x, z, z, |
683 | getRegName(REG_R19 + 2 * x), getRegName(REG_LR), z * 8); |
684 | } |
685 | else if ((b1 & 0xFE) == 0xD8) |
686 | { |
687 | // save_fregp: 1101100x | xxzzzzzz : save pair d(8 + #X) at [sp + #Z * 8], offset <= 504 |
688 | assert(i + 1 < countOfUnwindCodes); |
689 | b2 = *pUnwindCode++; |
690 | i++; |
691 | |
692 | x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); |
693 | z = (DWORD)(b2 & 0x3F); |
694 | |
695 | printf(" %02X %02X save_fregp X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n" , b1, b2, x, z, z, |
696 | getRegName(REG_V8 + x, true), getRegName(REG_V8 + x + 1, true), z * 8); |
697 | } |
698 | else if ((b1 & 0xFE) == 0xDA) |
699 | { |
700 | // save_fregp_x: 1101101x | xxzzzzzz : save pair d(8 + #X), at [sp - (#Z + 1) * 8]!, pre-indexed offset >= |
701 | // -512 |
702 | assert(i + 1 < countOfUnwindCodes); |
703 | b2 = *pUnwindCode++; |
704 | i++; |
705 | |
706 | x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); |
707 | z = (DWORD)(b2 & 0x3F); |
708 | |
709 | printf(" %02X %02X save_fregp_x X#%u Z#%u (0x%02X); stp %s, %s, [sp, #-%u]!\n" , b1, b2, x, z, z, |
710 | getRegName(REG_V8 + x, true), getRegName(REG_V8 + x + 1, true), (z + 1) * 8); |
711 | } |
712 | else if ((b1 & 0xFE) == 0xDC) |
713 | { |
714 | // save_freg: 1101110x | xxzzzzzz : save reg d(8 + #X) at [sp + #Z * 8], offset <= 504 |
715 | assert(i + 1 < countOfUnwindCodes); |
716 | b2 = *pUnwindCode++; |
717 | i++; |
718 | |
719 | x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); |
720 | z = (DWORD)(b2 & 0x3F); |
721 | |
722 | printf(" %02X %02X save_freg X#%u Z#%u (0x%02X); str %s, [sp, #%u]\n" , b1, b2, x, z, z, |
723 | getRegName(REG_V8 + x, true), z * 8); |
724 | } |
725 | else if (b1 == 0xDE) |
726 | { |
727 | // save_freg_x: 11011110 | xxxzzzzz : save reg d(8 + #X) at [sp - (#Z + 1) * 8]!, pre - indexed offset >= |
728 | // -256 |
729 | assert(i + 1 < countOfUnwindCodes); |
730 | b2 = *pUnwindCode++; |
731 | i++; |
732 | |
733 | x = (DWORD)(b2 >> 5); |
734 | z = (DWORD)(b2 & 0x1F); |
735 | |
736 | printf(" %02X %02X save_freg_x X#%u Z#%u (0x%02X); str %s, [sp, #-%u]!\n" , b1, b2, x, z, z, |
737 | getRegName(REG_V8 + x, true), (z + 1) * 8); |
738 | } |
739 | else if (b1 == 0xE0) |
740 | { |
741 | // alloc_l: 11100000 | xxxxxxxx | xxxxxxxx | xxxxxxxx : allocate large stack with size < 256M (2^24 * 16) |
742 | assert(i + 3 < countOfUnwindCodes); |
743 | b2 = *pUnwindCode++; |
744 | b3 = *pUnwindCode++; |
745 | b4 = *pUnwindCode++; |
746 | i += 3; |
747 | |
748 | x = ((DWORD)b2 << 16) | ((DWORD)b3 << 8) | (DWORD)b4; |
749 | |
750 | printf(" %02X %02X %02X %02X alloc_l %u (0x%06X); sub sp, sp, #%u (%06X)\n" , b1, b2, b3, b4, x, x, |
751 | x * 16, x * 16); |
752 | } |
753 | else if (b1 == 0xE1) |
754 | { |
755 | // set_fp: 11100001 : set up r29 : with : mov r29, sp |
756 | |
757 | printf(" %02X set_fp; mov %s, sp\n" , b1, getRegName(REG_FP)); |
758 | } |
759 | else if (b1 == 0xE2) |
760 | { |
761 | // add_fp: 11100010 | xxxxxxxx : set up r29 with : add r29, sp, #x * 8 |
762 | assert(i + 1 < countOfUnwindCodes); |
763 | b2 = *pUnwindCode++; |
764 | i++; |
765 | |
766 | x = (DWORD)b2; |
767 | |
768 | printf(" %02X %02X add_fp %u (0x%02X); add %s, sp, #%u\n" , b1, b2, x, x, getRegName(REG_FP), |
769 | x * 8); |
770 | } |
771 | else if (b1 == 0xE3) |
772 | { |
773 | // nop: 11100011: no unwind operation is required. |
774 | |
775 | printf(" %02X nop\n" , b1); |
776 | } |
777 | else if (b1 == 0xE4) |
778 | { |
779 | // end: 11100100 : end of unwind code |
780 | |
781 | printf(" %02X end\n" , b1); |
782 | } |
783 | else if (b1 == 0xE5) |
784 | { |
785 | // end_c: 11100101 : end of unwind code in current chained scope. |
786 | |
787 | printf(" %02X end_c\n" , b1); |
788 | } |
789 | else if (b1 == 0xE6) |
790 | { |
791 | // save_next: 11100110 : save next non - volatile Int or FP register pair. |
792 | |
793 | printf(" %02X save_next\n" , b1); |
794 | } |
795 | else |
796 | { |
797 | // Unknown / reserved unwind code |
798 | assert(!"Internal error decoding unwind codes" ); |
799 | } |
800 | } |
801 | |
802 | pdw += codeWords; |
803 | assert((PBYTE)pdw == pUnwindCode); |
804 | assert((PBYTE)pdw == pHeader + unwindBlockSize); |
805 | |
806 | assert(XBit == 0); // We don't handle the case where exception data is present, such as the Exception Handler RVA |
807 | |
808 | printf("\n" ); |
809 | } |
810 | |
811 | #endif // DEBUG |
812 | |
813 | #endif // _TARGET_ARM64_ |
814 | |