diff options
Diffstat (limited to 'src/jit/unwindamd64.cpp')
-rw-r--r-- | src/jit/unwindamd64.cpp | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/src/jit/unwindamd64.cpp b/src/jit/unwindamd64.cpp new file mode 100644 index 0000000000..89abdff2b3 --- /dev/null +++ b/src/jit/unwindamd64.cpp @@ -0,0 +1,1056 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX XX +XX UnwindInfo XX +XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +*/ + +#include "jitpch.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#if defined(_TARGET_AMD64_) +#ifdef UNIX_AMD64_ABI +int Compiler::mapRegNumToDwarfReg(regNumber reg) +{ + int dwarfReg = DWARF_REG_ILLEGAL; + + switch (reg) + { + case REG_RAX: + dwarfReg = 0; + break; + case REG_RCX: + dwarfReg = 2; + break; + case REG_RDX: + dwarfReg = 1; + break; + case REG_RBX: + dwarfReg = 3; + break; + case REG_RSP: + dwarfReg = 7; + break; + case REG_RBP: + dwarfReg = 6; + break; + case REG_RSI: + dwarfReg = 4; + break; + case REG_RDI: + dwarfReg = 5; + break; + case REG_R8: + dwarfReg = 8; + break; + case REG_R9: + dwarfReg = 9; + break; + case REG_R10: + dwarfReg = 10; + break; + case REG_R11: + dwarfReg = 11; + break; + case REG_R12: + dwarfReg = 12; + break; + case REG_R13: + dwarfReg = 13; + break; + case REG_R14: + dwarfReg = 14; + break; + case REG_R15: + dwarfReg = 15; + break; + case REG_XMM0: + dwarfReg = 17; + break; + case REG_XMM1: + dwarfReg = 18; + break; + case REG_XMM2: + dwarfReg = 19; + break; + case REG_XMM3: + dwarfReg = 20; + break; + case REG_XMM4: + dwarfReg = 21; + break; + case REG_XMM5: + dwarfReg = 22; + break; + case REG_XMM6: + dwarfReg = 23; + break; + case REG_XMM7: + dwarfReg = 24; + break; + case REG_XMM8: + dwarfReg = 25; + break; + case REG_XMM9: + dwarfReg = 26; + break; + case REG_XMM10: + dwarfReg = 27; + break; + case REG_XMM11: + dwarfReg = 28; + break; + case REG_XMM12: + dwarfReg = 29; + break; + case REG_XMM13: + dwarfReg = 30; + break; + case REG_XMM14: + dwarfReg = 31; + break; + case REG_XMM15: + dwarfReg = 32; + break; + default: + noway_assert(!"unexpected REG_NUM"); + } + + return dwarfReg; +} + +void Compiler::createCfiCode(FuncInfoDsc* func, UCHAR codeOffset, UCHAR cfiOpcode, USHORT dwarfReg, INT offset) +{ + CFI_CODE cfiEntry(codeOffset, cfiOpcode, dwarfReg, offset); + func->cfiCodes->push_back(cfiEntry); +} +#endif // UNIX_AMD64_ABI + +//------------------------------------------------------------------------ +// Compiler::unwindGetCurrentOffset: Calculate the current byte offset of the +// prolog being generated. +// +// Arguments: +// func - The main function or funclet of interest. +// +// Return Value: +// The byte offset of the prolog currently being generated. +// +UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func) +{ + assert(compGeneratingProlog); + UNATIVE_OFFSET offset; + if (func->funKind == FUNC_ROOT) + { + offset = genEmitter->emitGetPrologOffsetEstimate(); + } + else + { + assert(func->startLoc != nullptr); + offset = func->startLoc->GetFuncletPrologOffset(genEmitter); + } + + return offset; +} + +//------------------------------------------------------------------------ +// Compiler::unwindBegProlog: Initialize the unwind info data structures. +// Called at the beginning of main function or funclet prolog generation. +// +void Compiler::unwindBegProlog() +{ +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindBegPrologCFI(); + } + else +#endif // UNIX_AMD64_ABI + { + unwindBegPrologWindows(); + } +} + +void Compiler::unwindBegPrologWindows() +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + // There is only one prolog for a function/funclet, and it comes first. So now is + // a good time to initialize all the unwind data structures. + + unwindGetFuncLocations(func, true, &func->startLoc, &func->endLoc); + + if (fgFirstColdBlock != nullptr) + { + unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc); + } + + func->unwindCodeSlot = sizeof(func->unwindCodes); + func->unwindHeader.Version = 1; + func->unwindHeader.Flags = 0; + func->unwindHeader.CountOfUnwindCodes = 0; + func->unwindHeader.FrameRegister = 0; + func->unwindHeader.FrameOffset = 0; +} + +#ifdef UNIX_AMD64_ABI +template <typename T> +inline static T* allocate_any(jitstd::allocator<void>& alloc, size_t count = 5) +{ + return jitstd::allocator<T>(alloc).allocate(count); +} +typedef jitstd::vector<CFI_CODE> CFICodeVector; + +void Compiler::unwindBegPrologCFI() +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + // There is only one prolog for a function/funclet, and it comes first. So now is + // a good time to initialize all the unwind data structures. + + unwindGetFuncLocations(func, true, &func->startLoc, &func->endLoc); + + if (fgFirstColdBlock != nullptr) + { + unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc); + } + + jitstd::allocator<void> allocator(getAllocator()); + + func->cfiCodes = new (allocate_any<CFICodeVector>(allocator), jitstd::placement_t()) CFICodeVector(allocator); +} +#endif // UNIX_AMD64_ABI + +//------------------------------------------------------------------------ +// Compiler::unwindEndProlog: Called at the end of main function or funclet +// prolog generation to indicate there is no more unwind information for this prolog. +// +void Compiler::unwindEndProlog() +{ + assert(compGeneratingProlog); +} + +//------------------------------------------------------------------------ +// Compiler::unwindBegEpilog: Called at the beginning of main function or funclet +// epilog generation. +// +void Compiler::unwindBegEpilog() +{ + assert(compGeneratingEpilog); +} + +//------------------------------------------------------------------------ +// Compiler::unwindEndEpilog: Called at the end of main function or funclet +// epilog generation. +// +void Compiler::unwindEndEpilog() +{ + assert(compGeneratingEpilog); +} + +//------------------------------------------------------------------------ +// Compiler::unwindPush: Record a push/save of a register. +// +// Arguments: +// reg - The register being pushed/saved. +// +void Compiler::unwindPush(regNumber reg) +{ +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindPushCFI(reg); + } + else +#endif // UNIX_AMD64_ABI + { + unwindPushWindows(reg); + } +} + +void Compiler::unwindPushWindows(regNumber reg) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog + assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve + assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); + UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + code->CodeOffset = (BYTE)cbProlog; + + if ((RBM_CALLEE_SAVED & genRegMask(reg)) +#if ETW_EBP_FRAMED + // In case of ETW_EBP_FRAMED defined the REG_FPBASE (RBP) + // is excluded from the callee-save register list. + // Make sure the register gets PUSH unwind info in this case, + // since it is pushed as a frame register. + || (reg == REG_FPBASE) +#endif // ETW_EBP_FRAMED + ) + { + code->UnwindOp = UWOP_PUSH_NONVOL; + code->OpInfo = (BYTE)reg; + } + else + { + // Push of a volatile register is just a small stack allocation + code->UnwindOp = UWOP_ALLOC_SMALL; + code->OpInfo = 0; + } +} + +#ifdef UNIX_AMD64_ABI +void Compiler::unwindPushCFI(regNumber reg) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + + createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, 8); + if ((RBM_CALLEE_SAVED & genRegMask(reg)) +#if ETW_EBP_FRAMED + // In case of ETW_EBP_FRAMED defined the REG_FPBASE (RBP) + // is excluded from the callee-save register list. + // Make sure the register gets PUSH unwind info in this case, + // since it is pushed as a frame register. + || (reg == REG_FPBASE) +#endif // ETW_EBP_FRAMED + ) + { + createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg)); + } +} +#endif // UNIX_AMD64_ABI + +//------------------------------------------------------------------------ +// Compiler::unwindAllocStack: Record a stack frame allocation (sub sp, X). +// +// Arguments: +// size - The size of the stack frame allocation (the amount subtracted from the stack pointer). +// +void Compiler::unwindAllocStack(unsigned size) +{ +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindAllocStackCFI(size); + } + else +#endif // UNIX_AMD64_ABI + { + unwindAllocStackWindows(size); + } +} + +void Compiler::unwindAllocStackWindows(unsigned size) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog + assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve + assert(size % 8 == 0); // Stack size is *always* 8 byte aligned + UNWIND_CODE* code; + if (size <= 128) + { + assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); + code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->UnwindOp = UWOP_ALLOC_SMALL; + code->OpInfo = (size - 8) / 8; + } + else if (size <= 0x7FFF8) + { + assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT))); + USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)]; + *codedSize = (USHORT)(size / 8); + code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->UnwindOp = UWOP_ALLOC_LARGE; + code->OpInfo = 0; + } + else + { + assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); + ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; + *codedSize = size; + code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->UnwindOp = UWOP_ALLOC_LARGE; + code->OpInfo = 1; + } + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + code->CodeOffset = (BYTE)cbProlog; +} + +#ifdef UNIX_AMD64_ABI +void Compiler::unwindAllocStackCFI(unsigned size) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, size); +} +#endif // UNIX_AMD64_ABI + +//------------------------------------------------------------------------ +// Compiler::unwindSetFrameReg: Record a frame register. +// +// Arguments: +// reg - The register being set as the frame register. +// offset - The offset from the current stack pointer that the frame pointer will point at. +// +void Compiler::unwindSetFrameReg(regNumber reg, unsigned offset) +{ +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindSetFrameRegCFI(reg, offset); + } + else +#endif // UNIX_AMD64_ABI + { + unwindSetFrameRegWindows(reg, offset); + } +} + +void Compiler::unwindSetFrameRegWindows(regNumber reg, unsigned offset) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog + assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + + func->unwindHeader.FrameRegister = (BYTE)reg; + +#ifdef PLATFORM_UNIX + if (offset > 240) + { + // On Unix only, we have a CLR-only extension to the AMD64 unwind codes: UWOP_SET_FPREG_LARGE. + // It has a 32-bit offset (scaled). You must set UNWIND_INFO.FrameOffset to 15. The 32-bit + // offset follows in 2 UNWIND_CODE fields. + + assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); + ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; + assert(offset % 16 == 0); + *codedSize = offset / 16; + + UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->CodeOffset = (BYTE)cbProlog; + code->OpInfo = 0; + code->UnwindOp = UWOP_SET_FPREG_LARGE; + func->unwindHeader.FrameOffset = 15; + } + else +#endif // PLATFORM_UNIX + { + assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); + UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->CodeOffset = (BYTE)cbProlog; + code->OpInfo = 0; + code->UnwindOp = UWOP_SET_FPREG; + assert(offset <= 240); + assert(offset % 16 == 0); + func->unwindHeader.FrameOffset = offset / 16; + } +} + +#ifdef UNIX_AMD64_ABI +void Compiler::unwindSetFrameRegCFI(regNumber reg, unsigned offset) +{ + assert(compGeneratingProlog); + FuncInfoDsc* func = funCurrentFunc(); + + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + + createCfiCode(func, cbProlog, CFI_DEF_CFA_REGISTER, mapRegNumToDwarfReg(reg)); + if (offset != 0) + { + createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, offset); + } +} +#endif // UNIX_AMD64_ABI + +//------------------------------------------------------------------------ +// Compiler::unwindSaveReg: Record a register save. +// +// Arguments: +// reg - The register being saved. +// offset - The offset from the current stack pointer where the register is being saved. +// +void Compiler::unwindSaveReg(regNumber reg, unsigned offset) +{ +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindSaveRegCFI(reg, offset); + } + else +#endif // UNIX_AMD64_ABI + { + unwindSaveRegWindows(reg, offset); + } +} + +void Compiler::unwindSaveRegWindows(regNumber reg, unsigned offset) +{ + assert(compGeneratingProlog); + + FuncInfoDsc* func = funCurrentFunc(); + + assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog + assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve + if (RBM_CALLEE_SAVED & genRegMask(reg)) + { + UNWIND_CODE* code; + if (offset < 0x80000) + { + assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT))); + USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)]; + code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + + // As per AMD64 ABI, if saving entire xmm reg, then offset need to be scaled by 16. + if (genIsValidFloatReg(reg)) + { + *codedSize = (USHORT)(offset / 16); + code->UnwindOp = UWOP_SAVE_XMM128; + } + else + { + *codedSize = (USHORT)(offset / 8); + code->UnwindOp = UWOP_SAVE_NONVOL; + } + } + else + { + assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); + ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; + *codedSize = offset; + code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; + code->UnwindOp = (genIsValidFloatReg(reg)) ? UWOP_SAVE_XMM128_FAR : UWOP_SAVE_NONVOL_FAR; + } + code->OpInfo = (BYTE)reg; + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + code->CodeOffset = (BYTE)cbProlog; + } +} + +#ifdef UNIX_AMD64_ABI +void Compiler::unwindSaveRegCFI(regNumber reg, unsigned offset) +{ + assert(compGeneratingProlog); + + if (RBM_CALLEE_SAVED & genRegMask(reg)) + { + FuncInfoDsc* func = funCurrentFunc(); + + unsigned int cbProlog = unwindGetCurrentOffset(func); + noway_assert((BYTE)cbProlog == cbProlog); + createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg), offset); + } +} +#endif // UNIX_AMD64_ABI + +#ifdef DEBUG + +//------------------------------------------------------------------------ +// DumpUnwindInfo: Dump the unwind data. +// +// Arguments: +// isHotCode - true if this unwind data is for the hot section, false otherwise. +// 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. +// +void DumpUnwindInfo(bool isHotCode, + UNATIVE_OFFSET startOffset, + UNATIVE_OFFSET endOffset, + const UNWIND_INFO* const pHeader) +{ + printf("Unwind Info%s:\n", isHotCode ? "" : " COLD"); + printf(" >> Start offset : 0x%06x (not in unwind data)\n", dspOffset(startOffset)); + printf(" >> End offset : 0x%06x (not in unwind data)\n", dspOffset(endOffset)); + + if (pHeader == nullptr) + { + // Cold AMD64 code doesn't have unwind info; the VM creates chained unwind info. + assert(!isHotCode); + return; + } + + printf(" Version : %u\n", pHeader->Version); + printf(" Flags : 0x%02x", pHeader->Flags); + if (pHeader->Flags) + { + const UCHAR flags = pHeader->Flags; + printf(" ("); + if (flags & UNW_FLAG_EHANDLER) + { + printf(" UNW_FLAG_EHANDLER"); + } + if (flags & UNW_FLAG_UHANDLER) + { + printf(" UNW_FLAG_UHANDLER"); + } + if (flags & UNW_FLAG_CHAININFO) + { + printf(" UNW_FLAG_CHAININFO"); + } + printf(")"); + } + printf("\n"); + printf(" SizeOfProlog : 0x%02X\n", pHeader->SizeOfProlog); + printf(" CountOfUnwindCodes: %u\n", pHeader->CountOfUnwindCodes); + printf(" FrameRegister : %s (%u)\n", + (pHeader->FrameRegister == 0) ? "none" : getRegName(pHeader->FrameRegister), + pHeader->FrameRegister); // RAX (0) is not allowed as a frame register + if (pHeader->FrameRegister == 0) + { + printf(" FrameOffset : N/A (no FrameRegister) (Value=%u)\n", pHeader->FrameOffset); + } + else + { + printf(" FrameOffset : %u * 16 = 0x%02X\n", pHeader->FrameOffset, pHeader->FrameOffset * 16); + } + printf(" UnwindCodes :\n"); + + for (unsigned i = 0; i < pHeader->CountOfUnwindCodes; i++) + { + unsigned offset; + const UNWIND_CODE* const pCode = &(pHeader->UnwindCode[i]); + switch (pCode->UnwindOp) + { + case UWOP_PUSH_NONVOL: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_PUSH_NONVOL (%u) OpInfo: %s (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); + break; + + case UWOP_ALLOC_LARGE: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_ALLOC_LARGE (%u) OpInfo: %u - ", pCode->CodeOffset, + pCode->UnwindOp, pCode->OpInfo); + if (pCode->OpInfo == 0) + { + i++; + printf("Scaled small \n Size: %u * 8 = %u = 0x%05X\n", pHeader->UnwindCode[i].FrameOffset, + pHeader->UnwindCode[i].FrameOffset * 8, pHeader->UnwindCode[i].FrameOffset * 8); + } + else if (pCode->OpInfo == 1) + { + i++; + printf("Unscaled large\n Size: %u = 0x%08X\n\n", *(ULONG*)&(pHeader->UnwindCode[i]), + *(ULONG*)&(pHeader->UnwindCode[i])); + i++; + } + else + { + printf("Unknown\n"); + } + break; + + case UWOP_ALLOC_SMALL: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_ALLOC_SMALL (%u) OpInfo: %u * 8 + 8 = %u = 0x%02X\n", + pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo * 8 + 8, pCode->OpInfo * 8 + 8); + break; + + case UWOP_SET_FPREG: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SET_FPREG (%u) OpInfo: Unused (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero + break; + +#ifdef PLATFORM_UNIX + + case UWOP_SET_FPREG_LARGE: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SET_FPREG_LARGE (%u) OpInfo: Unused (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero + i++; + offset = *(ULONG*)&(pHeader->UnwindCode[i]); + i++; + printf(" Scaled Offset: %u * 16 = %u = 0x%08X\n", offset, offset * 16, offset * 16); + if ((offset & 0xF0000000) != 0) + { + printf(" Illegal unscaled offset: too large\n"); + } + break; + +#endif // PLATFORM_UNIX + + case UWOP_SAVE_NONVOL: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_NONVOL (%u) OpInfo: %s (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); + i++; + printf(" Scaled Small Offset: %u * 8 = %u = 0x%05X\n", pHeader->UnwindCode[i].FrameOffset, + pHeader->UnwindCode[i].FrameOffset * 8, pHeader->UnwindCode[i].FrameOffset * 8); + break; + + case UWOP_SAVE_NONVOL_FAR: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_NONVOL_FAR (%u) OpInfo: %s (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); + i++; + printf(" Unscaled Large Offset: 0x%08X\n\n", *(ULONG*)&(pHeader->UnwindCode[i])); + i++; + break; + + case UWOP_SAVE_XMM128: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_XMM128 (%u) OpInfo: XMM%u (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo); + i++; + printf(" Scaled Small Offset: %u * 16 = %u = 0x%05X\n", pHeader->UnwindCode[i].FrameOffset, + pHeader->UnwindCode[i].FrameOffset * 16, pHeader->UnwindCode[i].FrameOffset * 16); + break; + + case UWOP_SAVE_XMM128_FAR: + printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_XMM128_FAR (%u) OpInfo: XMM%u (%u)\n", + pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo); + i++; + printf(" Unscaled Large Offset: 0x%08X\n\n", *(ULONG*)&(pHeader->UnwindCode[i])); + i++; + break; + + case UWOP_EPILOG: + case UWOP_SPARE_CODE: + case UWOP_PUSH_MACHFRAME: + default: + printf(" Unrecognized UNWIND_CODE: 0x%04X\n", *(USHORT*)pCode); + break; + } + } +} + +#ifdef UNIX_AMD64_ABI +//------------------------------------------------------------------------ +// DumpCfiInfo: Dump the Cfi data. +// +// Arguments: +// isHotCode - true if this cfi data is for the hot section, false otherwise. +// startOffset - byte offset of the code start that this cfi data represents. +// endOffset - byte offset of the code end that this cfi data represents. +// pcFiCode - pointer to the cfi data blob. +// +void DumpCfiInfo(bool isHotCode, + UNATIVE_OFFSET startOffset, + UNATIVE_OFFSET endOffset, + DWORD cfiCodeBytes, + const CFI_CODE* const pCfiCode) +{ + printf("Cfi Info%s:\n", isHotCode ? "" : " COLD"); + printf(" >> Start offset : 0x%06x \n", dspOffset(startOffset)); + printf(" >> End offset : 0x%06x \n", dspOffset(endOffset)); + + for (int i = 0; i < cfiCodeBytes / sizeof(CFI_CODE); i++) + { + const CFI_CODE* const pCode = &(pCfiCode[i]); + + UCHAR codeOffset = pCode->CodeOffset; + SHORT dwarfReg = pCode->DwarfReg; + INT offset = pCode->Offset; + + switch (pCode->CfiOpCode) + { + case CFI_REL_OFFSET: + printf(" CodeOffset: 0x%02X Op: RelOffset DwarfReg:0x%x Offset:0x%X\n", codeOffset, dwarfReg, + offset); + break; + case CFI_DEF_CFA_REGISTER: + assert(offset == 0); + printf(" CodeOffset: 0x%02X Op: DefCfaRegister DwarfReg:0x%X\n", codeOffset, dwarfReg); + break; + case CFI_ADJUST_CFA_OFFSET: + assert(dwarfReg == DWARF_REG_ILLEGAL); + printf(" CodeOffset: 0x%02X Op: AdjustCfaOffset Offset:0x%X\n", codeOffset, offset); + break; + default: + printf(" Unrecognized CFI_CODE: 0x%IX\n", *(UINT64*)pCode); + break; + } + } +} +#endif // UNIX_AMD64_ABI +#endif // DEBUG + +//------------------------------------------------------------------------ +// Compiler::unwindReserve: Ask the VM to reserve space for the unwind information +// for the function and all its funclets. Called once, just before asking the VM +// for memory and emitting the generated code. Calls unwindReserveFunc() to handle +// the main function and each of the funclets, in turn. +// +void Compiler::unwindReserve() +{ + assert(!compGeneratingProlog); + assert(!compGeneratingEpilog); + + assert(compFuncInfoCount > 0); + for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++) + { + unwindReserveFunc(funGetFunc(funcIdx)); + } +} + +//------------------------------------------------------------------------ +// Compiler::unwindReserveFunc: Reserve the unwind information from the VM for a +// given main function or funclet. +// +// Arguments: +// func - The main function or funclet to reserve unwind info for. +// +void Compiler::unwindReserveFunc(FuncInfoDsc* func) +{ + unwindReserveFuncHelper(func, true); + + if (fgFirstColdBlock != nullptr) + { + unwindReserveFuncHelper(func, false); + } +} + +//------------------------------------------------------------------------ +// Compiler::unwindReserveFuncHelper: Reserve the unwind information from the VM for a +// given main function or funclet, for either the hot or the cold section. +// +// Arguments: +// func - The main function or funclet to reserve unwind info for. +// isHotCode - 'true' to reserve the hot section, 'false' to reserve the cold section. +// +void Compiler::unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode) +{ + DWORD unwindCodeBytes = 0; + if (isHotCode) + { +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + unwindCodeBytes = func->cfiCodes->size() * sizeof(CFI_CODE); + } + else +#endif // UNIX_AMD64_ABI + { + assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog + assert(func->unwindHeader.CountOfUnwindCodes == 0); // Only call this once per prolog + + // Set the size of the prolog to be the last encoded action + if (func->unwindCodeSlot < sizeof(func->unwindCodes)) + { + UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot]; + func->unwindHeader.SizeOfProlog = code->CodeOffset; + } + else + { + func->unwindHeader.SizeOfProlog = 0; + } + func->unwindHeader.CountOfUnwindCodes = + (BYTE)((sizeof(func->unwindCodes) - func->unwindCodeSlot) / sizeof(UNWIND_CODE)); + + // Prepend the unwindHeader onto the unwind codes + assert(func->unwindCodeSlot >= offsetof(UNWIND_INFO, UnwindCode)); + + func->unwindCodeSlot -= offsetof(UNWIND_INFO, UnwindCode); + UNWIND_INFO* pHeader = (UNWIND_INFO*)&func->unwindCodes[func->unwindCodeSlot]; + memcpy(pHeader, &func->unwindHeader, offsetof(UNWIND_INFO, UnwindCode)); + + unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot; + } + } + + BOOL isFunclet = (func->funKind != FUNC_ROOT); + BOOL isColdCode = isHotCode ? FALSE : TRUE; + + eeReserveUnwindInfo(isFunclet, isColdCode, unwindCodeBytes); +} + +//------------------------------------------------------------------------ +// Compiler::unwindEmit: Report all the unwind information to the VM. +// +// Arguments: +// pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. +// pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. +// +void Compiler::unwindEmit(void* pHotCode, void* pColdCode) +{ + assert(!compGeneratingProlog); + assert(!compGeneratingEpilog); + + assert(compFuncInfoCount > 0); + for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++) + { + unwindEmitFunc(funGetFunc(funcIdx), pHotCode, pColdCode); + } +} + +//------------------------------------------------------------------------ +// Compiler::unwindEmitFuncHelper: Report the unwind information to the VM for a +// given main function or funclet, for either the hot or cold section. +// +// Arguments: +// func - The main function or funclet to reserve unwind info for. +// pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. +// pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. +// Ignored if 'isHotCode' is true. +// isHotCode - 'true' to report the hot section, 'false' to report the cold section. +// +void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode) +{ + UNATIVE_OFFSET startOffset; + UNATIVE_OFFSET endOffset; + DWORD unwindCodeBytes = 0; + BYTE* pUnwindBlock = nullptr; + + if (isHotCode) + { + if (func->startLoc == nullptr) + { + startOffset = 0; + } + else + { + startOffset = func->startLoc->CodeOffset(genEmitter); + } + + if (func->endLoc == nullptr) + { + endOffset = info.compNativeCodeSize; + } + else + { + endOffset = func->endLoc->CodeOffset(genEmitter); + } + +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + int size = func->cfiCodes->size(); + if (size > 0) + { + unwindCodeBytes = size * sizeof(CFI_CODE); + pUnwindBlock = (BYTE*)&(*func->cfiCodes)[0]; + } + } + else +#endif // UNIX_AMD64_ABI + { + unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot; + +#ifdef DEBUG + UNWIND_INFO* pUnwindInfo = (UNWIND_INFO*)(&func->unwindCodes[func->unwindCodeSlot]); + DWORD unwindCodeBytesSpecified = + offsetof(UNWIND_INFO, UnwindCode) + + pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE); // This is what the unwind codes themselves say; + // it better match what we tell the VM. + assert(unwindCodeBytes == unwindCodeBytesSpecified); +#endif // DEBUG + + pUnwindBlock = &func->unwindCodes[func->unwindCodeSlot]; + } + } + else + { + assert(fgFirstColdBlock != nullptr); + assert(func->funKind == FUNC_ROOT); // No splitting of funclets. + + if (func->coldStartLoc == nullptr) + { + startOffset = 0; + } + else + { + startOffset = func->coldStartLoc->CodeOffset(genEmitter); + } + + if (func->coldEndLoc == nullptr) + { + endOffset = info.compNativeCodeSize; + } + else + { + endOffset = func->coldEndLoc->CodeOffset(genEmitter); + } + } + +#ifdef DEBUG + if (opts.dspUnwind) + { +#ifdef UNIX_AMD64_ABI + if (generateCFIUnwindCodes()) + { + DumpCfiInfo(isHotCode, startOffset, endOffset, unwindCodeBytes, (const CFI_CODE* const)pUnwindBlock); + } + else +#endif // UNIX_AMD64_ABI + { + DumpUnwindInfo(isHotCode, startOffset, endOffset, (const UNWIND_INFO* const)pUnwindBlock); + } + } +#endif // DEBUG + + // Adjust for cold or hot code: + // 1. The VM doesn't want the cold code pointer unless this is cold code. + // 2. The startOffset and endOffset need to be from the base of the hot section for hot code + // and from the base of the cold section for cold code + + if (isHotCode) + { + assert(endOffset <= info.compTotalHotCodeSize); + pColdCode = nullptr; + } + else + { + assert(startOffset >= info.compTotalHotCodeSize); + startOffset -= info.compTotalHotCodeSize; + endOffset -= info.compTotalHotCodeSize; + } + + eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindCodeBytes, pUnwindBlock, + (CorJitFuncKind)func->funKind); +} + +//------------------------------------------------------------------------ +// Compiler::unwindEmitFunc: Report the unwind information to the VM for a +// given main function or funclet. Reports the hot section, then the cold +// section if necessary. +// +// Arguments: +// func - The main function or funclet to reserve unwind info for. +// pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. +// pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. +// +void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode) +{ + // Verify that the JIT enum is in sync with the JIT-EE interface enum + static_assert_no_msg(FUNC_ROOT == (FuncKind)CORJIT_FUNC_ROOT); + static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER); + static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER); + + unwindEmitFuncHelper(func, pHotCode, pColdCode, true); + + if (pColdCode != nullptr) + { + unwindEmitFuncHelper(func, pHotCode, pColdCode, false); + } +} + +#endif // _TARGET_AMD64_ |