From ef1e2ab328087c61a6878c1e84f4fc5d710aebce Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 30 Jan 2015 14:14:42 -0800 Subject: Initial commit to populate CoreCLR repo [tfs-changeset: 1407945] --- src/jit/unwindarm64.cpp | 792 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 792 insertions(+) create mode 100644 src/jit/unwindarm64.cpp (limited to 'src/jit/unwindarm64.cpp') diff --git a/src/jit/unwindarm64.cpp b/src/jit/unwindarm64.cpp new file mode 100644 index 0000000000..af93a02101 --- /dev/null +++ b/src/jit/unwindarm64.cpp @@ -0,0 +1,792 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX UnwindInfo XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +#include "jitpch.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#if defined(_TARGET_ARM64_) + +void Compiler::unwindPush(regNumber reg) +{ + unreached(); // use one of the unwindSaveReg* functions instead. +} + +void Compiler::unwindAllocStack(unsigned size) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + assert(size % 16 == 0); + unsigned x = size / 16; + + if (x <= 0x1F) + { + // alloc_s: 000xxxxx: allocate small stack with size < 128 (2^5 * 16) + // TODO-Review: should say size < 512 + + pu->AddCode((BYTE)x); + } + else if (x <= 0x7FF) + { + // alloc_m: 11000xxx | xxxxxxxx: allocate large stack with size < 16k (2^11 * 16) + // TODO-Review: should say size < 32K + + pu->AddCode(0xC0 | (BYTE)(x >> 8), + (BYTE)x); + } + else + { + // alloc_l: 11100000 | xxxxxxxx | xxxxxxxx | xxxxxxxx : allocate large stack with size < 256M (2^24 * 16) + // + // For large stack size, the most significant bits + // are stored first (and next to the opCode) per the unwind spec. + + pu->AddCode(0xE0, + (BYTE)(x >> 16), + (BYTE)(x >> 8), + (BYTE)x); + } +} + +void Compiler::unwindSetFrameReg(regNumber reg, unsigned offset) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + if (offset == 0) + { + assert(reg == REG_FP); + + // set_fp: 11100001 : set up r29 : with : mov r29, sp + pu->AddCode(0xE1); + } + else + { + // add_fp: 11100010 | xxxxxxxx : set up r29 with : add r29, sp, #x * 8 + + assert(reg == REG_FP); + assert((offset % 8) == 0); + + unsigned x = offset / 8; + assert(x <= 0xFF); + + pu->AddCode(0xE2, (BYTE)x); + } +} + +void Compiler::unwindSaveReg(regNumber reg, unsigned offset) +{ + unreached(); +} + +void Compiler::unwindNop() +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + +#ifdef DEBUG + if (verbose) + { + printf("unwindNop: adding NOP\n"); + } +#endif + + INDEBUG(pu->uwiAddingNOP = true); + + // nop: 11100011: no unwind operation is required. + pu->AddCode(0xE3); + + INDEBUG(pu->uwiAddingNOP = false); +} + +// unwindSaveRegPair: save a register pair to the stack at the specified byte offset (which must be positive, +// a multiple of 8 from 0 to 504). Note that for ARM64 unwind codes, reg2 must be exactly one register higher than reg1, +// 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 +// can't be even, such as R20, because that would mean R19 was saved separately, instead of saving as a pair, +// which we should do instead). +void Compiler::unwindSaveRegPair(regNumber reg1, regNumber reg2, int offset) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + // stp reg1, reg2, [sp, #offset] + + // offset for store pair in prolog must be positive and a multiple of 8. + assert(0 <= offset && + offset <= 504); + assert((offset % 8) == 0); + + int z = offset / 8; + assert(0 <= z && z <= 0x3F); + + if (reg1 == REG_FP) + { + // save_fplr: 01zzzzzz: save pair at [sp+#Z*8], offset <= 504 + + assert(reg2 == REG_LR); + + pu->AddCode(0x40 | (BYTE)z); + } + else if (reg2 == REG_LR) + { + // save_lrpair: 1101011x | xxzzzzzz: save pair at [sp + #Z * 8], offset <= 504 + + assert(REG_R19 <= reg1 && // first legal pair: R19, LR + reg1 <= REG_R27); // last legal pair: R27, LR + + BYTE x = (BYTE)(reg1 - REG_R19); + assert((x % 2) == 0); // only legal reg1: R19, R21, R23, R25, R27 + x /= 2; + assert(0 <= x && x <= 0x7); + + pu->AddCode(0xD6 | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } + else if (emitter::isGeneralRegister(reg1)) + { + // save_regp: 110010xx | xxzzzzzz: save r(19 + #X) pair at [sp + #Z * 8], offset <= 504 + + assert(REG_NEXT(reg1) == reg2); + assert(REG_R19 <= reg1 && // first legal pair: R19, R20 + reg1 <= REG_R27); // last legal pair: R27, R28 (FP is never saved without LR) + + BYTE x = (BYTE)(reg1 - REG_R19); + assert(0 <= x && x <= 0xF); + + pu->AddCode(0xC8 | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } + else + { + // save_fregp: 1101100x | xxzzzzzz : save pair d(8 + #X) at [sp + #Z * 8], offset <= 504 + + assert(REG_NEXT(reg1) == reg2); + assert(REG_V8 <= reg1 && // first legal pair: V8, V9 + reg1 <= REG_V14); // last legal pair: V14, V15 + + BYTE x = (BYTE)(reg1 - REG_V8); + assert(0 <= x && x <= 0x7); + + pu->AddCode(0xD8 | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } +} + +// unwindSaveRegPairPreindexed: save a register pair to the stack at the specified byte offset (which must be negative, +// a multiple of 8 from -512 to -8). Note that for ARM64 unwind codes, reg2 must be exactly one register higher than reg1. +void Compiler::unwindSaveRegPairPreindexed(regNumber reg1, regNumber reg2, int offset) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + // stp reg1, reg2, [sp, #offset]! + + // pre-indexed offset in prolog must be negative and a multiple of 8. + assert(offset < 0); + assert((offset % 8) == 0); + + if (reg1 == REG_FP) + { + // save_fplr_x: 10zzzzzz: save pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 + + assert(-512 <= offset); + int z = (-offset) / 8 - 1; + assert(0 <= z && z <= 0x3F); + + assert(reg2 == REG_LR); + + pu->AddCode(0x80 | (BYTE)z); + } + else if ((reg1 == REG_R19) && (-256 <= offset)) // If the offset is between -512 and -256, we use the save_regp_x unwind code. + { + // save_r19r20_x: 001zzzzz: save pair at [sp-#Z*8]!, pre-indexed offset >= -248 + // NOTE: I'm not sure why we allow Z==0 here; seems useless, and the calculation of offset is different from the other cases. + + int z = (-offset) / 8; + assert(0 <= z && z <= 0x1F); + + assert(reg2 == REG_R20); + + pu->AddCode(0x20 | (BYTE)z); + } + else if (emitter::isGeneralRegister(reg1)) + { + // save_regp_x: 110011xx | xxzzzzzz: save pair r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 + + assert(-512 <= offset); + int z = (-offset) / 8 - 1; + assert(0 <= z && z <= 0x3F); + + assert(REG_NEXT(reg1) == reg2); + assert(REG_R19 <= reg1 && // first legal pair: R19, R20 + reg1 <= REG_R27); // last legal pair: R27, R28 (FP is never saved without LR) + + BYTE x = (BYTE)(reg1 - REG_R19); + assert(0 <= x && x <= 0xF); + + pu->AddCode(0xCC | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } + else + { + // save_fregp_x: 1101101x | xxzzzzzz : save pair d(8 + #X), at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 + + assert(-512 <= offset); + int z = (-offset) / 8 - 1; + assert(0 <= z && z <= 0x3F); + + assert(REG_NEXT(reg1) == reg2); + assert(REG_V8 <= reg1 && // first legal pair: V8, V9 + reg1 <= REG_V14); // last legal pair: V14, V15 + + BYTE x = (BYTE)(reg1 - REG_V8); + assert(0 <= x && x <= 0x7); + + pu->AddCode(0xDA | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } +} + +void Compiler::unwindSaveReg(regNumber reg, int offset) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + // str reg, [sp, #offset] + + // offset for store in prolog must be positive and a multiple of 8. + assert(0 <= offset && + offset <= 504); + assert((offset % 8) == 0); + + int z = offset / 8; + assert(0 <= z && z <= 0x3F); + + if (emitter::isGeneralRegister(reg)) + { + // save_reg: 110100xx | xxzzzzzz: save reg r(19 + #X) at [sp + #Z * 8], offset <= 504 + + assert(REG_R19 <= reg && // first legal register: R19 + reg <= REG_LR); // last legal register: LR + + BYTE x = (BYTE)(reg - REG_R19); + assert(0 <= x && x <= 0xF); + + pu->AddCode(0xD0 | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } + else + { + // save_freg: 1101110x | xxzzzzzz : save reg d(8 + #X) at [sp + #Z * 8], offset <= 504 + + assert(REG_V8 <= reg && // first legal register: V8 + reg <= REG_V15); // last legal register: V15 + + BYTE x = (BYTE)(reg - REG_V8); + assert(0 <= x && x <= 0x7); + + pu->AddCode(0xDC | (BYTE)(x >> 2), + (BYTE)(x << 6) | (BYTE)z); + } +} + +void Compiler::unwindSaveRegPreindexed(regNumber reg, int offset) +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + // str reg, [sp, #offset]! + + // pre-indexed offset in prolog must be negative and a multiple of 8. + assert(-256 <= offset && + offset < 0); + assert((offset % 8) == 0); + + int z = (-offset) / 8 - 1; + assert(0 <= z && z <= 0x1F); + + if (emitter::isGeneralRegister(reg)) + { + // save_reg_x: 1101010x | xxxzzzzz: save reg r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -256 + + assert(REG_R19 <= reg && // first legal register: R19 + reg <= REG_LR); // last legal register: LR + + BYTE x = (BYTE)(reg - REG_R19); + assert(0 <= x && x <= 0xF); + + pu->AddCode(0xD4 | (BYTE)(x >> 3), + (BYTE)(x << 5) | (BYTE)z); + } + else + { + // save_freg_x: 11011110 | xxxzzzzz : save reg d(8 + #X) at [sp - (#Z + 1) * 8]!, pre - indexed offset >= -256 + + assert(REG_V8 <= reg && // first legal register: V8 + reg <= REG_V15); // last legal register: V15 + + BYTE x = (BYTE)(reg - REG_V8); + assert(0 <= x && x <= 0x7); + + pu->AddCode(0xDE, + (BYTE)(x << 5) | (BYTE)z); + } +} + +void Compiler::unwindSaveNext() +{ + UnwindInfo * pu = &funCurrentFunc()->uwi; + + // We're saving the next register pair. The caller is responsible for ensuring this is correct! + + // save_next: 11100110 : save next non - volatile Int or FP register pair. + pu->AddCode(0xE6); +} + +void Compiler::unwindReturn(regNumber reg) +{ + // Nothing to do; we will always have at least one trailing "end" opcode in our padding. +} + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Unwind Info Debug helpers XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +#ifdef DEBUG + +// Return the size of the unwind code (from 1 to 4 bytes), given the first byte of the unwind bytes + +unsigned GetUnwindSizeFromUnwindHeader(BYTE b1) +{ + static BYTE s_UnwindSize[256] = { // array of unwind sizes, in bytes (as specified in the ARM unwind specification) + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00-0F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10-1F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20-2F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30-3F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40-4F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50-5F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60-6F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70-7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80-8F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90-9F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0-AF + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0-BF + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0-CF + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, // D0-DF + 4, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E0-EF + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F0-FF + }; + + unsigned size = s_UnwindSize[b1]; + assert(1 <= size && size <= 4); + return size; +} + +#endif // DEBUG + + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Unwind Info Support Classes XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +/////////////////////////////////////////////////////////////////////////////// +// +// UnwindCodesBase +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG + +// Walk the prolog codes and calculate the size of the prolog or epilog, in bytes. +unsigned UnwindCodesBase::GetCodeSizeFromUnwindCodes(bool isProlog) +{ + BYTE* pCodesStart = GetCodes(); + BYTE* pCodes = pCodesStart; + unsigned size = 0; + for (;;) + { + BYTE b1 = *pCodes; + if (IsEndCode(b1)) + { + break; // We hit an "end" code; we're done + } + size += 4; // All codes represent 4 byte instructions. + pCodes += GetUnwindSizeFromUnwindHeader(b1); + assert(pCodes - pCodesStart < 256); // 255 is the absolute maximum number of code bytes allowed + } + return size; +} + +#endif // DEBUG + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX Debug dumpers XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +#ifdef DEBUG + +// start is 0-based index from LSB, length is number of bits +DWORD ExtractBits(DWORD dw, DWORD start, DWORD length) +{ + return (dw >> start) & ((1 << length) - 1); +} + +// Dump the unwind data. +// Arguments: +// isHotCode: true if this unwind data is for the hot section +// startOffset: byte offset of the code start that this unwind data represents +// endOffset: byte offset of the code end that this unwind data represents +// pHeader: pointer to the unwind data blob +// unwindBlockSize: size in bytes of the unwind data blob + +void DumpUnwindInfo(Compiler* comp, bool isHotCode, UNATIVE_OFFSET startOffset, UNATIVE_OFFSET endOffset, const BYTE * const pHeader, ULONG unwindBlockSize) +{ + printf("Unwind Info%s:\n", isHotCode ? "" : " COLD"); + + // pHeader is not guaranteed to be aligned. We put four 0xFF end codes at the end + // to provide padding, and round down to get a multiple of 4 bytes in size. + DWORD UNALIGNED* pdw = (DWORD UNALIGNED *)pHeader; + DWORD dw; + + dw = *pdw++; + + DWORD codeWords = ExtractBits(dw, 27, 5); + DWORD epilogCount = ExtractBits(dw, 22, 5); + DWORD EBit = ExtractBits(dw, 21, 1); + DWORD XBit = ExtractBits(dw, 20, 1); + DWORD Vers = ExtractBits(dw, 18, 2); + DWORD functionLength = ExtractBits(dw, 0, 18); + + printf(" >> Start offset : 0x%06x (not in unwind data)\n", comp->dspOffset(startOffset)); + printf(" >> End offset : 0x%06x (not in unwind data)\n", comp->dspOffset(endOffset)); + printf(" Code Words : %u\n", codeWords); + printf(" Epilog Count : %u\n", epilogCount); + printf(" E bit : %u\n", EBit); + printf(" X bit : %u\n", XBit); + printf(" Vers : %u\n", Vers); + printf(" Function Length : %u (0x%05x) Actual length = %u (0x%06x)\n", + functionLength, functionLength, + functionLength * 4, functionLength * 4); + + assert(functionLength * 4 == endOffset - startOffset); + + if (codeWords == 0 && epilogCount == 0) + { + // We have an extension word specifying a larger number of Code Words or Epilog Counts + // than can be specified in the header word. + + dw = *pdw++; + + codeWords = ExtractBits(dw, 16, 8); + epilogCount = ExtractBits(dw, 0, 16); + assert((dw & 0xF0000000) == 0); // reserved field should be zero + + printf(" ---- Extension word ----\n"); + printf(" Extended Code Words : %u\n", codeWords); + printf(" Extended Epilog Count : %u\n", epilogCount); + } + + bool epilogStartAt[1024] = {}; // One byte per possible epilog start index; initialized to false + + if (EBit == 0) + { + // We have an array of epilog scopes + + printf(" ---- Epilog scopes ----\n"); + if (epilogCount == 0) + { + printf(" No epilogs\n"); + } + else + { + for (DWORD scope = 0; scope < epilogCount; scope++) + { + dw = *pdw++; + + DWORD epilogStartOffset = ExtractBits(dw, 0, 18); + DWORD res = ExtractBits(dw, 18, 4); + DWORD epilogStartIndex = ExtractBits(dw, 22, 10); + + // Note that epilogStartOffset for a funclet is the offset from the beginning + // of the current funclet, not the offset from the beginning of the main function. + // To help find it when looking through JitDump output, also show the offset from + // the beginning of the main function. + DWORD epilogStartOffsetFromMainFunctionBegin = epilogStartOffset * 4 + startOffset; + + assert(res == 0); + + printf(" ---- Scope %d\n", scope); + printf(" Epilog Start Offset : %u (0x%05x) Actual offset = %u (0x%06x) Offset from main function begin = %u (0x%06x)\n", + comp->dspOffset(epilogStartOffset), comp->dspOffset(epilogStartOffset), + comp->dspOffset(epilogStartOffset * 4), comp->dspOffset(epilogStartOffset * 4), + comp->dspOffset(epilogStartOffsetFromMainFunctionBegin), comp->dspOffset(epilogStartOffsetFromMainFunctionBegin)); + printf(" Epilog Start Index : %u (0x%02x)\n", epilogStartIndex, epilogStartIndex); + + epilogStartAt[epilogStartIndex] = true; // an epilog starts at this offset in the unwind codes + } + } + } + else + { + printf(" --- One epilog, unwind codes at %u\n", epilogCount); + assert(epilogCount < ArrLen(epilogStartAt)); + epilogStartAt[epilogCount] = true; // the one and only epilog starts its unwind codes at this offset + } + + // Dump the unwind codes + + printf(" ---- Unwind codes ----\n"); + + DWORD countOfUnwindCodes = codeWords * 4; + PBYTE pUnwindCode = (PBYTE)pdw; + BYTE b1, b2, b3, b4; + DWORD x, z; + for (DWORD i = 0; i < countOfUnwindCodes; i++) + { + // Does this byte start an epilog sequence? If so, note that fact. + if (epilogStartAt[i]) + { + printf(" ---- Epilog start at index %u ----\n", i); + } + + b1 = *pUnwindCode++; + + if ((b1 & 0xE0) == 0) + { + // alloc_s: 000xxxxx: allocate small stack with size < 128 (2^5 * 16) + // TODO-Review:should say size < 512 + x = b1 & 0x1F; + printf(" %02X alloc_s #%u (0x%02X); sub sp, sp, #%u (0x%03X)\n", b1, x, x, x * 16, x * 16); + } + else if ((b1 & 0xE0) == 0x20) + { + // save_r19r20_x: 001zzzzz: save pair at [sp-#Z*8]!, pre-indexed offset >= -248 + z = b1 & 0x1F; + printf(" %02X save_r19r20_x #%u (0x%02X); stp %s, %s, [sp, #-%u]!\n", b1, z, z, getRegName(REG_R19), getRegName(REG_R20), z * 8); + } + else if ((b1 & 0xC0) == 0x40) + { + // save_fplr: 01zzzzzz: save pair at [sp+#Z*8], offset <= 504 + z = b1 & 0x3F; + printf(" %02X save_fplr #%u (0x%02X); stp %s, %s, [sp, #%u]\n", b1, z, z, getRegName(REG_FP), getRegName(REG_LR), z * 8); + } + else if ((b1 & 0xC0) == 0x80) + { + // save_fplr_x: 10zzzzzz: save pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 + z = b1 & 0x3F; + printf(" %02X save_fplr_x #%u (0x%02X); stp %s, %s, [sp, #-%u]!\n", b1, z, z, getRegName(REG_FP), getRegName(REG_LR), (z + 1) * 8); + } + else if ((b1 & 0xF8) == 0xC0) + { + // alloc_m: 11000xxx | xxxxxxxx: allocate large stack with size < 16k (2^11 * 16) + // TODO-Review: should save size < 32K + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x7) << 8) | (DWORD)b2; + + printf(" %02X %02X alloc_m #%u (0x%03X); sub sp, sp, #%u (0x%04X)\n", b1, b2, x, x, x * 16, x * 16); + } + else if ((b1 & 0xFC) == 0xC8) + { + // save_regp: 110010xx | xxzzzzzz: save r(19 + #X) pair at [sp + #Z * 8], offset <= 504 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_regp X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n", b1, b2, x, z, z, getRegName(REG_R19 + x), getRegName(REG_R19 + x + 1), z * 8); + } + else if ((b1 & 0xFC) == 0xCC) + { + // save_regp_x: 110011xx | xxzzzzzz: save pair r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_regp_x X#%u Z#%u (0x%02X); stp %s, %s, [sp, #-%u]!\n", b1, b2, x, z, z, getRegName(REG_R19 + x), getRegName(REG_R19 + x + 1), (z + 1) * 8); + } + else if ((b1 & 0xFC) == 0xD0) + { + // save_reg: 110100xx | xxzzzzzz: save reg r(19 + #X) at [sp + #Z * 8], offset <= 504 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x3) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_reg X#%u Z#%u (0x%02X); str %s, [sp, #%u]\n", b1, b2, x, z, z, getRegName(REG_R19 + x), z * 8); + } + else if ((b1 & 0xFE) == 0xD4) + { + // save_reg_x: 1101010x | xxxzzzzz: save reg r(19 + #X) at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -256 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x1) << 3) | (DWORD)(b2 >> 5); + z = (DWORD)(b2 & 0x1F); + + printf(" %02X %02X save_reg_x X#%u Z#%u (0x%02X); str %s, [sp, #-%u]!\n", b1, b2, x, z, z, getRegName(REG_R19 + x), (z + 1) * 8); + } + else if ((b1 & 0xFE) == 0xD6) + { + // save_lrpair: 1101011x | xxzzzzzz: save pair at [sp + #Z * 8], offset <= 504 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_lrpair X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n", b1, b2, x, z, z, getRegName(REG_R19 + 2 * x), getRegName(REG_LR), z * 8); + } + else if ((b1 & 0xFE) == 0xD8) + { + // save_fregp: 1101100x | xxzzzzzz : save pair d(8 + #X) at [sp + #Z * 8], offset <= 504 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_fregp X#%u Z#%u (0x%02X); stp %s, %s, [sp, #%u]\n", b1, b2, x, z, z, getRegName(REG_V8 + x, true), getRegName(REG_V8 + x + 1, true), z * 8); + } + else if ((b1 & 0xFE) == 0xDA) + { + // save_fregp_x: 1101101x | xxzzzzzz : save pair d(8 + #X), at [sp - (#Z + 1) * 8]!, pre-indexed offset >= -512 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_fregp_x X#%u Z#%u (0x%02X); stp %s, %s, [sp, #-%u]!\n", b1, b2, x, z, z, getRegName(REG_V8 + x, true), getRegName(REG_V8 + x + 1, true), (z + 1) * 8); + } + else if ((b1 & 0xFE) == 0xDC) + { + // save_freg: 1101110x | xxzzzzzz : save reg d(8 + #X) at [sp + #Z * 8], offset <= 504 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = ((DWORD)(b1 & 0x1) << 2) | (DWORD)(b2 >> 6); + z = (DWORD)(b2 & 0x3F); + + printf(" %02X %02X save_freg X#%u Z#%u (0x%02X); str %s, [sp, #%u]\n", b1, b2, x, z, z, getRegName(REG_V8 + x, true), z * 8); + } + else if (b1 == 0xDE) + { + // save_freg_x: 11011110 | xxxzzzzz : save reg d(8 + #X) at [sp - (#Z + 1) * 8]!, pre - indexed offset >= -256 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = (DWORD)(b2 >> 5); + z = (DWORD)(b2 & 0x1F); + + printf(" %02X %02X save_freg_x X#%u Z#%u (0x%02X); str %s, [sp, #-%u]!\n", b1, b2, x, z, z, getRegName(REG_V8 + x, true), (z + 1) * 8); + } + else if (b1 == 0xE0) + { + // alloc_l: 11100000 | xxxxxxxx | xxxxxxxx | xxxxxxxx : allocate large stack with size < 256M (2^24 * 16) + assert(i + 3 < countOfUnwindCodes); + b2 = *pUnwindCode++; + b3 = *pUnwindCode++; + b4 = *pUnwindCode++; + i += 3; + + x = ((DWORD)b2 << 16) | ((DWORD)b3 << 8) | (DWORD)b4; + + printf(" %02X %02X %02X %02X alloc_l %u (0x%06X); sub sp, sp, #%u (%06X)\n", b1, b2, b3, b4, x, x, x * 16, x * 16); + } + else if (b1 == 0xE1) + { + // set_fp: 11100001 : set up r29 : with : mov r29, sp + + printf(" %02X set_fp; mov %s, sp\n", b1, getRegName(REG_FP)); + } + else if (b1 == 0xE2) + { + // add_fp: 11100010 | xxxxxxxx : set up r29 with : add r29, sp, #x * 8 + assert(i + 1 < countOfUnwindCodes); + b2 = *pUnwindCode++; + i++; + + x = (DWORD)b2; + + printf(" %02X %02X add_fp %u (0x%02X); add %s, sp, #%u\n", b1, b2, x, x, getRegName(REG_FP), x * 8); + } + else if (b1 == 0xE3) + { + // nop: 11100011: no unwind operation is required. + + printf(" %02X nop\n", b1); + } + else if (b1 == 0xE4) + { + // end: 11100100 : end of unwind code + + printf(" %02X end\n", b1); + } + else if (b1 == 0xE5) + { + // end_c: 11100101 : end of unwind code in current chained scope. + + printf(" %02X end_c\n", b1); + } + else if (b1 == 0xE6) + { + // save_next: 11100110 : save next non - volatile Int or FP register pair. + + printf(" %02X save_next\n", b1); + } + else + { + // Unknown / reserved unwind code + assert(!"Internal error decoding unwind codes"); + } + } + + pdw += codeWords; + assert((PBYTE)pdw == pUnwindCode); + assert((PBYTE)pdw == pHeader + unwindBlockSize); + + assert(XBit == 0); // We don't handle the case where exception data is present, such as the Exception Handler RVA + + printf("\n"); +} + +#endif // DEBUG + +#endif // _TARGET_ARM64_ -- cgit v1.2.3