text: //-------------------------------------------------------------------------------------------------------
  // Copyright (C) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  //-------------------------------------------------------------------------------------------------------
  class Encoder;
  
  
  enum RelocType {
      RelocTypeBranch,                // cond, uncond branch
      RelocTypeCallPcrel,             // calls
      RelocTypeLabelUse,              // direct use of a label
      RelocTypeLabel,                 // points to label instr
      RelocTypeAlignedLabel,          // points to loop-top label instr that needs alignment
      RelocTypeInlineeEntryOffset,    // points to offset immediate in buffer
  };
  
  
  ///---------------------------------------------------------------------------
  ///
  /// class EncoderReloc
  ///
  ///---------------------------------------------------------------------------
  
  class EncodeRelocAndLabels
  {
  public:
      RelocType           m_type;
      void *              m_ptr;          // points to encoded buffer byte or LabelInstr if RelocTypeLabel
      void *              m_origPtr;      // copy of m_ptr to be used to get offset in the source buffer during BR shortening
  
  private:
      union
      {
          IR::LabelInstr* m_labelInstr;        // ptr to Br Label
          BYTE            m_nopCount;
          uint64          m_InlineeOffset;
      };
      bool                m_isShortBr;
  
  public:
      void                init(RelocType type, void* ptr, IR::LabelInstr* labelInstr = nullptr)
      {
          m_type = type;
          m_ptr = ptr;
          m_InlineeOffset = 0;
          m_isShortBr = false;
  
          if (type == RelocTypeLabel)
          {
              // preserve original PC for labels
              m_origPtr   = (void*)((IR::LabelInstr*)ptr)->GetPC();
              m_nopCount  = 0;
          }
          else
          {
              m_origPtr = ptr;
              
              if (type == RelocTypeBranch)
              {
                  Assert(labelInstr);
                  m_labelInstr = labelInstr;
                  m_isShortBr = false;
              }
              else if (type == RelocTypeLabelUse)
              {
                  Assert(labelInstr);
                  m_labelInstr = labelInstr;
              }
          }
      }
  
      void                revert()
      {
          // recover old label PC
          if (isLabel())
          {
              // recover old label PC and reset alignment nops
              // we keep aligned labels type so we align them on the second attempt.
              setLabelCurrPC(getLabelOrigPC());
              m_nopCount = 0;
              return;
          }
  
          if (m_type == RelocTypeBranch)
          {
              m_isShortBr = false;
          }
  
          m_ptr = m_origPtr;
      }
  
      bool                isLabel()           const { return isAlignedLabel() || m_type == RelocTypeLabel; }
      bool                isAlignedLabel()    const { return m_type == RelocTypeAlignedLabel; }
      bool                isLongBr()          const { return m_type == RelocTypeBranch && !m_isShortBr; }
      bool                isShortBr()         const { return m_type == RelocTypeBranch && m_isShortBr; }
      BYTE*               getBrOpCodeByte()   const { return (BYTE*)m_origPtr - 1;}
  
      IR::LabelInstr *    getBrTargetLabel()  const
      {
          Assert((m_type == RelocTypeBranch || m_type == RelocTypeLabelUse) && m_labelInstr);
          return m_labelInstr;
      }
      IR::LabelInstr *    getLabel()  const
      {
          Assert(isLabel());
          return (IR::LabelInstr*) m_ptr;
      }
      // get label original PC without shortening/alignment
      BYTE *  getLabelOrigPC()  const
      {
          Assert(isLabel());
          return ((BYTE*) m_origPtr);
      }
  
      // get label PC after shortening/alignment
      BYTE *  getLabelCurrPC()  const
      {
          Assert(isLabel());
          return getLabel()->GetPC();
      }
  
      BYTE    getLabelNopCount() const
      {
          Assert(isAlignedLabel());
          return m_nopCount;
      }
  
      void    setLabelCurrPC(BYTE* pc)
      {
          Assert(isLabel());
          getLabel()->SetPC(pc);
      }
  
      void    setLabelNopCount(BYTE nopCount)
      {
          Assert(isAlignedLabel());
          Assert (nopCount >= 0 && nopCount < 16);
          m_nopCount = nopCount;
      }
  
      void                setAsShortBr()
      {
          Assert(m_type == RelocTypeBranch);
          m_isShortBr = true;
      }
  
      // Validates if the branch is short and its target PC fits in one byte
      bool                validateShortBrTarget() const
      {
          return isShortBr() &&
              getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) >= -128 &&
              getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) <= 127;
      }
  
      uint64 GetInlineOffset()
      {
          return m_InlineeOffset;
      }
  
      void SetInlineOffset(uint64 offset)
      {
          m_InlineeOffset = offset;
      }
  };
  
  
  
  ///---------------------------------------------------------------------------
  ///
  /// class EncoderMD
  ///
  ///---------------------------------------------------------------------------
  enum Forms : BYTE;
  
  typedef JsUtil::List<EncodeRelocAndLabels, ArenaAllocator> RelocList;
  typedef JsUtil::List<InlineeFrameRecord*, ArenaAllocator> InlineeFrameRecords;
  
  class EncoderMD
  {
  public:
      EncoderMD(Func * func) : m_func(func) {}
      ptrdiff_t       Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr);
      void            Init(Encoder *encoder);
      void            ApplyRelocs(size_t codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false);
      uint            GetRelocDataSize(EncodeRelocAndLabels *reloc);
      BYTE *          GetRelocBufferAddress(EncodeRelocAndLabels * reloc);
      void            EncodeInlineeCallInfo(IR::Instr *instr, uint32 offset);
      static bool     TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
      static bool     TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
      static bool     SetsConditionCode(IR::Instr *instr);
      static bool     UsesConditionCode(IR::Instr *instr);
      static bool     IsOPEQ(IR::Instr *instr);
      static bool     IsMOVEncoding(IR::Instr *instr);
      RelocList*      GetRelocList() const { return m_relocList; }
      int             AppendRelocEntry(RelocType type, void *ptr, IR::LabelInstr *label= nullptr);
      int             FixRelocListEntry(uint32 index, int totalBytesSaved, BYTE *buffStart, BYTE* buffEnd);
      void            FixMaps(uint32 brOffset, uint32 bytesSaved, uint32 *inlineeFrameRecordsIndex, uint32 *inlineeFrameMapIndex,  uint32 *pragmaInstToRecordOffsetIndex, uint32 *offsetBuffIndex);
      void            UpdateRelocListWithNewBuffer(RelocList * relocList, BYTE * newBuffer, BYTE * oldBufferStart, BYTE * oldBufferEnd);
  #ifdef DBG
      void            VerifyRelocList(BYTE *buffStart, BYTE *buffEnd);
  #endif
      void            AddLabelReloc(BYTE* relocAddress);
  
  private:
      const BYTE      GetOpcodeByte2(IR::Instr *instr);
      static Forms    GetInstrForm(IR::Instr *instr);
      const BYTE *    GetFormTemplate(IR::Instr *instr);
      const BYTE *    GetOpbyte(IR::Instr *instr);
      const BYTE      GetRegEncode(IR::RegOpnd *regOpnd);
      const BYTE      GetRegEncode(RegNum reg);
      static const uint32 GetOpdope(IR::Instr *instr);
      const uint32    GetLeadIn(IR::Instr * instr);
      BYTE            EmitModRM(IR::Instr * instr, IR::Opnd *opnd, BYTE reg1);
      void            EmitConst(size_t val, int size, bool allowImm64 = false);
      BYTE            EmitImmed(IR::Opnd * opnd, int opSize, int sbit, bool allowImm64 = false);
      static bool     FitsInByte(size_t value);
      bool            IsExtendedRegister(RegNum reg);
      BYTE            GetMod(IR::IndirOpnd * opr, int* pDispSize);
      BYTE            GetMod(IR::SymOpnd * opr, int* pDispSize, RegNum& rmReg);
      BYTE            GetMod(size_t offset, bool regIsRbpOrR13, int * pDispSize);
      BYTE            GetRexByte(BYTE rexCode, IR::Opnd * opnd);
      BYTE            GetRexByte(BYTE rexCode, RegNum reg);
      int             GetOpndSize(IR::Opnd * opnd);
  
      void            EmitRexByte(BYTE * prexByte, BYTE rexByte, bool skipRexByte, bool reservedRexByte);
  
      enum
      {
          REXOVERRIDE  = 0x40,
          REXW = 8,
          REXR = 4,
          REXX = 2,
          REXB = 1,
      };
  
      static const BYTE Mod00 = 0x00 << 6;
      static const BYTE Mod01 = 0x01 << 6;
      static const BYTE Mod10 = 0x02 << 6;
      static const BYTE Mod11 = 0x03 << 6;
  
  private:
      Func *          m_func;
      Encoder *       m_encoder;
      BYTE *          m_pc;
      RelocList *     m_relocList;
      int             m_lastLoopLabelPosition;
  };
  