summaryrefslogtreecommitdiff
path: root/src/jit/unwindarm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/unwindarm.cpp')
-rw-r--r--src/jit/unwindarm.cpp2320
1 files changed, 2320 insertions, 0 deletions
diff --git a/src/jit/unwindarm.cpp b/src/jit/unwindarm.cpp
new file mode 100644
index 0000000000..b537bef4a3
--- /dev/null
+++ b/src/jit/unwindarm.cpp
@@ -0,0 +1,2320 @@
+// 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
+
+#ifdef _TARGET_ARMARCH_
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX Unwind APIs XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+void Compiler::unwindBegProlog()
+{
+ 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.
+
+ emitLocation* startLoc;
+ emitLocation* endLoc;
+ unwindGetFuncLocations(func, true, &startLoc, &endLoc);
+
+ func->uwi.InitUnwindInfo(this, startLoc, endLoc);
+ func->uwi.CaptureLocation();
+
+ func->uwiCold = NULL; // No cold data yet
+}
+
+void Compiler::unwindEndProlog()
+{
+ assert(compGeneratingProlog);
+}
+
+void Compiler::unwindBegEpilog()
+{
+ assert(compGeneratingEpilog);
+ funCurrentFunc()->uwi.AddEpilog();
+}
+
+void Compiler::unwindEndEpilog()
+{
+ assert(compGeneratingEpilog);
+}
+
+#if defined(_TARGET_ARM_)
+
+void Compiler::unwindPushPopMaskInt(regMaskTP maskInt, bool useOpsize16)
+{
+ // floating point registers cannot be specified in 'maskInt'
+ assert((maskInt & RBM_ALLFLOAT) == 0);
+
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+ if (useOpsize16)
+ {
+ // The 16-bit opcode only encode R0-R7 and LR
+ assert((maskInt & ~(RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3 | RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_LR)) == 0);
+
+ bool shortFormat = false;
+ BYTE val = 0;
+
+ if ((maskInt & (RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3)) == 0)
+ {
+ regMaskTP matchMask = maskInt & (RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7);
+ regMaskTP valMask = RBM_R4;
+ while (val < 4)
+ {
+ if (matchMask == valMask)
+ {
+ shortFormat = true;
+ break;
+ }
+
+ valMask <<= 1;
+ valMask |= RBM_R4;
+
+ val++;
+ }
+ }
+
+ if (shortFormat)
+ {
+ // D0-D7 : pop {r4-rX,lr} (X=4-7) (opsize 16)
+ pu->AddCode(0xD0 | ((maskInt >> 12) & 0x4) | val);
+ }
+ else
+ {
+ // EC-ED : pop {r0-r7,lr} (opsize 16)
+ pu->AddCode(0xEC | ((maskInt >> 14) & 0x1), (BYTE)maskInt);
+ }
+ }
+ else
+ {
+ assert((maskInt &
+ ~(RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3 | RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8 | RBM_R9 | RBM_R10 |
+ RBM_R11 | RBM_R12 | RBM_LR)) == 0);
+
+ bool shortFormat = false;
+ BYTE val = 0;
+
+ if (((maskInt & (RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3)) == 0) &&
+ ((maskInt & (RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8)) == (RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8)))
+ {
+ regMaskTP matchMask = maskInt & (RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8 | RBM_R9 | RBM_R10 | RBM_R11);
+ regMaskTP valMask = RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8;
+ while (val < 4)
+ {
+ if (matchMask == valMask)
+ {
+ shortFormat = true;
+ break;
+ }
+
+ valMask <<= 1;
+ valMask |= RBM_R4;
+
+ val++;
+ }
+ }
+
+ if (shortFormat)
+ {
+ // D8-DF : pop {r4-rX,lr} (X=8-11) (opsize 32)
+ pu->AddCode(0xD8 | ((maskInt >> 12) & 0x4) | val);
+ }
+ else
+ {
+ // 80-BF : pop {r0-r12,lr} (opsize 32)
+ pu->AddCode(0x80 | ((maskInt >> 8) & 0x1F) | ((maskInt >> 9) & 0x20), (BYTE)maskInt);
+ }
+ }
+}
+
+void Compiler::unwindPushPopMaskFloat(regMaskTP maskFloat)
+{
+ // Only floating pointer registers can be specified in 'maskFloat'
+ assert((maskFloat & ~RBM_ALLFLOAT) == 0);
+
+ // If the maskFloat is zero there is no unwind code to emit
+ //
+ if (maskFloat == RBM_NONE)
+ {
+ return;
+ }
+
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+ BYTE val = 0;
+ regMaskTP valMask = (RBM_F16 | RBM_F17);
+
+ while (maskFloat != valMask)
+ {
+ valMask <<= 2;
+ valMask |= (RBM_F16 | RBM_F17);
+
+ val++;
+
+ if (val == 8)
+ {
+ noway_assert(!"Illegal maskFloat");
+ }
+ }
+
+ // E0-E7 : vpop {d8-dX} (X=8-15) (opsize 32)
+ assert(0 <= val && val <= 7);
+ pu->AddCode(0xE0 | val);
+}
+
+void Compiler::unwindPushMaskInt(regMaskTP maskInt)
+{
+ // Only r0-r12 and lr are supported
+ assert((maskInt &
+ ~(RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3 | RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8 | RBM_R9 | RBM_R10 |
+ RBM_R11 | RBM_R12 | RBM_LR)) == 0);
+
+ bool useOpsize16 = ((maskInt & (RBM_LOW_REGS | RBM_LR)) == maskInt); // Can PUSH use the 16-bit encoding?
+ unwindPushPopMaskInt(maskInt, useOpsize16);
+}
+
+void Compiler::unwindPushMaskFloat(regMaskTP maskFloat)
+{
+ // Only floating point registers should be in maskFloat
+ assert((maskFloat & RBM_ALLFLOAT) == maskFloat);
+ unwindPushPopMaskFloat(maskFloat);
+}
+
+void Compiler::unwindPopMaskInt(regMaskTP maskInt)
+{
+ // Only r0-r12 and lr and pc are supported (pc is mapped to lr when encoding)
+ assert((maskInt &
+ ~(RBM_R0 | RBM_R1 | RBM_R2 | RBM_R3 | RBM_R4 | RBM_R5 | RBM_R6 | RBM_R7 | RBM_R8 | RBM_R9 | RBM_R10 |
+ RBM_R11 | RBM_R12 | RBM_LR | RBM_PC)) == 0);
+
+ bool useOpsize16 = ((maskInt & (RBM_LOW_REGS | RBM_PC)) == maskInt); // Can POP use the 16-bit encoding?
+
+ // If we are popping PC, then we'll return from the function. In this case, we assume
+ // the first thing the prolog did was push LR, so give the unwind codes in terms of
+ // the LR that was pushed. Note that the epilog unwind codes are meant to reverse
+ // the effect of the prolog. For "pop {pc}", the prolog had "push {lr}", so we need
+ // an epilog code to model the reverse of that.
+ if (maskInt & RBM_PC)
+ {
+ maskInt = (maskInt & ~RBM_PC) | RBM_LR;
+ }
+ unwindPushPopMaskInt(maskInt, useOpsize16);
+}
+
+void Compiler::unwindPopMaskFloat(regMaskTP maskFloat)
+{
+ // Only floating point registers should be in maskFloat
+ assert((maskFloat & RBM_ALLFLOAT) == maskFloat);
+ unwindPushPopMaskFloat(maskFloat);
+}
+
+void Compiler::unwindAllocStack(unsigned size)
+{
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+ assert(size % 4 == 0);
+ size /= 4;
+
+ if (size <= 0x7F)
+ {
+ // 00-7F : add sp, sp, #X*4 (opsize 16)
+ pu->AddCode((BYTE)size);
+ }
+ else if (size <= 0x3FF)
+ {
+ // E8-EB : addw sp, sp, #X*4 (opsize 32)
+ pu->AddCode(0xE8 | (BYTE)(size >> 8), (BYTE)size);
+ }
+ else if (size <= 0xFFFF)
+ {
+ // F7 : add sp, sp, #X*4 (opsize 16)
+ // F9 : add sp, sp, #X*4 (opsize 32)
+ //
+ // For large stack size, the most significant bits
+ // are stored first (and next to the opCode (F9)) per the unwind spec.
+ unsigned instrSizeInBytes = pu->GetInstructionSize();
+ BYTE b1 = (instrSizeInBytes == 2) ? 0xF7 : 0xF9;
+ pu->AddCode(b1,
+ (BYTE)(size >> 8), // msb
+ (BYTE)size); // lsb
+ }
+ else
+ {
+ // F8 : add sp, sp, #X*4 (opsize 16)
+ // FA : add sp, sp, #X*4 (opsize 32)
+ //
+ // For large stack size, the most significant bits
+ // are stored first (and next to the opCode (FA)) per the unwind spec.
+ unsigned instrSizeInBytes = pu->GetInstructionSize();
+ BYTE b1 = (instrSizeInBytes == 2) ? 0xF8 : 0xFA;
+ pu->AddCode(b1, (BYTE)(size >> 16), (BYTE)(size >> 8), (BYTE)size);
+ }
+}
+
+void Compiler::unwindSetFrameReg(regNumber reg, unsigned offset)
+{
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+ // Arm unwind info does not allow offset
+ assert(offset == 0);
+ assert(0 <= reg && reg <= 15);
+
+ // C0-CF : mov sp, rX (opsize 16)
+ pu->AddCode((BYTE)(0xC0 + reg));
+}
+
+void Compiler::unwindSaveReg(regNumber reg, unsigned offset)
+{
+ unreached();
+}
+
+void Compiler::unwindBranch16()
+{
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+ // TODO-CQ: need to handle changing the exit code from 0xFF to 0xFD. Currently, this will waste an extra 0xFF at the
+ // end, automatically added.
+ pu->AddCode(0xFD);
+}
+
+void Compiler::unwindNop(unsigned codeSizeInBytes) // codeSizeInBytes is 2 or 4 bytes for Thumb2 instruction
+{
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("unwindNop: adding NOP for %d byte instruction\n", codeSizeInBytes);
+ }
+#endif
+
+ INDEBUG(pu->uwiAddingNOP = true);
+
+ if (codeSizeInBytes == 2)
+ {
+ // FB : nop (opsize 16)
+ pu->AddCode(0xFB);
+ }
+ else
+ {
+ noway_assert(codeSizeInBytes == 4);
+
+ // FC : nop (opsize 32)
+ pu->AddCode(0xFC);
+ }
+
+ INDEBUG(pu->uwiAddingNOP = false);
+}
+
+#endif // defined(_TARGET_ARM_)
+
+// The instructions between the last captured "current state" and the current instruction
+// are in the prolog but have no effect for unwinding. Emit the appropriate NOP unwind codes
+// for them.
+void Compiler::unwindPadding()
+{
+ UnwindInfo* pu = &funCurrentFunc()->uwi;
+ genEmitter->emitUnwindNopPadding(pu->GetCurrentEmitterLocation(), this);
+}
+
+// Ask the VM to reserve space for the unwind information for the function and
+// all its funclets.
+void Compiler::unwindReserve()
+{
+ assert(compFuncInfoCount > 0);
+ for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++)
+ {
+ unwindReserveFunc(funGetFunc(funcIdx));
+ }
+}
+
+void Compiler::unwindReserveFunc(FuncInfoDsc* func)
+{
+ BOOL isFunclet = (func->funKind == FUNC_ROOT) ? FALSE : TRUE;
+ bool funcHasColdSection = false;
+
+ // If there is cold code, split the unwind data between the hot section and the
+ // cold section. This needs to be done before we split into fragments, as each
+ // of the hot and cold sections can have multiple fragments.
+
+ if (fgFirstColdBlock != NULL)
+ {
+ assert(!isFunclet); // TODO-CQ: support hot/cold splitting with EH
+
+ emitLocation* startLoc;
+ emitLocation* endLoc;
+ unwindGetFuncLocations(func, false, &startLoc, &endLoc);
+
+ func->uwiCold = new (this, CMK_UnwindInfo) UnwindInfo();
+ func->uwiCold->InitUnwindInfo(this, startLoc, endLoc);
+ func->uwiCold->HotColdSplitCodes(&func->uwi);
+
+ funcHasColdSection = true;
+ }
+
+ // First we need to split the function or funclet into fragments that are no larger
+ // than 512K, so the fragment size will fit in the unwind data "Function Length" field.
+ // The ARM Exception Data specification "Function Fragments" section describes this.
+ func->uwi.Split();
+
+ func->uwi.Reserve(isFunclet, true);
+
+ // After the hot section, split and reserve the cold section
+
+ if (funcHasColdSection)
+ {
+ assert(func->uwiCold != NULL);
+
+ func->uwiCold->Split();
+ func->uwiCold->Reserve(isFunclet, false);
+ }
+}
+
+// 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(compFuncInfoCount > 0);
+ for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++)
+ {
+ unwindEmitFunc(funGetFunc(funcIdx), pHotCode, pColdCode);
+ }
+}
+
+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);
+
+ func->uwi.Allocate((CorJitFuncKind)func->funKind, pHotCode, pColdCode, true);
+
+ if (func->uwiCold != NULL)
+ {
+ func->uwiCold->Allocate((CorJitFuncKind)func->funKind, pHotCode, pColdCode, false);
+ }
+}
+
+#if defined(_TARGET_ARM_)
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX Unwind Info Debug helpers XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+#ifdef DEBUG
+
+// Return the opcode size of an instruction, in bytes, given the first byte of
+// its corresponding unwind code.
+
+unsigned GetOpcodeSizeFromUnwindHeader(BYTE b1)
+{
+ static BYTE s_UnwindOpsize[256] = {
+ // array of opsizes, in bytes (as specified in the ARM unwind specification)
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 00-0F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10-1F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 20-2F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 30-3F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 40-4F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 50-5F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 60-6F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 70-7F
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 80-8F
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 90-9F
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // A0-AF
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 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, 4, 4, 4, 4, 4, 4, 4, 4, // D0-DF
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 4, // E0-EF
+ 0, 0, 0, 0, 0, 4, 4, 2, 2, 4, 4, 2, 4, 2, 4, 0 // F0-FF
+ };
+
+ BYTE opsize = s_UnwindOpsize[b1];
+ assert(opsize == 2 ||
+ opsize == 4); // We shouldn't get a code with no opsize (the 0xFF end code is handled specially)
+ return opsize;
+}
+
+// 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
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 80-8F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 90-9F
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // A0-AF
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // B0-BF
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C0-CF
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D0-DF
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, // E0-EF
+ 1, 1, 1, 1, 1, 2, 2, 3, 4, 3, 4, 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.
+// The 0xFD and 0xFE "end + NOP" codes need to be handled differently between
+// the prolog and epilog. They count as pure "end" codes in a prolog, but they
+// count as 16 and 32 bit NOPs (respectively), as well as an "end", in an epilog.
+unsigned UnwindCodesBase::GetCodeSizeFromUnwindCodes(bool isProlog)
+{
+ BYTE* pCodesStart = GetCodes();
+ BYTE* pCodes = pCodesStart;
+ unsigned size = 0;
+ for (;;)
+ {
+ BYTE b1 = *pCodes;
+ if (b1 >= 0xFD)
+ {
+ // 0xFD, 0xFE, 0xFF are "end" codes
+
+ if (!isProlog && (b1 == 0xFD || b1 == 0xFE))
+ {
+ // Count the special "end + NOP" code size in the epilog
+ size += GetOpcodeSizeFromUnwindHeader(b1);
+ }
+
+ break; // We hit an "end" code; we're done
+ }
+ size += GetOpcodeSizeFromUnwindHeader(b1);
+ pCodes += GetUnwindSizeFromUnwindHeader(b1);
+ assert(pCodes - pCodesStart < 256); // 255 is the absolute maximum number of code bytes allowed
+ }
+ return size;
+}
+
+#endif // DEBUG
+
+#endif // defined(_TARGET_ARM_)
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// UnwindPrologCodes
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// We're going to use the prolog codes memory to store the final unwind data.
+// Ensure we have enough memory to store everything. If 'epilogBytes' > 0, then
+// move the prolog codes so there are 'epilogBytes' bytes after the prolog codes.
+// Set the header pointer for future use, adding the header bytes (this pointer
+// is updated when a header byte is added), and remember the index that points
+// to the beginning of the header.
+
+void UnwindPrologCodes::SetFinalSize(int headerBytes, int epilogBytes)
+{
+#ifdef DEBUG
+ // We're done adding codes. Check that we didn't accidentally create a bigger prolog.
+ unsigned codeSize = GetCodeSizeFromUnwindCodes(true);
+ assert(codeSize <= MAX_PROLOG_SIZE_BYTES);
+#endif // DEBUG
+
+ int prologBytes = Size();
+
+ EnsureSize(headerBytes + prologBytes + epilogBytes + 3); // 3 = padding bytes for alignment
+
+ upcUnwindBlockSlot = upcCodeSlot - headerBytes - epilogBytes; // Index of the first byte of the unwind header
+
+ assert(upcMemSize == upcUnwindBlockSlot + headerBytes + prologBytes + epilogBytes + 3);
+
+ upcHeaderSlot = upcUnwindBlockSlot - 1; // upcHeaderSlot is always incremented before storing
+ assert(upcHeaderSlot >= -1);
+
+ if (epilogBytes > 0)
+ {
+ // The prolog codes that are already at the end of the array need to get moved to the middle,
+ // with space for the non-matching epilog codes to follow.
+
+ memmove_s(&upcMem[upcUnwindBlockSlot + headerBytes], upcMemSize - (upcUnwindBlockSlot + headerBytes),
+ &upcMem[upcCodeSlot], prologBytes);
+
+ // Note that the three UWC_END padding bytes still exist at the end of the array.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef DEBUG
+ // Zero out the epilog codes memory, to ensure we've copied the right bytes. Don't zero the padding bytes.
+ memset(&upcMem[upcUnwindBlockSlot + headerBytes + prologBytes], 0, epilogBytes);
+#endif // DEBUG
+
+ upcEpilogSlot =
+ upcUnwindBlockSlot + headerBytes + prologBytes; // upcEpilogSlot points to the next epilog location to fill
+
+ // Update upcCodeSlot to point at the new beginning of the prolog codes
+ upcCodeSlot = upcUnwindBlockSlot + headerBytes;
+ }
+}
+
+// Add a header word. Header words are added starting at the beginning, in order: first to last.
+// This is in contrast to the prolog unwind codes, which are added in reverse order.
+void UnwindPrologCodes::AddHeaderWord(DWORD d)
+{
+ assert(-1 <= upcHeaderSlot);
+ assert(upcHeaderSlot + 4 < upcCodeSlot); // Don't collide with the unwind codes that are already there!
+
+ // Store it byte-by-byte in little-endian format. We've already ensured there is enough space
+ // in SetFinalSize().
+ upcMem[++upcHeaderSlot] = (BYTE)d;
+ upcMem[++upcHeaderSlot] = (BYTE)(d >> 8);
+ upcMem[++upcHeaderSlot] = (BYTE)(d >> 16);
+ upcMem[++upcHeaderSlot] = (BYTE)(d >> 24);
+}
+
+// AppendEpilog: copy the epilog bytes to the next epilog bytes slot
+void UnwindPrologCodes::AppendEpilog(UnwindEpilogInfo* pEpi)
+{
+ assert(upcEpilogSlot != -1);
+
+ int epiSize = pEpi->Size();
+ memcpy_s(&upcMem[upcEpilogSlot], upcMemSize - upcEpilogSlot - 3, pEpi->GetCodes(),
+ epiSize); // -3 to avoid writing to the alignment padding
+ assert(pEpi->GetStartIndex() ==
+ upcEpilogSlot - upcCodeSlot); // Make sure we copied it where we expected to copy it.
+
+ upcEpilogSlot += epiSize;
+ assert(upcEpilogSlot <= upcMemSize - 3);
+}
+
+// GetFinalInfo: return a pointer to the final unwind info to hand to the VM, and the size of this info in bytes
+void UnwindPrologCodes::GetFinalInfo(/* OUT */ BYTE** ppUnwindBlock, /* OUT */ ULONG* pUnwindBlockSize)
+{
+ assert(upcHeaderSlot + 1 == upcCodeSlot); // We better have filled in the header before asking for the final data!
+
+ *ppUnwindBlock = &upcMem[upcUnwindBlockSlot];
+
+ // We put 4 'end' codes at the end for padding, so we can ensure we have an
+ // unwind block that is a multiple of 4 bytes in size. Subtract off three 'end'
+ // codes (leave one), and then align the size up to a multiple of 4.
+ *pUnwindBlockSize = AlignUp((UINT)(upcMemSize - upcUnwindBlockSlot - 3), sizeof(DWORD));
+}
+
+// Do the argument unwind codes match our unwind codes?
+// If they don't match, return -1. If they do, return the offset into
+// our codes at which they match. Note that this means that the
+// argument codes can match a subset of our codes. The subset needs to be at
+// the end, for the "end" code to match.
+//
+// This is similar to UnwindEpilogInfo::Match().
+//
+#if defined(_TARGET_ARM_)
+// Note that if we wanted to handle 0xFD and 0xFE codes, by converting
+// an existing 0xFF code to one of those, we might do that here.
+#endif // defined(_TARGET_ARM_)
+
+int UnwindPrologCodes::Match(UnwindEpilogInfo* pEpi)
+{
+ if (Size() < pEpi->Size())
+ {
+ return -1;
+ }
+
+ int matchIndex = Size() - pEpi->Size();
+
+ if (0 == memcmp(GetCodes() + matchIndex, pEpi->GetCodes(), pEpi->Size()))
+ {
+ return matchIndex;
+ }
+
+ return -1;
+}
+
+// Copy the prolog codes from another prolog. The only time this is legal is
+// if we are at the initial state and no prolog codes have been added.
+// This is used to create the 'phantom' prolog for non-first fragments.
+
+void UnwindPrologCodes::CopyFrom(UnwindPrologCodes* pCopyFrom)
+{
+ assert(uwiComp == pCopyFrom->uwiComp);
+ assert(upcMem == upcMemLocal);
+ assert(upcMemSize == UPC_LOCAL_COUNT);
+ assert(upcHeaderSlot == -1);
+ assert(upcEpilogSlot == -1);
+
+ // Copy the codes
+ EnsureSize(pCopyFrom->upcMemSize);
+ assert(upcMemSize == pCopyFrom->upcMemSize);
+ memcpy_s(upcMem, upcMemSize, pCopyFrom->upcMem, pCopyFrom->upcMemSize);
+
+ // Copy the other data
+ upcCodeSlot = pCopyFrom->upcCodeSlot;
+ upcHeaderSlot = pCopyFrom->upcHeaderSlot;
+ upcEpilogSlot = pCopyFrom->upcEpilogSlot;
+ upcUnwindBlockSlot = pCopyFrom->upcUnwindBlockSlot;
+}
+
+void UnwindPrologCodes::EnsureSize(int requiredSize)
+{
+ if (requiredSize > upcMemSize)
+ {
+ // Reallocate, and copy everything to a new array.
+
+ // Choose the next power of two size. This may or may not be the best choice.
+ noway_assert((requiredSize & 0xC0000000) == 0); // too big!
+ int newSize;
+ for (newSize = upcMemSize << 1; newSize < requiredSize; newSize <<= 1)
+ {
+ // do nothing
+ }
+
+ BYTE* newUnwindCodes = new (uwiComp, CMK_UnwindInfo) BYTE[newSize];
+ memcpy_s(newUnwindCodes + newSize - upcMemSize, upcMemSize, upcMem,
+ upcMemSize); // copy the existing data to the end
+#ifdef DEBUG
+ // Clear the old unwind codes; nobody should be looking at them
+ memset(upcMem, 0xFF, upcMemSize);
+#endif // DEBUG
+ upcMem = newUnwindCodes; // we don't free anything that used to be there since we have a no-release allocator
+ upcCodeSlot += newSize - upcMemSize;
+ upcMemSize = newSize;
+ }
+}
+
+#ifdef DEBUG
+void UnwindPrologCodes::Dump(int indent)
+{
+ printf("%*sUnwindPrologCodes @0x%08p, size:%d:\n", indent, "", dspPtr(this), sizeof(*this));
+ printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
+ printf("%*s &upcMemLocal[0]: 0x%08p\n", indent, "", dspPtr(&upcMemLocal[0]));
+ printf("%*s upcMem: 0x%08p\n", indent, "", dspPtr(upcMem));
+ printf("%*s upcMemSize: %d\n", indent, "", upcMemSize);
+ printf("%*s upcCodeSlot: %d\n", indent, "", upcCodeSlot);
+ printf("%*s upcHeaderSlot: %d\n", indent, "", upcHeaderSlot);
+ printf("%*s upcEpilogSlot: %d\n", indent, "", upcEpilogSlot);
+ printf("%*s upcUnwindBlockSlot: %d\n", indent, "", upcUnwindBlockSlot);
+
+ if (upcMemSize > 0)
+ {
+ printf("%*s codes:", indent, "");
+ for (int i = 0; i < upcMemSize; i++)
+ {
+ printf(" %02x", upcMem[i]);
+ if (i == upcCodeSlot)
+ printf(" <-C");
+ else if (i == upcHeaderSlot)
+ printf(" <-H");
+ else if (i == upcEpilogSlot)
+ printf(" <-E");
+ else if (i == upcUnwindBlockSlot)
+ printf(" <-U");
+ }
+ printf("\n");
+ }
+}
+#endif // DEBUG
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// UnwindEpilogCodes
+//
+///////////////////////////////////////////////////////////////////////////////
+
+void UnwindEpilogCodes::EnsureSize(int requiredSize)
+{
+ if (requiredSize > uecMemSize)
+ {
+ // Reallocate, and copy everything to a new array.
+
+ // Choose the next power of two size. This may or may not be the best choice.
+ noway_assert((requiredSize & 0xC0000000) == 0); // too big!
+ int newSize;
+ for (newSize = uecMemSize << 1; newSize < requiredSize; newSize <<= 1)
+ {
+ // do nothing
+ }
+
+ BYTE* newUnwindCodes = new (uwiComp, CMK_UnwindInfo) BYTE[newSize];
+ memcpy_s(newUnwindCodes, newSize, uecMem, uecMemSize);
+#ifdef DEBUG
+ // Clear the old unwind codes; nobody should be looking at them
+ memset(uecMem, 0xFF, uecMemSize);
+#endif // DEBUG
+ uecMem = newUnwindCodes; // we don't free anything that used to be there since we have a no-release allocator
+ // uecCodeSlot stays the same
+ uecMemSize = newSize;
+ }
+}
+
+#ifdef DEBUG
+void UnwindEpilogCodes::Dump(int indent)
+{
+ printf("%*sUnwindEpilogCodes @0x%08p, size:%d:\n", indent, "", dspPtr(this), sizeof(*this));
+ printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
+ printf("%*s &uecMemLocal[0]: 0x%08p\n", indent, "", dspPtr(&uecMemLocal[0]));
+ printf("%*s uecMem: 0x%08p\n", indent, "", dspPtr(uecMem));
+ printf("%*s uecMemSize: %d\n", indent, "", uecMemSize);
+ printf("%*s uecCodeSlot: %d\n", indent, "", uecCodeSlot);
+ printf("%*s uecFinalized: %s\n", indent, "", dspBool(uecFinalized));
+
+ if (uecMemSize > 0)
+ {
+ printf("%*s codes:", indent, "");
+ for (int i = 0; i < uecMemSize; i++)
+ {
+ printf(" %02x", uecMem[i]);
+ if (i == uecCodeSlot)
+ printf(" <-C"); // Indicate the current pointer
+ }
+ printf("\n");
+ }
+}
+#endif // DEBUG
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// UnwindEpilogInfo
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Do the current unwind codes match those of the argument epilog?
+// If they don't match, return -1. If they do, return the offset into
+// our codes at which the argument codes match. Note that this means that
+// the argument codes can match a subset of our codes. The subset needs to be at
+// the end, for the "end" code to match.
+//
+// Note that if we wanted to handle 0xFD and 0xFE codes, by converting
+// an existing 0xFF code to one of those, we might do that here.
+
+int UnwindEpilogInfo::Match(UnwindEpilogInfo* pEpi)
+{
+ if (Matches())
+ {
+ // We are already matched to someone else, and won't provide codes to the final layout
+ return -1;
+ }
+
+ if (Size() < pEpi->Size())
+ {
+ return -1;
+ }
+
+ int matchIndex = Size() - pEpi->Size();
+
+ if (0 == memcmp(GetCodes() + matchIndex, pEpi->GetCodes(), pEpi->Size()))
+ {
+ return matchIndex;
+ }
+
+ return -1;
+}
+
+void UnwindEpilogInfo::CaptureEmitLocation()
+{
+ noway_assert(epiEmitLocation == NULL); // This function is only called once per epilog
+ epiEmitLocation = new (uwiComp, CMK_UnwindInfo) emitLocation();
+ epiEmitLocation->CaptureLocation(uwiComp->genEmitter);
+}
+
+void UnwindEpilogInfo::FinalizeOffset()
+{
+ epiStartOffset = epiEmitLocation->CodeOffset(uwiComp->genEmitter);
+}
+
+#ifdef DEBUG
+void UnwindEpilogInfo::Dump(int indent)
+{
+ printf("%*sUnwindEpilogInfo @0x%08p, size:%d:\n", indent, "", dspPtr(this), sizeof(*this));
+ printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
+ printf("%*s epiNext: 0x%08p\n", indent, "", dspPtr(epiNext));
+ printf("%*s epiEmitLocation: 0x%08p\n", indent, "", dspPtr(epiEmitLocation));
+ printf("%*s epiStartOffset: 0x%x\n", indent, "", epiStartOffset);
+ printf("%*s epiMatches: %s\n", indent, "", dspBool(epiMatches));
+ printf("%*s epiStartIndex: %d\n", indent, "", epiStartIndex);
+
+ epiCodes.Dump(indent + 2);
+}
+#endif // DEBUG
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// UnwindFragmentInfo
+//
+///////////////////////////////////////////////////////////////////////////////
+
+UnwindFragmentInfo::UnwindFragmentInfo(Compiler* comp, emitLocation* emitLoc, bool hasPhantomProlog)
+ : UnwindBase(comp)
+ , ufiNext(NULL)
+ , ufiEmitLoc(emitLoc)
+ , ufiHasPhantomProlog(hasPhantomProlog)
+ , ufiPrologCodes(comp)
+ , ufiEpilogFirst(comp)
+ , ufiEpilogList(NULL)
+ , ufiEpilogLast(NULL)
+ , ufiCurCodes(&ufiPrologCodes)
+ , ufiSize(0)
+ , ufiStartOffset(UFI_ILLEGAL_OFFSET)
+{
+#ifdef DEBUG
+ ufiNum = 1;
+ ufiInProlog = true;
+ ufiInitialized = UFI_INITIALIZED_PATTERN;
+#endif // DEBUG
+}
+
+void UnwindFragmentInfo::FinalizeOffset()
+{
+ if (ufiEmitLoc == NULL)
+ {
+ // NULL emit location means the beginning of the code. This is to handle the first fragment prolog.
+ ufiStartOffset = 0;
+ }
+ else
+ {
+ ufiStartOffset = ufiEmitLoc->CodeOffset(uwiComp->genEmitter);
+ }
+
+ for (UnwindEpilogInfo* pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+ pEpi->FinalizeOffset();
+ }
+}
+
+void UnwindFragmentInfo::AddEpilog()
+{
+ assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
+
+#ifdef DEBUG
+ if (ufiInProlog)
+ {
+ assert(ufiEpilogList == NULL);
+ ufiInProlog = false;
+ }
+ else
+ {
+ assert(ufiEpilogList != NULL);
+ }
+#endif // DEBUG
+
+ // Either allocate a new epilog object, or, for the first one, use the
+ // preallocated one that is a member of the UnwindFragmentInfo class.
+
+ UnwindEpilogInfo* newepi;
+
+ if (ufiEpilogList == NULL)
+ {
+ // Use the epilog that's in the class already. Be sure to initialize it!
+ newepi = ufiEpilogList = &ufiEpilogFirst;
+ }
+ else
+ {
+ newepi = new (uwiComp, CMK_UnwindInfo) UnwindEpilogInfo(uwiComp);
+ }
+
+ // Put the new epilog at the end of the epilog list
+
+ if (ufiEpilogLast != NULL)
+ {
+ ufiEpilogLast->epiNext = newepi;
+ }
+
+ ufiEpilogLast = newepi;
+
+ // What is the starting code offset of the epilog? Store an emitter location
+ // so we can ask the emitter later, after codegen.
+
+ newepi->CaptureEmitLocation();
+
+ // Put subsequent unwind codes in this new epilog
+
+ ufiCurCodes = &newepi->epiCodes;
+}
+
+// Copy the prolog codes from the 'pCopyFrom' fragment. These prolog codes will
+// become 'phantom' prolog codes in this fragment. Note that this fragment should
+// not have any prolog codes currently; it is at the initial state.
+
+void UnwindFragmentInfo::CopyPrologCodes(UnwindFragmentInfo* pCopyFrom)
+{
+ ufiPrologCodes.CopyFrom(&pCopyFrom->ufiPrologCodes);
+#ifdef _TARGET_ARM64_
+ ufiPrologCodes.AddCode(UWC_END_C);
+#endif
+}
+
+// Split the epilog codes that currently exist in 'pSplitFrom'. The ones that represent
+// epilogs that start at or after the location represented by 'emitLoc' are removed
+// from 'pSplitFrom' and moved to this fragment. Note that this fragment should not have
+// any epilog codes currently; it is at the initial state.
+
+void UnwindFragmentInfo::SplitEpilogCodes(emitLocation* emitLoc, UnwindFragmentInfo* pSplitFrom)
+{
+ UnwindEpilogInfo* pEpiPrev;
+ UnwindEpilogInfo* pEpi;
+
+ UNATIVE_OFFSET splitOffset = emitLoc->CodeOffset(uwiComp->genEmitter);
+
+ for (pEpiPrev = NULL, pEpi = pSplitFrom->ufiEpilogList; pEpi != NULL; pEpiPrev = pEpi, pEpi = pEpi->epiNext)
+ {
+ pEpi->FinalizeOffset(); // Get the offset of the epilog from the emitter so we can compare it
+ if (pEpi->GetStartOffset() >= splitOffset)
+ {
+ // This epilog and all following epilogs, which must be in order of increasing offsets,
+ // get moved to this fragment.
+
+ // Splice in the epilogs to this fragment. Set the head of the epilog
+ // list to this epilog.
+ ufiEpilogList = pEpi; // In this case, don't use 'ufiEpilogFirst'
+ ufiEpilogLast = pSplitFrom->ufiEpilogLast;
+
+ // Splice out the tail of the list from the 'pSplitFrom' epilog list
+ pSplitFrom->ufiEpilogLast = pEpiPrev;
+ if (pSplitFrom->ufiEpilogLast == NULL)
+ {
+ pSplitFrom->ufiEpilogList = NULL;
+ }
+ else
+ {
+ pSplitFrom->ufiEpilogLast->epiNext = NULL;
+ }
+
+ // No more codes should be added once we start splitting
+ pSplitFrom->ufiCurCodes = NULL;
+ ufiCurCodes = NULL;
+
+ break;
+ }
+ }
+}
+
+// Is this epilog at the end of an unwind fragment? Ask the emitter.
+// Note that we need to know this before all code offsets are finalized,
+// so we can determine whether we can omit an epilog scope word for a
+// single matching epilog.
+
+bool UnwindFragmentInfo::IsAtFragmentEnd(UnwindEpilogInfo* pEpi)
+{
+ return uwiComp->genEmitter->emitIsFuncEnd(pEpi->epiEmitLocation, (ufiNext == NULL) ? NULL : ufiNext->ufiEmitLoc);
+}
+
+// Merge the unwind codes as much as possible.
+// This function is called before all offsets are final.
+// Also, compute the size of the final unwind block. Store this
+// and some other data for later, when we actually emit the
+// unwind block.
+
+void UnwindFragmentInfo::MergeCodes()
+{
+ assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
+
+ unsigned epilogCount = 0;
+ unsigned epilogCodeBytes = 0; // The total number of unwind code bytes used by epilogs that don't match the
+ // prolog codes
+ unsigned epilogIndex = ufiPrologCodes.Size(); // The "Epilog Start Index" for the next non-matching epilog codes
+ UnwindEpilogInfo* pEpi;
+
+ for (pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+ ++epilogCount;
+
+ pEpi->FinalizeCodes();
+
+ // Does this epilog match the prolog?
+ // NOTE: for the purpose of matching, we don't handle the 0xFD and 0xFE end codes that allow slightly unequal
+ // prolog and epilog codes.
+
+ int matchIndex;
+
+ matchIndex = ufiPrologCodes.Match(pEpi);
+ if (matchIndex != -1)
+ {
+ pEpi->SetMatches();
+ pEpi->SetStartIndex(matchIndex); // Prolog codes start at zero, so matchIndex is exactly the start index
+ }
+ else
+ {
+ // The epilog codes don't match the prolog codes. Do they match any of the epilogs
+ // we've seen so far?
+
+ bool matched = false;
+ for (UnwindEpilogInfo* pEpi2 = ufiEpilogList; pEpi2 != pEpi; pEpi2 = pEpi2->epiNext)
+ {
+ matchIndex = pEpi2->Match(pEpi);
+ if (matchIndex != -1)
+ {
+ // Use the same epilog index as the one we matched, as it has already been set.
+ pEpi->SetMatches();
+ pEpi->SetStartIndex(pEpi2->GetStartIndex() + matchIndex); // We might match somewhere inside pEpi2's
+ // codes, in which case matchIndex > 0
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched)
+ {
+ pEpi->SetStartIndex(epilogIndex); // We'll copy these codes to the next available location
+ epilogCodeBytes += pEpi->Size();
+ epilogIndex += pEpi->Size();
+ }
+ }
+ }
+
+ DWORD codeBytes = ufiPrologCodes.Size() + epilogCodeBytes;
+ codeBytes = AlignUp(codeBytes, sizeof(DWORD));
+
+ DWORD codeWords =
+ codeBytes / sizeof(DWORD); // This is how many words we need to store all the unwind codes in the unwind block
+
+ // Do we need the 2nd header word for "Extended Code Words" or "Extended Epilog Count"?
+
+ bool needExtendedCodeWordsEpilogCount =
+ (codeWords > UW_MAX_CODE_WORDS_COUNT) || (epilogCount > UW_MAX_EPILOG_COUNT);
+
+ // How many epilog scope words do we need?
+
+ bool setEBit = false; // do we need to set the E bit?
+ unsigned epilogScopes = epilogCount; // Note that this could be zero if we have no epilogs!
+
+ if (epilogCount == 1)
+ {
+ assert(ufiEpilogList != NULL);
+ assert(ufiEpilogList->epiNext == NULL);
+
+ if (ufiEpilogList->Matches() && (ufiEpilogList->GetStartIndex() == 0) && // The match is with the prolog
+ !needExtendedCodeWordsEpilogCount && IsAtFragmentEnd(ufiEpilogList))
+ {
+ epilogScopes = 0; // Don't need any epilog scope words
+ setEBit = true;
+ }
+ }
+
+ DWORD headerBytes = (1 // Always need first header DWORD
+ + (needExtendedCodeWordsEpilogCount ? 1 : 0) // Do we need the 2nd DWORD for Extended Code
+ // Words or Extended Epilog Count?
+ + epilogScopes // One DWORD per epilog scope, for EBit = 0
+ ) *
+ sizeof(DWORD); // convert it to bytes
+
+ DWORD finalSize = headerBytes + codeBytes; // Size of actual unwind codes, aligned up to 4-byte words,
+ // including end padding if necessary
+
+ // Construct the final unwind information.
+
+ // We re-use the memory for the prolog unwind codes to construct the full unwind data. If all the epilogs
+ // match the prolog, this is easy: we just prepend the header. If there are epilog codes that don't match
+ // the prolog, we still use the prolog codes memory, but it's a little more complicated, since the
+ // unwind info is ordered as: (a) header, (b) prolog codes, (c) non-matching epilog codes. And, the prolog
+ // codes array is filled in from end-to-beginning. So, we compute the size of memory we need, ensure we
+ // have that much memory, and then copy the prolog codes to the right place, appending the non-matching
+ // epilog codes and prepending the header.
+
+ ufiPrologCodes.SetFinalSize(headerBytes, epilogCodeBytes);
+
+ if (epilogCodeBytes != 0)
+ {
+ // We need to copy the epilog code bytes to their final memory location
+
+ for (pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+ if (!pEpi->Matches())
+ {
+ ufiPrologCodes.AppendEpilog(pEpi);
+ }
+ }
+ }
+
+ // Save some data for later
+
+ ufiSize = finalSize;
+ ufiSetEBit = setEBit;
+ ufiNeedExtendedCodeWordsEpilogCount = needExtendedCodeWordsEpilogCount;
+ ufiCodeWords = codeWords;
+ ufiEpilogScopes = epilogScopes;
+}
+
+// Finalize: Prepare the unwind information for the VM. Compute and prepend the unwind header.
+
+void UnwindFragmentInfo::Finalize(UNATIVE_OFFSET functionLength)
+{
+ assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
+
+#ifdef DEBUG
+ if (0 && uwiComp->verbose)
+ {
+ printf("*************** Before fragment #%d finalize\n", ufiNum);
+ Dump();
+ }
+#endif
+
+// Compute the header
+
+#if defined(_TARGET_ARM_)
+ noway_assert((functionLength & 1) == 0);
+ DWORD headerFunctionLength = functionLength / 2;
+#elif defined(_TARGET_ARM64_)
+ noway_assert((functionLength & 3) == 0);
+ DWORD headerFunctionLength = functionLength / 4;
+#endif // _TARGET_ARM64_
+
+ DWORD headerVers = 0; // Version of the unwind info is zero. No other version number is currently defined.
+ DWORD headerXBit = 0; // We never generate "exception data", but the VM might add some.
+ DWORD headerEBit;
+#if defined(_TARGET_ARM_)
+ DWORD headerFBit = ufiHasPhantomProlog ? 1 : 0; // Is this data a fragment in the sense of the unwind data
+ // specification? That is, do the prolog codes represent a real
+ // prolog or not?
+#endif // defined(_TARGET_ARM_)
+ DWORD headerEpilogCount; // This depends on how we set headerEBit.
+ DWORD headerCodeWords;
+ DWORD headerExtendedEpilogCount = 0; // This depends on how we set headerEBit.
+ DWORD headerExtendedCodeWords = 0;
+
+ if (ufiSetEBit)
+ {
+ headerEBit = 1;
+ headerEpilogCount = ufiEpilogList->GetStartIndex(); // probably zero -- the start of the prolog codes!
+ headerCodeWords = ufiCodeWords;
+ }
+ else
+ {
+ headerEBit = 0;
+
+ if (ufiNeedExtendedCodeWordsEpilogCount)
+ {
+ headerEpilogCount = 0;
+ headerCodeWords = 0;
+ headerExtendedEpilogCount = ufiEpilogScopes;
+ headerExtendedCodeWords = ufiCodeWords;
+ }
+ else
+ {
+ headerEpilogCount = ufiEpilogScopes;
+ headerCodeWords = ufiCodeWords;
+ }
+ }
+
+ // Start writing the header
+
+ noway_assert(headerFunctionLength <=
+ 0x3FFFFU); // We create fragments to prevent this from firing, so if it hits, we have an internal error
+
+ if ((headerEpilogCount > UW_MAX_EPILOG_COUNT) || (headerCodeWords > UW_MAX_CODE_WORDS_COUNT))
+ {
+ IMPL_LIMITATION("unwind data too large");
+ }
+
+#if defined(_TARGET_ARM_)
+ DWORD header = headerFunctionLength | (headerVers << 18) | (headerXBit << 20) | (headerEBit << 21) |
+ (headerFBit << 22) | (headerEpilogCount << 23) | (headerCodeWords << 28);
+#elif defined(_TARGET_ARM64_)
+ DWORD header = headerFunctionLength | (headerVers << 18) | (headerXBit << 20) | (headerEBit << 21) |
+ (headerEpilogCount << 22) | (headerCodeWords << 27);
+#endif // defined(_TARGET_ARM64_)
+
+ ufiPrologCodes.AddHeaderWord(header);
+
+ // Construct the second header word, if needed
+
+ if (ufiNeedExtendedCodeWordsEpilogCount)
+ {
+ noway_assert(headerEBit == 0);
+ noway_assert(headerEpilogCount == 0);
+ noway_assert(headerCodeWords == 0);
+ noway_assert((headerExtendedEpilogCount > UW_MAX_EPILOG_COUNT) ||
+ (headerExtendedCodeWords > UW_MAX_CODE_WORDS_COUNT));
+
+ if ((headerExtendedEpilogCount > UW_MAX_EXTENDED_EPILOG_COUNT) ||
+ (headerExtendedCodeWords > UW_MAX_EXTENDED_CODE_WORDS_COUNT))
+ {
+ IMPL_LIMITATION("unwind data too large");
+ }
+
+ DWORD header2 = headerExtendedEpilogCount | (headerExtendedCodeWords << 16);
+
+ ufiPrologCodes.AddHeaderWord(header2);
+ }
+
+ // Construct the epilog scope words, if needed
+
+ if (!ufiSetEBit)
+ {
+ for (UnwindEpilogInfo* pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+#if defined(_TARGET_ARM_)
+ DWORD headerCondition = 0xE; // The epilog is unconditional. We don't have epilogs under the IT instruction.
+#endif // defined(_TARGET_ARM_)
+
+ // The epilog must strictly follow the prolog. The prolog is in the first fragment of
+ // the hot section. If this epilog is at the start of a fragment, it can't be the
+ // first fragment in the hot section. We actually don't know if we're processing
+ // the hot or cold section (or a funclet), so we can't distinguish these cases. Thus,
+ // we just assert that the epilog starts within the fragment.
+ assert(pEpi->GetStartOffset() >= GetStartOffset());
+
+ // We report the offset of an epilog as the offset from the beginning of the function/funclet fragment,
+ // NOT the offset from the beginning of the main function.
+ DWORD headerEpilogStartOffset = pEpi->GetStartOffset() - GetStartOffset();
+
+#if defined(_TARGET_ARM_)
+ noway_assert((headerEpilogStartOffset & 1) == 0);
+ headerEpilogStartOffset /= 2; // The unwind data stores the actual offset divided by 2 (since the low bit of
+ // the actual offset is always zero)
+#elif defined(_TARGET_ARM64_)
+ noway_assert((headerEpilogStartOffset & 3) == 0);
+ headerEpilogStartOffset /= 4; // The unwind data stores the actual offset divided by 4 (since the low 2 bits
+ // of the actual offset is always zero)
+#endif // defined(_TARGET_ARM64_)
+
+ DWORD headerEpilogStartIndex = pEpi->GetStartIndex();
+
+ if ((headerEpilogStartOffset > UW_MAX_EPILOG_START_OFFSET) ||
+ (headerEpilogStartIndex > UW_MAX_EPILOG_START_INDEX))
+ {
+ IMPL_LIMITATION("unwind data too large");
+ }
+
+#if defined(_TARGET_ARM_)
+ DWORD epilogScopeWord = headerEpilogStartOffset | (headerCondition << 20) | (headerEpilogStartIndex << 24);
+#elif defined(_TARGET_ARM64_)
+ DWORD epilogScopeWord = headerEpilogStartOffset | (headerEpilogStartIndex << 22);
+#endif // defined(_TARGET_ARM64_)
+
+ ufiPrologCodes.AddHeaderWord(epilogScopeWord);
+ }
+ }
+
+ // The unwind code words are already here, following the header, so we're done!
+}
+
+void UnwindFragmentInfo::Reserve(BOOL isFunclet, bool isHotCode)
+{
+ assert(isHotCode || !isFunclet); // TODO-CQ: support hot/cold splitting in functions with EH
+
+ MergeCodes();
+
+ BOOL isColdCode = isHotCode ? FALSE : TRUE;
+
+ ULONG unwindSize = Size();
+
+#ifdef DEBUG
+ if (uwiComp->verbose)
+ {
+ if (ufiNum != 1)
+ printf("reserveUnwindInfo: fragment #%d:\n", ufiNum);
+ }
+#endif
+
+ uwiComp->eeReserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate the unwind info for a fragment with the VM.
+// Arguments:
+// funKind: funclet kind
+// pHotCode: hot section code buffer
+// pColdCode: cold section code buffer
+// funcEndOffset: offset of the end of this function/funclet. Used if this fragment is the last one for a
+// function/funclet.
+// isHotCode: are we allocating the unwind info for the hot code section?
+
+void UnwindFragmentInfo::Allocate(
+ CorJitFuncKind funKind, void* pHotCode, void* pColdCode, UNATIVE_OFFSET funcEndOffset, bool isHotCode)
+{
+ UNATIVE_OFFSET startOffset;
+ UNATIVE_OFFSET endOffset;
+ UNATIVE_OFFSET codeSize;
+
+ // We don't support hot/cold splitting with EH, so if there is cold code, this
+ // better not be a funclet!
+ // TODO-CQ: support funclets in cold code
+
+ noway_assert(isHotCode || funKind == CORJIT_FUNC_ROOT);
+
+ // Compute the final size, and start and end offsets of the fragment
+
+ startOffset = GetStartOffset();
+
+ if (ufiNext == NULL)
+ {
+ // This is the last fragment, so the fragment extends to the end of the function/fragment.
+ assert(funcEndOffset != 0);
+ endOffset = funcEndOffset;
+ }
+ else
+ {
+ // The fragment length is all the code between the beginning of this fragment
+ // and the beginning of the next fragment. Note that all fragments have had their
+ // offsets computed before any fragment is allocated.
+ endOffset = ufiNext->GetStartOffset();
+ }
+
+ assert(endOffset > startOffset);
+ codeSize = endOffset - startOffset;
+
+ // Finalize the fragment unwind block to hand to the VM
+
+ Finalize(codeSize);
+
+ // Get the final unwind information and hand it to the VM
+
+ ULONG unwindBlockSize;
+ BYTE* pUnwindBlock;
+
+ GetFinalInfo(&pUnwindBlock, &unwindBlockSize);
+
+#ifdef DEBUG
+ if (uwiComp->opts.dspUnwind)
+ {
+ DumpUnwindInfo(uwiComp, isHotCode, startOffset, endOffset, pUnwindBlock, unwindBlockSize);
+ }
+#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 <= uwiComp->info.compTotalHotCodeSize);
+ pColdCode = NULL;
+ }
+ else
+ {
+ assert(startOffset >= uwiComp->info.compTotalHotCodeSize);
+ startOffset -= uwiComp->info.compTotalHotCodeSize;
+ endOffset -= uwiComp->info.compTotalHotCodeSize;
+ }
+
+#ifdef DEBUG
+ if (uwiComp->verbose)
+ {
+ if (ufiNum != 1)
+ printf("unwindEmit: fragment #%d:\n", ufiNum);
+ }
+#endif // DEBUG
+
+ uwiComp->eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindBlockSize, pUnwindBlock,
+ funKind);
+}
+
+#ifdef DEBUG
+void UnwindFragmentInfo::Dump(int indent)
+{
+ unsigned count;
+ UnwindEpilogInfo* pEpi;
+
+ count = 0;
+ for (pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+ ++count;
+ }
+
+ printf("%*sUnwindFragmentInfo #%d, @0x%08p, size:%d:\n", indent, "", ufiNum, dspPtr(this), sizeof(*this));
+ printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
+ printf("%*s ufiNext: 0x%08p\n", indent, "", dspPtr(ufiNext));
+ printf("%*s ufiEmitLoc: 0x%08p\n", indent, "", dspPtr(ufiEmitLoc));
+ printf("%*s ufiHasPhantomProlog: %s\n", indent, "", dspBool(ufiHasPhantomProlog));
+ printf("%*s %d epilog%s\n", indent, "", count, (count != 1) ? "s" : "");
+ printf("%*s ufiEpilogList: 0x%08p\n", indent, "", dspPtr(ufiEpilogList));
+ printf("%*s ufiEpilogLast: 0x%08p\n", indent, "", dspPtr(ufiEpilogLast));
+ printf("%*s ufiCurCodes: 0x%08p\n", indent, "", dspPtr(ufiCurCodes));
+ printf("%*s ufiSize: %u\n", indent, "", ufiSize);
+ printf("%*s ufiSetEBit: %s\n", indent, "", dspBool(ufiSetEBit));
+ printf("%*s ufiNeedExtendedCodeWordsEpilogCount: %s\n", indent, "", dspBool(ufiNeedExtendedCodeWordsEpilogCount));
+ printf("%*s ufiCodeWords: %u\n", indent, "", ufiCodeWords);
+ printf("%*s ufiEpilogScopes: %u\n", indent, "", ufiEpilogScopes);
+ printf("%*s ufiStartOffset: 0x%x\n", indent, "", ufiStartOffset);
+ printf("%*s ufiInProlog: %s\n", indent, "", dspBool(ufiInProlog));
+ printf("%*s ufiInitialized: 0x%08x\n", indent, "", ufiInitialized);
+
+ ufiPrologCodes.Dump(indent + 2);
+
+ for (pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
+ {
+ pEpi->Dump(indent + 2);
+ }
+}
+#endif // DEBUG
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// UnwindInfo
+//
+///////////////////////////////////////////////////////////////////////////////
+
+void UnwindInfo::InitUnwindInfo(Compiler* comp, emitLocation* startLoc, emitLocation* endLoc)
+{
+ uwiComp = comp;
+
+ // The first fragment is a member of UnwindInfo, so it doesn't need to be allocated.
+ // However, its constructor needs to be explicitly called, since the constructor for
+ // UnwindInfo is not called.
+
+ uwiFragmentFirst.UnwindFragmentInfo::UnwindFragmentInfo(comp, startLoc, false);
+
+ uwiFragmentLast = &uwiFragmentFirst;
+
+ uwiEndLoc = endLoc;
+
+ // Allocate an emitter location object. It is initialized to something
+ // invalid: it has a null 'ig' that needs to get set before it can be used.
+ // Note that when we create an UnwindInfo for the cold section, this never
+ // gets initialized with anything useful, since we never add unwind codes
+ // to the cold section; we simply distribute the existing (previously added) codes.
+ uwiCurLoc = new (uwiComp, CMK_UnwindInfo) emitLocation();
+
+#ifdef DEBUG
+ uwiInitialized = UWI_INITIALIZED_PATTERN;
+ uwiAddingNOP = false;
+#endif // DEBUG
+}
+
+// Split the unwind codes in 'puwi' into those that are in the hot section (leave them in 'puwi')
+// and those that are in the cold section (move them to 'this'). There is exactly one fragment
+// in each UnwindInfo; the fragments haven't been split for size, yet.
+
+void UnwindInfo::HotColdSplitCodes(UnwindInfo* puwi)
+{
+ // Ensure that there is exactly a single fragment in both the hot and the cold sections
+ assert(&uwiFragmentFirst == uwiFragmentLast);
+ assert(&puwi->uwiFragmentFirst == puwi->uwiFragmentLast);
+ assert(uwiFragmentLast->ufiNext == NULL);
+ assert(puwi->uwiFragmentLast->ufiNext == NULL);
+
+ // The real prolog is in the hot section, so this, cold, section has a phantom prolog
+ uwiFragmentLast->ufiHasPhantomProlog = true;
+ uwiFragmentLast->CopyPrologCodes(puwi->uwiFragmentLast);
+
+ // Now split the epilog codes
+ uwiFragmentLast->SplitEpilogCodes(uwiFragmentLast->ufiEmitLoc, puwi->uwiFragmentLast);
+}
+
+// Split the function or funclet into fragments that are no larger than 512K,
+// so the fragment size will fit in the unwind data "Function Length" field.
+// The ARM Exception Data specification "Function Fragments" section describes this.
+// We split the function so that it is no larger than 512K bytes, or the value of
+// the COMPlus_JitSplitFunctionSize value, if defined (and smaller). We must determine
+// how to split the function/funclet before we issue the instructions, so we can
+// reserve the unwind space with the VM. The instructions issued may shrink (but not
+// expand!) during issuing (although this is extremely rare in any case, and may not
+// actually occur on ARM), so we don't finalize actual sizes or offsets.
+//
+// ARM64 has very similar limitations, except functions can be up to 1MB. TODO-ARM64-Bug?: make sure this works!
+//
+// We don't split any prolog or epilog. Ideally, we might not split an instruction,
+// although that doesn't matter because the unwind at any point would still be
+// well-defined.
+
+void UnwindInfo::Split()
+{
+ UNATIVE_OFFSET maxFragmentSize; // The maximum size of a code fragment in bytes
+
+ maxFragmentSize = UW_MAX_FRAGMENT_SIZE_BYTES;
+
+#ifdef DEBUG
+ // Consider COMPlus_JitSplitFunctionSize
+ unsigned splitFunctionSize = (unsigned)JitConfig.JitSplitFunctionSize();
+
+ if (splitFunctionSize != 0)
+ if (splitFunctionSize < maxFragmentSize)
+ maxFragmentSize = splitFunctionSize;
+#endif // DEBUG
+
+ // Now, there should be exactly one fragment.
+
+ assert(uwiFragmentLast != NULL);
+ assert(uwiFragmentLast == &uwiFragmentFirst);
+ assert(uwiFragmentLast->ufiNext == NULL);
+
+ // Find the code size of this function/funclet.
+
+ UNATIVE_OFFSET startOffset;
+ UNATIVE_OFFSET endOffset;
+ UNATIVE_OFFSET codeSize;
+
+ if (uwiFragmentLast->ufiEmitLoc == NULL)
+ {
+ // NULL emit location means the beginning of the code. This is to handle the first fragment prolog.
+ startOffset = 0;
+ }
+ else
+ {
+ startOffset = uwiFragmentLast->ufiEmitLoc->CodeOffset(uwiComp->genEmitter);
+ }
+
+ if (uwiEndLoc == NULL)
+ {
+ // Note that compTotalHotCodeSize and compTotalColdCodeSize are computed before issuing instructions
+ // from the emitter instruction group offsets, and will be accurate unless the issued code shrinks.
+ // compNativeCodeSize is precise, but is only set after instructions are issued, which is too late
+ // for us, since we need to decide how many fragments we need before the code memory is allocated
+ // (which is before instruction issuing).
+ UNATIVE_OFFSET estimatedTotalCodeSize =
+ uwiComp->info.compTotalHotCodeSize + uwiComp->info.compTotalColdCodeSize;
+ assert(estimatedTotalCodeSize != 0);
+ endOffset = estimatedTotalCodeSize;
+ }
+ else
+ {
+ endOffset = uwiEndLoc->CodeOffset(uwiComp->genEmitter);
+ }
+
+ assert(endOffset > startOffset); // there better be at least 1 byte of code
+ codeSize = endOffset - startOffset;
+
+ // Now that we know the code size for this section (main function hot or cold, or funclet),
+ // figure out how many fragments we're going to need.
+
+ UNATIVE_OFFSET numberOfFragments = (codeSize + maxFragmentSize - 1) / maxFragmentSize; // round up
+ assert(numberOfFragments > 0);
+
+ if (numberOfFragments == 1)
+ {
+ // No need to split; we're done
+ return;
+ }
+
+ // Now, we're going to commit to splitting the function into "numberOfFragments" fragments,
+ // for the purpose of unwind information. We need to do the actual splits so we can figure out
+ // the size of each piece of unwind data for the call to reserveUnwindInfo(). We won't know
+ // the actual offsets of the splits since we haven't issued the instructions yet, so store
+ // an emitter location instead of an offset, and "finalize" the offset in the unwindEmit() phase,
+ // like we do for the function length and epilog offsets.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef DEBUG
+ if (uwiComp->verbose)
+ {
+ printf("Split unwind info into %d fragments (function/funclet size: %d, maximum fragment size: %d)\n",
+ numberOfFragments, codeSize, maxFragmentSize);
+ }
+#endif // DEBUG
+
+ // Call the emitter to do the split, and call us back for every split point it chooses.
+ uwiComp->genEmitter->emitSplit(uwiFragmentLast->ufiEmitLoc, uwiEndLoc, maxFragmentSize, (void*)this,
+ EmitSplitCallback);
+
+#ifdef DEBUG
+ // Did the emitter split the function/funclet into as many fragments as we asked for?
+ // It might be fewer if the COMPlus_JitSplitFunctionSize was used, but it better not
+ // be fewer if we're splitting into 512K blocks!
+
+ unsigned fragCount = 0;
+ for (UnwindFragmentInfo* pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ ++fragCount;
+ }
+ if (fragCount < numberOfFragments)
+ {
+ if (uwiComp->verbose)
+ {
+ printf("WARNING: asked the emitter for %d fragments, but only got %d\n", numberOfFragments, fragCount);
+ }
+
+ // If this fires, then we split into fewer fragments than we asked for, and we are using
+ // the default, unwind-data-defined 512K maximum fragment size. We won't be able to fit
+ // this fragment into the unwind data! If you set COMPlus_JitSplitFunctionSize to something
+ // small, we might not be able to split into as many fragments as asked for, because we
+ // can't split prologs or epilogs.
+ assert(maxFragmentSize != UW_MAX_FRAGMENT_SIZE_BYTES);
+ }
+#endif // DEBUG
+}
+
+/*static*/ void UnwindInfo::EmitSplitCallback(void* context, emitLocation* emitLoc)
+{
+ UnwindInfo* puwi = (UnwindInfo*)context;
+ puwi->AddFragment(emitLoc);
+}
+
+// Reserve space for the unwind info for all fragments
+
+void UnwindInfo::Reserve(BOOL isFunclet, bool isHotCode)
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+ assert(isHotCode || !isFunclet);
+
+ for (UnwindFragmentInfo* pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ pFrag->Reserve(isFunclet, isHotCode);
+ }
+}
+
+// Allocate and populate VM unwind info for all fragments
+
+void UnwindInfo::Allocate(CorJitFuncKind funKind, void* pHotCode, void* pColdCode, bool isHotCode)
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+
+ UnwindFragmentInfo* pFrag;
+
+ // First, finalize all the offsets (the location of the beginning of fragments, and epilogs),
+ // so a fragment can use the finalized offset of the subsequent fragment to determine its code size.
+
+ UNATIVE_OFFSET endOffset;
+
+ if (uwiEndLoc == NULL)
+ {
+ assert(uwiComp->info.compNativeCodeSize != 0);
+ endOffset = uwiComp->info.compNativeCodeSize;
+ }
+ else
+ {
+ endOffset = uwiEndLoc->CodeOffset(uwiComp->genEmitter);
+ }
+
+ for (pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ pFrag->FinalizeOffset();
+ }
+
+ for (pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ pFrag->Allocate(funKind, pHotCode, pColdCode, endOffset, isHotCode);
+ }
+}
+
+void UnwindInfo::AddEpilog()
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+ assert(uwiFragmentLast != NULL);
+ uwiFragmentLast->AddEpilog();
+ CaptureLocation();
+}
+
+#if defined(_TARGET_ARM_)
+
+unsigned UnwindInfo::GetInstructionSize()
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+ return uwiComp->genEmitter->emitGetInstructionSize(uwiCurLoc);
+}
+
+#endif // defined(_TARGET_ARM_)
+
+void UnwindInfo::CaptureLocation()
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+ assert(uwiCurLoc != NULL);
+ uwiCurLoc->CaptureLocation(uwiComp->genEmitter);
+}
+
+void UnwindInfo::AddFragment(emitLocation* emitLoc)
+{
+ assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
+ assert(uwiFragmentLast != NULL);
+
+ UnwindFragmentInfo* newFrag = new (uwiComp, CMK_UnwindInfo) UnwindFragmentInfo(uwiComp, emitLoc, true);
+
+#ifdef DEBUG
+ newFrag->ufiNum = uwiFragmentLast->ufiNum + 1;
+#endif // DEBUG
+
+ newFrag->CopyPrologCodes(&uwiFragmentFirst);
+ newFrag->SplitEpilogCodes(emitLoc, uwiFragmentLast);
+
+ // Link the new fragment in at the end of the fragment list
+ uwiFragmentLast->ufiNext = newFrag;
+ uwiFragmentLast = newFrag;
+}
+
+#ifdef DEBUG
+
+#if defined(_TARGET_ARM_)
+
+// Given the first byte of the unwind code, check that its opsize matches
+// the last instruction added in the emitter.
+void UnwindInfo::CheckOpsize(BYTE b1)
+{
+ // Adding NOP padding goes through the same path, but doesn't update the location to indicate
+ // the correct location of the instruction for which we are adding a NOP, so just skip the
+ // assert. Should be ok, because the emitter is telling us the size of the instruction for
+ // which we are adding the NOP.
+ if (uwiAddingNOP)
+ return;
+
+ unsigned opsizeInBytes = GetOpcodeSizeFromUnwindHeader(b1);
+ unsigned instrSizeInBytes = GetInstructionSize();
+ assert(opsizeInBytes == instrSizeInBytes);
+}
+
+#endif // defined(_TARGET_ARM_)
+
+void UnwindInfo::Dump(bool isHotCode, int indent)
+{
+ unsigned count;
+ UnwindFragmentInfo* pFrag;
+
+ count = 0;
+ for (pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ ++count;
+ }
+
+ printf("%*sUnwindInfo %s@0x%08p, size:%d:\n", indent, "", isHotCode ? "" : "COLD ", dspPtr(this), sizeof(*this));
+ printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
+ printf("%*s %d fragment%s\n", indent, "", count, (count != 1) ? "s" : "");
+ printf("%*s uwiFragmentLast: 0x%08p\n", indent, "", dspPtr(uwiFragmentLast));
+ printf("%*s uwiEndLoc: 0x%08p\n", indent, "", dspPtr(uwiEndLoc));
+ printf("%*s uwiInitialized: 0x%08x\n", indent, "", uwiInitialized);
+
+ for (pFrag = &uwiFragmentFirst; pFrag != NULL; pFrag = pFrag->ufiNext)
+ {
+ pFrag->Dump(indent + 2);
+ }
+}
+
+#endif // DEBUG
+
+#if defined(_TARGET_ARM_)
+
+/*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 an integer register set. 'x' is an array of bits where bit 0 = r0, bit 1 = r1, etc.
+// The highest register considered is r12.
+// If 'lr' is non-zero, the "lr" register is emitted last.
+// Returns the number of characters printed.
+DWORD DumpIntRegSet(DWORD x, DWORD lr)
+{
+ assert(x != 0 || lr != 0); // we must have one
+ assert((x & 0xE000) == 0); // don't handle r13 (sp), r14 (lr), r15 (pc) in 'x'
+ DWORD printed = 0;
+
+ printf("{");
+ ++printed;
+ bool first = true;
+ DWORD bitMask = 1;
+ for (DWORD bitNum = 0; bitNum < 12; bitNum++)
+ {
+ if (x & bitMask)
+ {
+ if (!first)
+ {
+ printf(",");
+ ++printed;
+ }
+ printf("r%u", bitNum);
+ printed += (bitNum < 10) ? 2 : 3;
+ first = false;
+ }
+ bitMask <<= 1;
+ }
+ if (lr)
+ {
+ if (!first)
+ {
+ printf(",");
+ ++printed;
+ }
+ printf("lr");
+ printed += 2;
+ }
+ printf("}");
+ ++printed;
+
+ return printed;
+}
+
+// Dump a register set range from register 'start' to register 'end'.
+// rtype should be "r" or "d" to indicate register type.
+// If 'lr' is non-zero, the "lr" register is emitted last. (Note that
+// 'lr' should be zero for rtype == "d".)
+// Returns the number of characters printed.
+DWORD DumpRegSetRange(const char* const rtype, DWORD start, DWORD end, DWORD lr)
+{
+ assert(start <= end);
+ DWORD printed = 0;
+ DWORD rtypeLen = strlen(rtype);
+
+ printf("{");
+ ++printed;
+ bool first = true;
+ for (DWORD reg = start; reg <= end; reg++)
+ {
+ if (!first)
+ {
+ printf(",");
+ ++printed;
+ }
+ printf("%s%u", rtype, reg);
+ printed += rtypeLen + ((reg < 10) ? 1 : 2);
+ first = false;
+ }
+ if (lr)
+ {
+ assert(!first); // If 'lr' is set, it can't be first, since we require a non-empty range
+ printf(",lr");
+ printed += 3;
+ }
+ printf("}");
+ ++printed;
+
+ return printed;
+}
+
+// Dump the opsize.
+// Returns the number of characters printed.
+DWORD DumpOpsize(DWORD padding, DWORD opsize)
+{
+ if (padding > 100) // underflow?
+ padding = 4;
+ DWORD printed = padding;
+ for (; padding > 0; padding--)
+ printf(" ");
+ printf("; opsize %d\n", opsize);
+ return printed + 11; // assumes opsize is always 2 digits
+}
+
+// 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, 28, 4);
+ DWORD epilogCount = ExtractBits(dw, 23, 5);
+ DWORD FBit = ExtractBits(dw, 22, 1);
+ 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(" F bit : %u\n", FBit);
+ 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 * 2, functionLength * 2);
+
+ assert(functionLength * 2 == 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[256] = {}; // 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, 2);
+ DWORD condition = ExtractBits(dw, 20, 4);
+ DWORD epilogStartIndex = ExtractBits(dw, 24, 8);
+
+ // 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 * 2 + 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 * 2), comp->dspOffset(epilogStartOffset * 2),
+ comp->dspOffset(epilogStartOffsetFromMainFunctionBegin),
+ comp->dspOffset(epilogStartOffsetFromMainFunctionBegin));
+ printf(" Condition : %u (0x%x)%s\n", condition, condition,
+ (condition == 0xE) ? " (always)" : "");
+ 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 < sizeof(epilogStartAt) / sizeof(epilogStartAt[0]));
+ epilogStartAt[epilogCount] = true; // the one and only epilog starts its unwind codes at this offset
+ }
+
+ if (FBit)
+ {
+ printf(" ---- Note: 'F' bit is set. Prolog codes are for a 'phantom' prolog.\n");
+ }
+
+ // Dump the unwind codes
+
+ printf(" ---- Unwind codes ----\n");
+
+ DWORD countOfUnwindCodes = codeWords * 4;
+ PBYTE pUnwindCode = (PBYTE)pdw;
+ BYTE b1, b2, b3, b4;
+ DWORD x, y;
+ DWORD opsize;
+ DWORD opCol = 52;
+ DWORD printed;
+ 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 & 0x80) == 0)
+ {
+ // 00-7F : add sp, sp, #X*4 (opsize 16)
+ x = b1 & 0x7F;
+ printf(" %02X add sp, sp, #%-8d", b1, x * 4);
+ DumpOpsize(opCol - 37, 16);
+ }
+ else if ((b1 & 0xC0) == 0x80)
+ {
+ // 80-BF : pop {r0-r12,lr} (X = bitmask) (opsize 32)
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ DWORD LBit = ExtractBits(b1, 5, 1);
+ x = ((DWORD)(b1 & 0x1F) << 8) | (DWORD)b2;
+
+ printf(" %02X %02X pop ", b1, b2);
+ printed = 20;
+ printed += DumpIntRegSet(x, LBit);
+ DumpOpsize(opCol - printed, 32);
+ }
+ else if ((b1 & 0xF0) == 0xC0)
+ {
+ // C0-CF : mov sp, rX (X=0-15) (opsize 16)
+ x = b1 & 0xF;
+ printf(" %02X mov sp, r%u", b1, x);
+ printed = 25 + ((x > 10) ? 2 : 1);
+ DumpOpsize(opCol - printed, 16);
+ }
+ else if ((b1 & 0xF8) == 0xD0)
+ {
+ // D0-D7 : pop {r4-rX,lr} (X=4-7) (opsize 16)
+ x = b1 & 0x3;
+ DWORD LBit = b1 & 0x4;
+ printf(" %02X pop ", b1);
+ printed = 20;
+ printed += DumpRegSetRange("r", 4, x + 4, LBit);
+ DumpOpsize(opCol - printed, 16);
+ }
+ else if ((b1 & 0xF8) == 0xD8)
+ {
+ // D8-DF : pop {r4-rX,lr} (X=8-11) (opsize 32)
+ x = b1 & 0x3;
+ DWORD LBit = b1 & 0x4;
+ printf(" %02X pop ", b1);
+ printed = 20;
+ printed += DumpRegSetRange("r", 4, x + 8, LBit);
+ DumpOpsize(opCol - printed, 32);
+ }
+ else if ((b1 & 0xF8) == 0xE0)
+ {
+ // E0-E7 : vpop {d8-dX} (X=8-15) (opsize 32)
+ x = b1 & 0x7;
+ printf(" %02X vpop ", b1);
+ printed = 21;
+ printed += DumpRegSetRange("d", 8, x + 8, 0);
+ DumpOpsize(opCol - printed, 32);
+ }
+ else if ((b1 & 0xFC) == 0xE8)
+ {
+ // E8-EB : addw sp, sp, #X*4 (opsize 32)
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ x = ((DWORD)(b1 & 0x3) << 8) | (DWORD)b2;
+
+ printf(" %02X %02X addw sp, sp, #%-8u", b1, b2, x * 4);
+ DumpOpsize(opCol - 38, 32);
+ }
+ else if ((b1 & 0xFE) == 0xEC)
+ {
+ // EC-ED : pop {r0-r7,lr} (X = bitmask) (opsize 16)
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ DWORD LBit = ExtractBits(b1, 0, 1);
+ x = (DWORD)b2;
+
+ printf(" %02X %02X pop ", b1, b2);
+ printed = 20;
+ printed += DumpIntRegSet(x, LBit);
+ DumpOpsize(opCol - printed, 16);
+ }
+ else if (b1 == 0xEE)
+ {
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ if ((b2 & 0xF0) == 0)
+ {
+ // EE/0x (opsize 16)
+ x = b2 & 0xF;
+ printf(" %02X %02X Microsoft-specific (x = %02X)", b1, b2, x);
+ DumpOpsize(4, 16);
+ }
+ else
+ {
+ // EE/xy (opsize 16)
+ x = ExtractBits(b2, 4, 4);
+ y = ExtractBits(b2, 0, 4);
+ printf(" %02X %02X Available (x = %02X, y = %02X)", b1, b2, x, y);
+ DumpOpsize(4, 16);
+ }
+ }
+ else if (b1 == 0xEF)
+ {
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ if ((b2 & 0xF0) == 0)
+ {
+ // EF/0x : ldr lr, [sp], #X*4 (opsize 32)
+ x = b2 & 0xF;
+ printf(" %02X %02X ldr lr, [sp], #%-8u", b1, b2, x * 4);
+ DumpOpsize(opCol - 39, 32);
+ }
+ else
+ {
+ // EF/xy (opsize 32)
+ x = ExtractBits(b2, 4, 4);
+ y = ExtractBits(b2, 0, 4);
+ printf(" %02X %02X Available (x = %02X, y = %02X)", b1, b2, x, y);
+ DumpOpsize(4, 32);
+ }
+ }
+ else if ((b1 & 0xF7) == 0xF0)
+ {
+ // F0-F4
+ x = b1 & 0x7;
+ printf(" %02X Available (x = %02X)\n", b1, x);
+ }
+ else if (b1 == 0xF5)
+ {
+ // F5 : vpop {dS-dE} (opsize 32)
+
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ DWORD s = ExtractBits(b2, 4, 4);
+ DWORD e = ExtractBits(b2, 0, 4);
+
+ printf(" %02X %02X vpop ", b1, b2);
+ printed = 21;
+ printed += DumpRegSetRange("d", s, e, 0);
+ DumpOpsize(opCol - printed, 32);
+ }
+ else if (b1 == 0xF6)
+ {
+ // F6 : vpop {d(S+16)-d(E+16)} (opsize 32)
+
+ assert(i + 1 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ i++;
+
+ DWORD s = ExtractBits(b2, 4, 4);
+ DWORD e = ExtractBits(b2, 0, 4);
+
+ printf(" %02X %02X vpop ", b1, b2);
+ printed = 21;
+ printed += DumpRegSetRange("d", s + 16, e + 16, 0);
+ DumpOpsize(opCol - printed, 32);
+ }
+ else if (b1 == 0xF7 || b1 == 0xF9)
+ {
+ // F7, F9 : add sp, sp, #X*4
+ // 0xF7 has opsize 16, 0xF9 has opsize 32
+
+ assert(i + 2 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ b3 = *pUnwindCode++;
+ i += 2;
+
+ x = ((DWORD)b2 << 8) | (DWORD)b3;
+
+ opsize = (b1 == 0xF7) ? 16 : 32;
+
+ printf(" %02X %02X %02X add sp, sp, #%-8u", b1, b2, b3, x * 4, opsize);
+ DumpOpsize(opCol - 37, opsize);
+ }
+ else if (b1 == 0xF8 || b1 == 0xFA)
+ {
+ // F8, FA : add sp, sp, #X*4
+ // 0xF8 has opsize 16, 0xFA has opsize 32
+
+ assert(i + 3 < countOfUnwindCodes);
+ b2 = *pUnwindCode++;
+ b3 = *pUnwindCode++;
+ b4 = *pUnwindCode++;
+ i += 3;
+
+ x = ((DWORD)b2 << 16) | ((DWORD)b3 << 8) | (DWORD)b4;
+
+ opsize = (b1 == 0xF8) ? 16 : 32;
+
+ printf(" %02X %02X %02X %02X add sp, sp, #%-8u", b1, b2, b3, b4, x * 4, opsize);
+ DumpOpsize(opCol - 37, opsize);
+ }
+ else if (b1 == 0xFB || b1 == 0xFC)
+ {
+ // FB, FC : nop
+ // 0xFB has opsize 16, 0xFC has opsize 32
+
+ opsize = (b1 == 0xFB) ? 16 : 32;
+
+ printf(" %02X nop", b1, opsize);
+ DumpOpsize(opCol - 19, opsize);
+ }
+ else if (b1 == 0xFD || b1 == 0xFE)
+ {
+ // FD, FE : end + nop
+ // 0xFD has opsize 16, 0xFE has opsize 32
+
+ opsize = (b1 == 0xFD) ? 16 : 32;
+
+ printf(" %02X end + nop", b1, opsize);
+ DumpOpsize(opCol - 25, opsize);
+ }
+ else if (b1 == 0xFF)
+ {
+ // FF : end
+
+ printf(" %02X end\n", b1);
+ }
+ else
+ {
+ 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 // defined(_TARGET_ARM_)
+
+#endif // _TARGET_ARMARCH_