text: // 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_ARM_) && defined(_TARGET_UNIX_)
  int Compiler::mapRegNumToDwarfReg(regNumber reg)
  {
      int dwarfReg = DWARF_REG_ILLEGAL;
  
      switch (reg)
      {
          case REG_R0:
              dwarfReg = 0;
              break;
          case REG_R1:
              dwarfReg = 1;
              break;
          case REG_R2:
              dwarfReg = 2;
              break;
          case REG_R3:
              dwarfReg = 3;
              break;
          case REG_R4:
              dwarfReg = 4;
              break;
          case REG_R5:
              dwarfReg = 5;
              break;
          case REG_R6:
              dwarfReg = 6;
              break;
          case REG_R7:
              dwarfReg = 7;
              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_F0:
              dwarfReg = 64;
              break;
          case REG_F1:
              dwarfReg = 65;
              break;
          case REG_F2:
              dwarfReg = 66;
              break;
          case REG_F3:
              dwarfReg = 67;
              break;
          case REG_F4:
              dwarfReg = 68;
              break;
          case REG_F5:
              dwarfReg = 69;
              break;
          case REG_F6:
              dwarfReg = 70;
              break;
          case REG_F7:
              dwarfReg = 71;
              break;
          case REG_F8:
              dwarfReg = 72;
              break;
          case REG_F9:
              dwarfReg = 73;
              break;
          case REG_F10:
              dwarfReg = 74;
              break;
          case REG_F11:
              dwarfReg = 75;
              break;
          case REG_F12:
              dwarfReg = 76;
              break;
          case REG_F13:
              dwarfReg = 77;
              break;
          case REG_F14:
              dwarfReg = 78;
              break;
          case REG_F15:
              dwarfReg = 79;
              break;
          case REG_F16:
              dwarfReg = 80;
              break;
          case REG_F17:
              dwarfReg = 81;
              break;
          case REG_F18:
              dwarfReg = 82;
              break;
          case REG_F19:
              dwarfReg = 83;
              break;
          case REG_F20:
              dwarfReg = 84;
              break;
          case REG_F21:
              dwarfReg = 85;
              break;
          case REG_F22:
              dwarfReg = 86;
              break;
          case REG_F23:
              dwarfReg = 87;
              break;
          case REG_F24:
              dwarfReg = 88;
              break;
          case REG_F25:
              dwarfReg = 89;
              break;
          case REG_F26:
              dwarfReg = 90;
              break;
          case REG_F27:
              dwarfReg = 91;
              break;
          case REG_F28:
              dwarfReg = 92;
              break;
          case REG_F29:
              dwarfReg = 93;
              break;
          case REG_F30:
              dwarfReg = 94;
              break;
          case REG_F31:
              dwarfReg = 95;
              break;
          default:
              noway_assert(!"unexpected REG_NUM");
      }
  
      return dwarfReg;
  }
  #endif // _TARGET_ARM_ && _TARGET_UNIX_
  
  #ifdef _TARGET_ARMARCH_
  
  /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  XX                                                                           XX
  XX  Unwind APIs                                                              XX
  XX                                                                           XX
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  */
  
  void Compiler::unwindBegProlog()
  {
      assert(compGeneratingProlog);
  
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          unwindBegPrologCFI();
          return;
      }
  #endif // _TARGET_UNIX_
  
      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);
  
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      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);
  
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          // If we are pushing LR, we should give unwind codes in terms of caller's PC
          if (maskInt & RBM_LR)
          {
              maskInt = (maskInt & ~RBM_LR) | RBM_PC;
          }
          unwindPushPopMaskCFI(maskInt, false);
          return;
      }
  #endif // _TARGET_UNIX_
  
      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);
  
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          unwindPushPopMaskCFI(maskFloat, true);
          return;
      }
  #endif // _TARGET_UNIX_
  
      unwindPushPopMaskFloat(maskFloat);
  }
  
  void Compiler::unwindPopMaskInt(regMaskTP maskInt)
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      // 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)
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      // Only floating point registers should be in maskFloat
      assert((maskFloat & RBM_ALLFLOAT) == maskFloat);
      unwindPushPopMaskFloat(maskFloat);
  }
  
  void Compiler::unwindAllocStack(unsigned size)
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          if (compGeneratingProlog)
          {
              unwindAllocStackCFI(size);
          }
          return;
      }
  #endif // _TARGET_UNIX_
  
      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)
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          if (compGeneratingProlog)
          {
              unwindSetFrameRegCFI(reg, offset);
          }
          return;
      }
  #endif // _TARGET_UNIX_
  
      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()
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      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
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      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()
  {
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          return;
      }
  #endif // _TARGET_UNIX_
  
      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(!compGeneratingProlog);
      assert(!compGeneratingEpilog);
  
      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 defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          DWORD unwindCodeBytes = 0;
          if (fgFirstColdBlock != nullptr)
          {
              eeReserveUnwindInfo(isFunclet, true /*isColdCode*/, unwindCodeBytes);
          }
          unwindCodeBytes = (DWORD)(func->cfiCodes->size() * sizeof(CFI_CODE));
          eeReserveUnwindInfo(isFunclet, false /*isColdCode*/, unwindCodeBytes);
  
          return;
      }
  #endif // _TARGET_UNIX_
  
      // 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);
  
  #if defined(_TARGET_UNIX_)
      if (generateCFIUnwindCodes())
      {
          unwindEmitFuncCFI(func, pHotCode, pColdCode);
          return;
      }
  #endif // _TARGET_UNIX_
  
      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.
  
      new (&uwiFragmentFirst, jitstd::placement_t()) 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 = (DWORD)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 < _countof(epilogStartAt));
          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_
  