| 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 | |