summaryrefslogtreecommitdiff
path: root/src/jit/emit.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/emit.h')
-rw-r--r--src/jit/emit.h2742
1 files changed, 2742 insertions, 0 deletions
diff --git a/src/jit/emit.h b/src/jit/emit.h
new file mode 100644
index 0000000000..8fb24bcd60
--- /dev/null
+++ b/src/jit/emit.h
@@ -0,0 +1,2742 @@
+// 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.
+/*****************************************************************************/
+
+#ifndef _EMIT_H_
+#define _EMIT_H_
+
+#include "instr.h"
+
+#ifndef _GCINFO_H_
+#include "gcinfo.h"
+#endif
+
+#include "jitgcinfo.h"
+
+/*****************************************************************************/
+#ifdef TRANSLATE_PDB
+#ifndef _ADDRMAP_INCLUDED_
+#include "addrmap.h"
+#endif
+#ifndef _LOCALMAP_INCLUDED_
+#include "localmap.h"
+#endif
+#ifndef _PDBREWRITE_H_
+#include "pdbrewrite.h"
+#endif
+#endif // TRANSLATE_PDB
+
+/*****************************************************************************/
+#ifdef _MSC_VER
+#pragma warning(disable : 4200) // allow arrays of 0 size inside structs
+#endif
+#define TRACK_GC_TEMP_LIFETIMES 0
+
+/*****************************************************************************/
+
+#if 0
+#define EMITVERBOSE 1
+#else
+#define EMITVERBOSE (emitComp->verbose)
+#endif
+
+#if 0
+#define EMIT_GC_VERBOSE 0
+#else
+#define EMIT_GC_VERBOSE (emitComp->verbose)
+#endif
+
+#if 1
+#define EMIT_INSTLIST_VERBOSE 0
+#else
+#define EMIT_INSTLIST_VERBOSE (emitComp->verbose)
+#endif
+
+/*****************************************************************************/
+
+#ifdef DEBUG
+#define DEBUG_EMIT 1
+#else
+#define DEBUG_EMIT 0
+#endif
+
+#if EMITTER_STATS
+void emitterStats(FILE* fout);
+void emitterStaticStats(FILE* fout); // Static stats about the emitter (data structure offsets, sizes, etc.)
+#endif
+
+void printRegMaskInt(regMaskTP mask);
+
+/*****************************************************************************/
+/* Forward declarations */
+
+class emitLocation;
+class emitter;
+struct insGroup;
+
+typedef void (*emitSplitCallbackType)(void* context, emitLocation* emitLoc);
+
+/*****************************************************************************/
+
+//-----------------------------------------------------------------------------
+
+inline bool needsGC(GCtype gcType)
+{
+ if (gcType == GCT_NONE)
+ {
+ return false;
+ }
+ else
+ {
+ assert(gcType == GCT_GCREF || gcType == GCT_BYREF);
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef DEBUG
+
+inline bool IsValidGCtype(GCtype gcType)
+{
+ return (gcType == GCT_NONE || gcType == GCT_GCREF || gcType == GCT_BYREF);
+}
+
+// Get a string name to represent the GC type
+
+inline const char* GCtypeStr(GCtype gcType)
+{
+ switch (gcType)
+ {
+ case GCT_NONE:
+ return "npt";
+ case GCT_GCREF:
+ return "gcr";
+ case GCT_BYREF:
+ return "byr";
+ default:
+ assert(!"Invalid GCtype");
+ return "err";
+ }
+}
+
+#endif // DEBUG
+
+/*****************************************************************************/
+
+#if DEBUG_EMIT
+#define INTERESTING_JUMP_NUM -1 // set to 0 to see all jump info
+//#define INTERESTING_JUMP_NUM 0
+#endif
+
+/*****************************************************************************
+ *
+ * Represent an emitter location.
+ */
+
+class emitLocation
+{
+public:
+ emitLocation() : ig(nullptr), codePos(0)
+ {
+ }
+
+ emitLocation(insGroup* _ig) : ig(_ig), codePos(0)
+ {
+ }
+
+ emitLocation(void* emitCookie) : ig((insGroup*)emitCookie), codePos(0)
+ {
+ }
+
+ // A constructor for code that needs to call it explicitly.
+ void Init()
+ {
+ this->emitLocation::emitLocation();
+ }
+
+ void CaptureLocation(emitter* emit);
+
+ bool IsCurrentLocation(emitter* emit) const;
+
+ // This function is highly suspect, since it presumes knowledge of the codePos "cookie",
+ // and doesn't look at the 'ig' pointer.
+ bool IsOffsetZero() const
+ {
+ return (codePos == 0);
+ }
+
+ UNATIVE_OFFSET CodeOffset(emitter* emit) const;
+
+ insGroup* GetIG() const
+ {
+ return ig;
+ }
+
+ int GetInsNum() const;
+
+ bool operator!=(const emitLocation& other) const
+ {
+ return (ig != other.ig) || (codePos != other.codePos);
+ }
+
+ bool operator==(const emitLocation& other) const
+ {
+ return !(*this != other);
+ }
+
+ bool Valid() const
+ {
+ // Things we could validate:
+ // 1. the instruction group pointer is non-nullptr.
+ // 2. 'ig' is a legal pointer to an instruction group.
+ // 3. 'codePos' is a legal offset into 'ig'.
+ // Currently, we just do #1.
+ // #2 and #3 should only be done in DEBUG, if they are implemented.
+
+ if (ig == nullptr)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+#ifdef _TARGET_AMD64_
+ UNATIVE_OFFSET GetFuncletPrologOffset(emitter* emit) const;
+#endif // _TARGET_AMD64_
+
+#ifdef DEBUG
+ void Print() const;
+#endif // DEBUG
+
+private:
+ insGroup* ig; // the instruction group
+ unsigned codePos; // the code position within the IG (see emitCurOffset())
+};
+
+/************************************************************************/
+/* The following describes an instruction group */
+/************************************************************************/
+
+DECLARE_TYPED_ENUM(insGroupPlaceholderType, unsigned char)
+{
+ IGPT_PROLOG, // currently unused
+ IGPT_EPILOG,
+#if FEATURE_EH_FUNCLETS
+ IGPT_FUNCLET_PROLOG, IGPT_FUNCLET_EPILOG,
+#endif // FEATURE_EH_FUNCLETS
+}
+END_DECLARE_TYPED_ENUM(insGroupPlaceholderType, unsigned char)
+
+#if defined(_MSC_VER) && defined(_TARGET_ARM_)
+// ARM aligns structures that contain 64-bit ints or doubles on 64-bit boundaries. This causes unwanted
+// padding to be added to the end, so sizeof() is unnecessarily big.
+#pragma pack(push)
+#pragma pack(4)
+#endif // defined(_MSC_VER) && defined(_TARGET_ARM_)
+
+struct insPlaceholderGroupData
+{
+ insGroup* igPhNext;
+ BasicBlock* igPhBB;
+ VARSET_TP igPhInitGCrefVars;
+ regMaskTP igPhInitGCrefRegs;
+ regMaskTP igPhInitByrefRegs;
+ VARSET_TP igPhPrevGCrefVars;
+ regMaskTP igPhPrevGCrefRegs;
+ regMaskTP igPhPrevByrefRegs;
+ insGroupPlaceholderType igPhType;
+}; // end of struct insPlaceholderGroupData
+
+struct insGroup
+{
+ insGroup* igNext;
+
+#ifdef DEBUG
+ insGroup* igSelf; // for consistency checking
+#endif
+
+ UNATIVE_OFFSET igNum; // for ordering (and display) purposes
+ UNATIVE_OFFSET igOffs; // offset of this group within method
+ unsigned int igFuncIdx; // Which function/funclet does this belong to? (Index into Compiler::compFuncInfos array.)
+ unsigned short igFlags; // see IGF_xxx below
+ unsigned short igSize; // # of bytes of code in this group
+
+#define IGF_GC_VARS 0x0001 // new set of live GC ref variables
+#define IGF_BYREF_REGS 0x0002 // new set of live by-ref registers
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+#define IGF_FINALLY_TARGET 0x0004 // this group is the start of a basic block that is returned to after a finally.
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+#define IGF_FUNCLET_PROLOG 0x0008 // this group belongs to a funclet prolog
+#ifdef DEBUG
+#define IGF_FUNCLET_EPILOG 0x0010 // this group belongs to a funclet epilog. Currently, this is only needed for DEBUG.
+#endif
+#define IGF_EPILOG 0x0020 // this group belongs to a main function epilog
+#define IGF_NOGCINTERRUPT 0x0040 // this IG is is a no-interrupt region (prolog, epilog, etc.)
+#define IGF_UPD_ISZ 0x0080 // some instruction sizes updated
+#define IGF_PLACEHOLDER 0x0100 // this is a placeholder group, to be filled in later
+#define IGF_EMIT_ADD 0x0200 // this is a block added by the emitter
+ // because the codegen block was too big. Also used for
+ // placeholder IGs that aren't also labels.
+
+// Mask of IGF_* flags that should be propagated to new blocks when they are created.
+// This allows prologs and epilogs to be any number of IGs, but still be
+// automatically marked properly.
+#if FEATURE_EH_FUNCLETS
+#ifdef DEBUG
+#define IGF_PROPAGATE_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG)
+#else // DEBUG
+#define IGF_PROPAGATE_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG)
+#endif // DEBUG
+#else // FEATURE_EH_FUNCLETS
+#define IGF_PROPAGATE_MASK (IGF_EPILOG)
+#endif // FEATURE_EH_FUNCLETS
+
+ // Try to do better packing based on how large regMaskSmall is (8, 16, or 64 bits).
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#if REGMASK_BITS <= 32
+
+ union {
+ BYTE* igData; // addr of instruction descriptors
+ insPlaceholderGroupData* igPhData; // when igFlags & IGF_PLACEHOLDER
+ };
+
+#if EMIT_TRACK_STACK_DEPTH
+ unsigned igStkLvl; // stack level on entry
+#endif
+ regMaskSmall igGCregs; // set of registers with live GC refs
+ unsigned char igInsCnt; // # of instructions in this group
+
+#else // REGMASK_BITS
+
+ regMaskSmall igGCregs; // set of registers with live GC refs
+
+ union {
+ BYTE* igData; // addr of instruction descriptors
+ insPlaceholderGroupData* igPhData; // when igFlags & IGF_PLACEHOLDER
+ };
+
+#if EMIT_TRACK_STACK_DEPTH
+ unsigned igStkLvl; // stack level on entry
+#endif
+
+ unsigned char igInsCnt; // # of instructions in this group
+
+#endif // REGMASK_BITS
+
+ VARSET_VALRET_TP igGCvars() const
+ {
+ assert(igFlags & IGF_GC_VARS);
+
+ BYTE* ptr = (BYTE*)igData;
+ ptr -= sizeof(VARSET_TP);
+
+ return *(VARSET_TP*)ptr;
+ }
+
+ unsigned igByrefRegs() const
+ {
+ assert(igFlags & IGF_BYREF_REGS);
+
+ BYTE* ptr = (BYTE*)igData;
+
+ if (igFlags & IGF_GC_VARS)
+ {
+ ptr -= sizeof(VARSET_TP);
+ }
+
+ ptr -= sizeof(unsigned);
+
+ return *(unsigned*)ptr;
+ }
+
+}; // end of struct insGroup
+
+// For AMD64 the maximum prolog/epilog size supported on the OS is 256 bytes
+// Since it is incorrect for us to be jumping across funclet prolog/epilogs
+// we will use the following estimate as the maximum placeholder size.
+//
+#define MAX_PLACEHOLDER_IG_SIZE 256
+
+#if defined(_MSC_VER) && defined(_TARGET_ARM_)
+#pragma pack(pop)
+#endif // defined(_MSC_VER) && defined(_TARGET_ARM_)
+
+/*****************************************************************************/
+
+#define DEFINE_ID_OPS
+#include "emitfmts.h"
+#undef DEFINE_ID_OPS
+
+enum LclVarAddrTag
+{
+ LVA_STANDARD_ENCODING = 0,
+ LVA_LARGE_OFFSET = 1,
+ LVA_COMPILER_TEMP = 2,
+ LVA_LARGE_VARNUM = 3
+};
+
+struct emitLclVarAddr
+{
+ // Constructor
+ void initLclVarAddr(int varNum, unsigned offset);
+
+ int lvaVarNum(); // Returns the variable to access. Note that it returns a negative number for compiler spill temps.
+ unsigned lvaOffset(); // returns the offset into the variable to access
+
+ // This struct should be 32 bits in size for the release build.
+ // We have this constraint because this type is used in a union
+ // with several other pointer sized types in the instrDesc struct.
+ //
+protected:
+ unsigned _lvaVarNum : 15; // Usually the lvaVarNum
+ unsigned _lvaExtra : 15; // Usually the lvaOffset
+ unsigned _lvaTag : 2; // tag field to support larger varnums
+};
+
+enum idAddrUnionTag
+{
+ iaut_ALIGNED_POINTER = 0x0,
+ iaut_DATA_OFFSET = 0x1,
+ iaut_INST_COUNT = 0x2,
+ iaut_UNUSED_TAG = 0x3,
+
+ iaut_MASK = 0x3,
+ iaut_SHIFT = 2
+};
+
+class emitter
+{
+ friend class emitLocation;
+ friend class Compiler;
+ friend class CodeGen;
+ friend class CodeGenInterface;
+
+public:
+ /*************************************************************************
+ *
+ * Define the public entry points.
+ */
+
+ // Constructor.
+ emitter()
+ {
+#ifdef DEBUG
+ // There seem to be some cases where this is used without being initialized via CodeGen::inst_set_SV_var().
+ emitVarRefOffs = 0;
+#endif // DEBUG
+#ifdef FEATURE_AVX_SUPPORT
+ SetUseAVX(false);
+#endif // FEATURE_AVX_SUPPORT
+ }
+
+#include "emitpub.h"
+
+protected:
+ /************************************************************************/
+ /* Miscellaneous stuff */
+ /************************************************************************/
+
+ Compiler* emitComp;
+ GCInfo* gcInfo;
+ CodeGen* codeGen;
+
+ typedef GCInfo::varPtrDsc varPtrDsc;
+ typedef GCInfo::regPtrDsc regPtrDsc;
+ typedef GCInfo::CallDsc callDsc;
+
+ void* emitGetMem(size_t sz);
+
+ DECLARE_TYPED_ENUM(opSize, unsigned)
+ {
+ OPSZ1 = 0, OPSZ2 = 1, OPSZ4 = 2, OPSZ8 = 3, OPSZ16 = 4, OPSZ32 = 5, OPSZ_COUNT = 6,
+#ifdef _TARGET_AMD64_
+ OPSZP = OPSZ8,
+#else
+ OPSZP = OPSZ4,
+#endif
+ }
+ END_DECLARE_TYPED_ENUM(opSize, unsigned)
+
+#define OPSIZE_INVALID ((opSize)0xffff)
+
+ static const emitter::opSize emitSizeEncode[];
+ static const emitAttr emitSizeDecode[];
+
+ static emitter::opSize emitEncodeSize(emitAttr size);
+ static emitAttr emitDecodeSize(emitter::opSize ensz);
+
+ // Currently, we only allow one IG for the prolog
+ bool emitIGisInProlog(const insGroup* ig)
+ {
+ return ig == emitPrologIG;
+ }
+
+ bool emitIGisInEpilog(const insGroup* ig)
+ {
+ return (ig != nullptr) && ((ig->igFlags & IGF_EPILOG) != 0);
+ }
+
+#if FEATURE_EH_FUNCLETS
+
+ bool emitIGisInFuncletProlog(const insGroup* ig)
+ {
+ return (ig != nullptr) && ((ig->igFlags & IGF_FUNCLET_PROLOG) != 0);
+ }
+
+#ifdef DEBUG
+ bool emitIGisInFuncletEpilog(const insGroup* ig)
+ {
+ return (ig != nullptr) && ((ig->igFlags & IGF_FUNCLET_EPILOG) != 0);
+ }
+#endif // DEBUG
+#endif // FEATURE_EH_FUNCLETS
+
+ // If "ig" corresponds to the start of a basic block that is the
+ // target of a funclet return, generate GC information for it's start
+ // address "cp", as if it were the return address of a call.
+ void emitGenGCInfoIfFuncletRetTarget(insGroup* ig, BYTE* cp);
+
+ void emitRecomputeIGoffsets();
+
+ /************************************************************************/
+ /* The following describes a single instruction */
+ /************************************************************************/
+
+ DECLARE_TYPED_ENUM(insFormat, unsigned)
+ {
+#define IF_DEF(en, op1, op2) IF_##en,
+#include "emitfmts.h"
+
+ IF_COUNT
+ }
+ END_DECLARE_TYPED_ENUM(insFormat, unsigned)
+
+#define AM_DISP_BITS ((sizeof(unsigned) * 8) - 2 * (REGNUM_BITS + 1) - 2)
+#define AM_DISP_BIG_VAL (-(1 << (AM_DISP_BITS - 1)))
+#define AM_DISP_MIN (-((1 << (AM_DISP_BITS - 1)) - 1))
+#define AM_DISP_MAX (+((1 << (AM_DISP_BITS - 1)) - 1))
+
+ struct emitAddrMode
+ {
+ regNumber amBaseReg : REGNUM_BITS + 1;
+ regNumber amIndxReg : REGNUM_BITS + 1;
+ emitter::opSize amScale : 2;
+ int amDisp : AM_DISP_BITS;
+ };
+
+#if defined(DEBUG) || defined(LATE_DISASM) // LATE_DISASM needs the idMemCookie on calls to display the call target name
+
+ struct instrDesc;
+
+ struct instrDescDebugInfo
+ {
+ unsigned idNum;
+ size_t idSize; // size of the instruction descriptor
+ unsigned idVarRefOffs; // IL offset for LclVar reference
+ size_t idMemCookie; // for display of member names in addr modes
+ void* idClsCookie; // for display of member names in addr modes
+#ifdef TRANSLATE_PDB
+ unsigned int idilStart; // instruction descriptor source information for PDB translation
+#endif
+ bool idFinallyCall; // Branch instruction is a call to finally
+ bool idCatchRet; // Instruction is for a catch 'return'
+ CORINFO_SIG_INFO* idCallSig; // Used to report native call site signatures to the EE
+ };
+
+#endif // defined(DEBUG) || defined(LATE_DISASM)
+
+#ifdef _TARGET_ARM_
+ unsigned insEncodeSetFlags(insFlags sf);
+
+ DECLARE_TYPED_ENUM(insSize, unsigned)
+ {
+ ISZ_16BIT, ISZ_32BIT, ISZ_48BIT // pseudo-instruction for conditional branch with imm24 range,
+ // encoded as IT of condition followed by an unconditional branch
+ }
+ END_DECLARE_TYPED_ENUM(insSize, unsigned)
+
+ unsigned insEncodeShiftOpts(insOpts opt);
+ unsigned insEncodePUW_G0(insOpts opt, int imm);
+ unsigned insEncodePUW_H0(insOpts opt, int imm);
+
+#endif // _TARGET_ARM_
+
+#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND)
+#define HAS_TINY_DESC 1
+#else
+#define HAS_TINY_DESC 0
+#endif
+
+ struct instrDescCns;
+
+ struct instrDesc
+ {
+ private:
+#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ // The assembly instruction
+ instruction _idIns : 9;
+#else // !defined(_TARGET_XARCH_) || defined(LEGACY_BACKEND)
+ // The assembly instruction
+ instruction _idIns : 8;
+#endif // !defined(_TARGET_XARCH_) || defined(LEGACY_BACKEND)
+ // The format for the instruction
+ insFormat _idInsFmt : 8;
+
+ public:
+ instruction idIns() const
+ {
+ return _idIns;
+ }
+ void idIns(instruction ins)
+ {
+ _idIns = ins;
+ assert(_idIns == ins);
+ }
+
+ insFormat idInsFmt() const
+ {
+ return _idInsFmt;
+ }
+ void idInsFmt(insFormat insFmt)
+ {
+#if defined(_TARGET_ARM64_)
+ noway_assert(insFmt != IF_NONE); // Only the x86 emitter uses IF_NONE, it is invalid for ARM64 (and ARM32)
+#endif
+ _idInsFmt = insFmt;
+ assert(_idInsFmt == insFmt);
+ }
+
+ /*
+ The idReg1 and idReg2 fields hold the first and second register
+ operand(s), whenever these are present. Note that the size of
+ these fields ranges from 3 to 6 bits, and care needs to be taken
+ to make sure all of these fields stay reasonably packed.
+ */
+
+ void idSetRelocFlags(emitAttr attr)
+ {
+ _idCnsReloc = (EA_IS_CNS_RELOC(attr) ? 1 : 0);
+ _idDspReloc = (EA_IS_DSP_RELOC(attr) ? 1 : 0);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Space taken up to here:
+ // x86: 16 bits
+ // amd64: 17 bits
+ // arm: 16 bits
+ // arm64: 16 bits
+
+ private:
+#ifdef _TARGET_XARCH_
+ unsigned _idCodeSize : 4; // size of instruction in bytes
+#endif
+
+#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ opSize _idOpSize : 3; // operand size: 0=1 , 1=2 , 2=4 , 3=8, 4=16, 5=32
+ // At this point we have fully consumed first DWORD so that next field
+ // doesn't cross a byte boundary.
+#elif defined(_TARGET_ARM64_)
+// Moved the definition of '_idOpSize' later so that we don't cross a 32-bit boundary when laying out bitfields
+#else // ARM or x86-LEGACY_BACKEND
+ opSize _idOpSize : 2; // operand size: 0=1 , 1=2 , 2=4 , 3=8
+#endif // ARM or x86-LEGACY_BACKEND
+
+ // On Amd64, this is where the second DWORD begins
+ // On System V a call could return a struct in 2 registers. The instrDescCGCA struct below has member that
+ // stores the GC-ness of the second register.
+ // It is added to the instrDescCGCA and not here (the base struct) since it is not needed by all the
+ // instructions. This struct (instrDesc) is very carefully kept to be no more than 128 bytes. There is no more
+ // space to add members for keeping GC-ness of the second return registers. It will also bloat the base struct
+ // unnecessarily since the GC-ness of the second register is only needed for call instructions.
+ // The instrDescCGCA struct's member keeping the GC-ness of the first return register is _idcSecondRetRegGCType.
+ GCtype _idGCref : 2; // GCref operand? (value is a "GCtype")
+
+ // Note that we use the _idReg1 and _idReg2 fields to hold
+ // the live gcrefReg mask for the call instructions on x86/x64
+ //
+ regNumber _idReg1 : REGNUM_BITS; // register num
+
+ regNumber _idReg2 : REGNUM_BITS;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Space taken up to here:
+ // x86: 30 bits
+ // amd64: 38 bits
+ // arm: 32 bits
+ // arm64: 30 bits
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if HAS_TINY_DESC
+ //
+ // For x86 use last two bits to differentiate if we are tiny or small
+ //
+ unsigned _idTinyDsc : 1; // is this a "tiny" descriptor?
+ unsigned _idSmallDsc : 1; // is this a "small" descriptor?
+
+#else // !HAS_TINY_DESC
+
+ //
+ // On x86/arm platforms we have used 32 bits so far (4 bytes)
+ // On amd64 we have used 38 bits so far (4 bytes + 6 bits)
+ //
+
+ //
+ // For amd64 we just can't fit anything useful into a single DWORD
+ // So we eliminate the notion of 'tiny', and have small (2 DWORDS)
+ // or not small (which is bigger, just like x86)
+ //
+
+ unsigned _idSmallDsc : 1; // is this a "small" descriptor?
+ unsigned _idLargeCns : 1; // does a large constant follow?
+ unsigned _idLargeDsp : 1; // does a large displacement follow?
+ unsigned _idLargeCall : 1; // large call descriptor used
+
+ unsigned _idBound : 1; // jump target / frame offset bound
+ unsigned _idCallRegPtr : 1; // IL indirect calls: addr in reg
+ unsigned _idCallAddr : 1; // IL indirect calls: can make a direct call to iiaAddr
+ unsigned _idNoGC : 1; // Some helpers don't get recorded in GC tables
+
+#ifdef _TARGET_ARM64_
+ opSize _idOpSize : 3; // operand size: 0=1 , 1=2 , 2=4 , 3=8, 4=16
+ insOpts _idInsOpt : 6; // options for instructions
+ unsigned _idLclVar : 1; // access a local on stack
+#endif
+
+#ifdef _TARGET_ARM_
+ insSize _idInsSize : 2; // size of instruction: 16, 32 or 48 bits
+ insFlags _idInsFlags : 1; // will this instruction set the flags
+ unsigned _idLclVar : 1; // access a local on stack
+ unsigned _idLclFPBase : 1; // access a local on stack - SP based offset
+ insOpts _idInsOpt : 3; // options for Load/Store instructions
+
+// For arm we have used 16 bits
+#define ID_EXTRA_BITFIELD_BITS (16)
+
+#elif defined(_TARGET_ARM64_)
+// For Arm64, we have used 15 bits from the second DWORD.
+#define ID_EXTRA_BITFIELD_BITS (16)
+#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+// For xarch !LEGACY_BACKEND, we have used 14 bits from the second DWORD.
+#define ID_EXTRA_BITFIELD_BITS (14)
+#elif defined(_TARGET_X86_)
+// For x86, we have used 6 bits from the second DWORD.
+#define ID_EXTRA_BITFIELD_BITS (6)
+#else
+#error Unsupported or unset target architecture
+#endif
+
+ ////////////////////////////////////////////////////////////////////////
+ // Space taken up to here:
+ // x86: 38 bits // if HAS_TINY_DESC is not defined (which it is)
+ // amd64: 46 bits
+ // arm: 48 bits
+ // arm64: 48 bits
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef RELOC_SUPPORT
+
+ unsigned _idCnsReloc : 1; // LargeCns is an RVA and needs reloc tag
+ unsigned _idDspReloc : 1; // LargeDsp is an RVA and needs reloc tag
+
+#define ID_EXTRA_RELOC_BITS (2)
+
+#else // RELOC_SUPPORT
+
+#define ID_EXTRA_RELOC_BITS (0)
+
+#endif // RELOC_SUPPORT
+
+ ////////////////////////////////////////////////////////////////////////
+ // Space taken up to here (assuming RELOC_SUPPORT):
+ // x86: 40 bits
+ // amd64: 48 bits
+ // arm: 50 bits
+ // arm64: 50 bits
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#define ID_EXTRA_BITS (ID_EXTRA_RELOC_BITS + ID_EXTRA_BITFIELD_BITS)
+
+/* Use whatever bits are left over for small constants */
+
+#define ID_BIT_SMALL_CNS (32 - ID_EXTRA_BITS)
+#define ID_MIN_SMALL_CNS 0
+#define ID_MAX_SMALL_CNS (int)((1 << ID_BIT_SMALL_CNS) - 1U)
+
+ ////////////////////////////////////////////////////////////////////////
+ // Small constant size (assuming RELOC_SUPPORT):
+ // x86: 24 bits
+ // amd64: 16 bits
+ // arm: 14 bits
+ // arm64: 14 bits
+
+ unsigned _idSmallCns : ID_BIT_SMALL_CNS;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Space taken up to here (with RELOC_SUPPORT): 64 bits, all architectures, by design.
+ ////////////////////////////////////////////////////////////////////////
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#endif // !HAS_TINY_DESC
+
+#if defined(DEBUG) || defined(LATE_DISASM)
+
+ instrDescDebugInfo* _idDebugOnlyInfo;
+
+ public:
+ instrDescDebugInfo* idDebugOnlyInfo() const
+ {
+ return _idDebugOnlyInfo;
+ }
+ void idDebugOnlyInfo(instrDescDebugInfo* info)
+ {
+ _idDebugOnlyInfo = info;
+ }
+
+ private:
+#endif // defined(DEBUG) || defined(LATE_DISASM)
+
+ //
+ // This is the end of the smallest instrDesc we can allocate for all
+ // platforms.
+ // Non-DEBUG sizes:
+ // x86: 32 bits, and it is called the 'tiny' descriptor.
+ // amd64/arm/arm64: 64 bits, and it is called the 'small' descriptor.
+ // DEBUG sizes (includes one pointer):
+ // x86: 2 DWORDs, 64 bits
+ // amd64: 4 DWORDs, 128 bits
+ // arm: 3 DWORDs, 96 bits
+ // arm64: 4 DWORDs, 128 bits
+ // There should no padding or alignment issues on any platform or
+ // configuration (including DEBUG which has 1 extra pointer).
+ //
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if HAS_TINY_DESC
+
+ unsigned _idLargeCns : 1; // does a large constant follow?
+ unsigned _idLargeDsp : 1; // does a large displacement follow?
+ unsigned _idLargeCall : 1; // large call descriptor used
+ unsigned _idBound : 1; // jump target / frame offset bound
+
+ unsigned _idCallRegPtr : 1; // IL indirect calls: addr in reg
+ unsigned _idCallAddr : 1; // IL indirect calls: can make a direct call to iiaAddr
+ unsigned _idNoGC : 1; // Some helpers don't get recorded in GC tables
+
+#define ID_EXTRA_BITFIELD_BITS (7)
+
+//
+// For x86, we are using 7 bits from the second DWORD for bitfields.
+//
+
+#ifdef RELOC_SUPPORT
+
+ unsigned _idCnsReloc : 1; // LargeCns is an RVA and needs reloc tag
+ unsigned _idDspReloc : 1; // LargeDsp is an RVA and needs reloc tag
+
+#define ID_EXTRA_RELOC_BITS (2)
+
+#else // RELOC_SUPPORT
+
+#define ID_EXTRA_RELOC_BITS (0)
+
+#endif // RELOC_SUPPORT
+
+#define ID_EXTRA_REG_BITS (0)
+
+#define ID_EXTRA_BITS (ID_EXTRA_BITFIELD_BITS + ID_EXTRA_RELOC_BITS + ID_EXTRA_REG_BITS)
+
+/* Use whatever bits are left over for small constants */
+
+#define ID_BIT_SMALL_CNS (32 - ID_EXTRA_BITS)
+#define ID_MIN_SMALL_CNS 0
+#define ID_MAX_SMALL_CNS (int)((1 << ID_BIT_SMALL_CNS) - 1U)
+
+ // For x86 (assuming RELOC_SUPPORT) we have 23 bits remaining for the
+ // small constant in this extra DWORD.
+
+ unsigned _idSmallCns : ID_BIT_SMALL_CNS;
+
+#endif // HAS_TINY_DESC
+
+//
+// This is the end of the 'small' instrDesc which is the same on all
+// platforms (except 64-bit DEBUG which is a little bigger).
+// Non-DEBUG sizes:
+// x86/amd64/arm/arm64: 64 bits
+// DEBUG sizes (includes one pointer):
+// x86: 2 DWORDs, 64 bits
+// amd64: 4 DWORDs, 128 bits
+// arm: 3 DWORDs, 96 bits
+// arm64: 4 DWORDs, 128 bits
+// There should no padding or alignment issues on any platform or
+// configuration (including DEBUG which has 1 extra pointer).
+//
+
+/*
+ If you add lots more fields that need to be cleared (such
+ as various flags), you might need to update the body of
+ emitter::emitAllocInstr() to clear them.
+ */
+
+#if defined(DEBUG) || defined(LATE_DISASM)
+#define TINY_IDSC_DEBUG_EXTRA (sizeof(void*))
+#else
+#define TINY_IDSC_DEBUG_EXTRA (0)
+#endif
+
+#if HAS_TINY_DESC
+#define TINY_IDSC_SIZE (4 + TINY_IDSC_DEBUG_EXTRA)
+#define SMALL_IDSC_SIZE (8 + TINY_IDSC_DEBUG_EXTRA)
+#else
+#define TINY_IDSC_SIZE (8 + TINY_IDSC_DEBUG_EXTRA)
+#define SMALL_IDSC_SIZE TINY_IDSC_SIZE
+#endif
+
+ void checkSizes();
+
+ union idAddrUnion {
+ // TODO-Cleanup: We should really add a DEBUG-only tag to this union so we can add asserts
+ // about reading what we think is here, to avoid unexpected corruption issues.
+
+ emitLclVarAddr iiaLclVar;
+ BasicBlock* iiaBBlabel;
+ insGroup* iiaIGlabel;
+ BYTE* iiaAddr;
+ emitAddrMode iiaAddrMode;
+
+ CORINFO_FIELD_HANDLE iiaFieldHnd; // iiaFieldHandle is also used to encode
+ // an offset into the JIT data constant area
+ bool iiaIsJitDataOffset() const;
+ int iiaGetJitDataOffset() const;
+
+#ifdef _TARGET_ARMARCH_
+
+ // iiaEncodedInstrCount and its accessor functions are used to specify an instruction
+ // count for jumps, instead of using a label and multiple blocks. This is used in the
+ // prolog as well as for IF_LARGEJMP pseudo-branch instructions.
+ int iiaEncodedInstrCount;
+
+ bool iiaHasInstrCount() const
+ {
+ return (iiaEncodedInstrCount & iaut_MASK) == iaut_INST_COUNT;
+ }
+ int iiaGetInstrCount() const
+ {
+ assert(iiaHasInstrCount());
+ return (iiaEncodedInstrCount >> iaut_SHIFT);
+ }
+ void iiaSetInstrCount(int count)
+ {
+ assert(abs(count) < 10);
+ iiaEncodedInstrCount = (count << iaut_SHIFT) | iaut_INST_COUNT;
+ }
+
+ struct
+ {
+ regNumber _idReg3 : REGNUM_BITS;
+ regNumber _idReg4 : REGNUM_BITS;
+#ifdef _TARGET_ARM64_
+ unsigned _idReg3Scaled : 1; // Reg3 is scaled by idOpSize bits
+#endif
+ };
+#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ struct
+ {
+ regNumber _idReg3 : REGNUM_BITS;
+ };
+#endif // defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+
+ } _idAddrUnion;
+
+ /* Trivial wrappers to return properly typed enums */
+ public:
+#if HAS_TINY_DESC
+
+ bool idIsTiny() const
+ {
+ return (_idTinyDsc != 0);
+ }
+ void idSetIsTiny()
+ {
+ _idTinyDsc = 1;
+ }
+
+#else
+
+ bool idIsTiny() const
+ {
+ return false;
+ }
+ void idSetIsTiny()
+ {
+ _idSmallDsc = 1;
+ }
+
+#endif // HAS_TINY_DESC
+
+ bool idIsSmallDsc() const
+ {
+ return (_idSmallDsc != 0);
+ }
+ void idSetIsSmallDsc()
+ {
+ _idSmallDsc = 1;
+ }
+
+#if defined(_TARGET_XARCH_)
+
+ unsigned idCodeSize() const
+ {
+ return _idCodeSize;
+ }
+ void idCodeSize(unsigned sz)
+ {
+ _idCodeSize = sz;
+ assert(sz == _idCodeSize);
+ }
+
+#elif defined(_TARGET_ARM64_)
+ unsigned idCodeSize() const
+ {
+ int size = 4;
+ switch (idInsFmt())
+ {
+ case IF_LARGEADR:
+ // adrp + add
+ case IF_LARGEJMP:
+ // b<cond> + b<uncond>
+ size = 8;
+ break;
+ case IF_LARGELDC:
+ if (isVectorRegister(idReg1()))
+ {
+ // adrp + ldr + fmov
+ size = 12;
+ }
+ else
+ {
+ // adrp + ldr
+ size = 8;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return size;
+ }
+
+#elif defined(_TARGET_ARM_)
+
+ bool idInstrIsT1() const
+ {
+ return (_idInsSize == ISZ_16BIT);
+ }
+ unsigned idCodeSize() const
+ {
+ unsigned result = (_idInsSize == ISZ_16BIT) ? 2 : (_idInsSize == ISZ_32BIT) ? 4 : 6;
+ return result;
+ }
+ insSize idInsSize() const
+ {
+ return _idInsSize;
+ }
+ void idInsSize(insSize isz)
+ {
+ _idInsSize = isz;
+ assert(isz == _idInsSize);
+ }
+ insFlags idInsFlags() const
+ {
+ return _idInsFlags;
+ }
+ void idInsFlags(insFlags sf)
+ {
+ _idInsFlags = sf;
+ assert(sf == _idInsFlags);
+ }
+#endif // _TARGET_ARM_
+
+ emitAttr idOpSize()
+ {
+ return emitDecodeSize(_idOpSize);
+ }
+ void idOpSize(emitAttr opsz)
+ {
+ _idOpSize = emitEncodeSize(opsz);
+ }
+
+ GCtype idGCref() const
+ {
+ return (GCtype)_idGCref;
+ }
+ void idGCref(GCtype gctype)
+ {
+ _idGCref = gctype;
+ }
+
+ regNumber idReg1() const
+ {
+ return _idReg1;
+ }
+ void idReg1(regNumber reg)
+ {
+ _idReg1 = reg;
+ assert(reg == _idReg1);
+ }
+
+ regNumber idReg2() const
+ {
+ return _idReg2;
+ }
+ void idReg2(regNumber reg)
+ {
+ _idReg2 = reg;
+ assert(reg == _idReg2);
+ }
+
+#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+ regNumber idReg3() const
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ return idAddr()->_idReg3;
+ }
+ void idReg3(regNumber reg)
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ idAddr()->_idReg3 = reg;
+ assert(reg == idAddr()->_idReg3);
+ }
+#endif // defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
+#ifdef _TARGET_ARMARCH_
+ insOpts idInsOpt() const
+ {
+ return (insOpts)_idInsOpt;
+ }
+ void idInsOpt(insOpts opt)
+ {
+ _idInsOpt = opt;
+ assert(opt == _idInsOpt);
+ }
+
+ regNumber idReg3() const
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ return idAddr()->_idReg3;
+ }
+ void idReg3(regNumber reg)
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ idAddr()->_idReg3 = reg;
+ assert(reg == idAddr()->_idReg3);
+ }
+ regNumber idReg4() const
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ return idAddr()->_idReg4;
+ }
+ void idReg4(regNumber reg)
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ idAddr()->_idReg4 = reg;
+ assert(reg == idAddr()->_idReg4);
+ }
+#ifdef _TARGET_ARM64_
+ bool idReg3Scaled() const
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ return (idAddr()->_idReg3Scaled == 1);
+ }
+ void idReg3Scaled(bool val)
+ {
+ assert(!idIsTiny());
+ assert(!idIsSmallDsc());
+ idAddr()->_idReg3Scaled = val ? 1 : 0;
+ }
+#endif // _TARGET_ARM64_
+
+#endif // _TARGET_ARMARCH_
+
+ inline static bool fitsInSmallCns(ssize_t val)
+ {
+ return ((val >= ID_MIN_SMALL_CNS) && (val <= ID_MAX_SMALL_CNS));
+ }
+
+ bool idIsLargeCns() const
+ {
+ assert(!idIsTiny());
+ return _idLargeCns != 0;
+ }
+ void idSetIsLargeCns()
+ {
+ assert(!idIsTiny());
+ _idLargeCns = 1;
+ }
+
+ bool idIsLargeDsp() const
+ {
+ assert(!idIsTiny());
+ return _idLargeDsp != 0;
+ }
+ void idSetIsLargeDsp()
+ {
+ assert(!idIsTiny());
+ _idLargeDsp = 1;
+ }
+ void idSetIsSmallDsp()
+ {
+ assert(!idIsTiny());
+ _idLargeDsp = 0;
+ }
+
+ bool idIsLargeCall() const
+ {
+ assert(!idIsTiny());
+ return _idLargeCall != 0;
+ }
+ void idSetIsLargeCall()
+ {
+ assert(!idIsTiny());
+ _idLargeCall = 1;
+ }
+
+ bool idIsBound() const
+ {
+ assert(!idIsTiny());
+ return _idBound != 0;
+ }
+ void idSetIsBound()
+ {
+ assert(!idIsTiny());
+ _idBound = 1;
+ }
+
+ bool idIsCallRegPtr() const
+ {
+ assert(!idIsTiny());
+ return _idCallRegPtr != 0;
+ }
+ void idSetIsCallRegPtr()
+ {
+ assert(!idIsTiny());
+ _idCallRegPtr = 1;
+ }
+
+ bool idIsCallAddr() const
+ {
+ assert(!idIsTiny());
+ return _idCallAddr != 0;
+ }
+ void idSetIsCallAddr()
+ {
+ assert(!idIsTiny());
+ _idCallAddr = 1;
+ }
+
+ // Only call instructions that call helper functions may be marked as "IsNoGC", indicating
+ // that a thread executing such a call cannot be stopped for GC. Thus, in partially-interruptible
+ // code, it is not necessary to generate GC info for a call so labeled.
+ bool idIsNoGC() const
+ {
+ assert(!idIsTiny());
+ return _idNoGC != 0;
+ }
+ void idSetIsNoGC(bool val)
+ {
+ assert(!idIsTiny());
+ _idNoGC = val;
+ }
+
+#ifdef _TARGET_ARMARCH_
+ bool idIsLclVar() const
+ {
+ return !idIsTiny() && _idLclVar != 0;
+ }
+ void idSetIsLclVar()
+ {
+ assert(!idIsTiny());
+ _idLclVar = 1;
+ }
+#endif // _TARGET_ARMARCH_
+
+#if defined(_TARGET_ARM_)
+ bool idIsLclFPBase() const
+ {
+ return !idIsTiny() && _idLclFPBase != 0;
+ }
+ void idSetIsLclFPBase()
+ {
+ assert(!idIsTiny());
+ _idLclFPBase = 1;
+ }
+#endif // defined(_TARGET_ARM_)
+
+#ifdef RELOC_SUPPORT
+
+ bool idIsCnsReloc() const
+ {
+ assert(!idIsTiny());
+ return _idCnsReloc != 0;
+ }
+ void idSetIsCnsReloc()
+ {
+ assert(!idIsTiny());
+ _idCnsReloc = 1;
+ }
+
+ bool idIsDspReloc() const
+ {
+ assert(!idIsTiny());
+ return _idDspReloc != 0;
+ }
+ void idSetIsDspReloc(bool val = true)
+ {
+ assert(!idIsTiny());
+ _idDspReloc = val;
+ }
+ bool idIsReloc()
+ {
+ return idIsDspReloc() || idIsCnsReloc();
+ }
+
+#endif
+
+ unsigned idSmallCns() const
+ {
+ assert(!idIsTiny());
+ return _idSmallCns;
+ }
+ void idSmallCns(size_t value)
+ {
+ assert(!idIsTiny());
+ assert(fitsInSmallCns(value));
+ _idSmallCns = value;
+ }
+
+ inline const idAddrUnion* idAddr() const
+ {
+ assert(!idIsSmallDsc() && !idIsTiny());
+ return &this->_idAddrUnion;
+ }
+
+ inline idAddrUnion* idAddr()
+ {
+ assert(!idIsSmallDsc() && !idIsTiny());
+ return &this->_idAddrUnion;
+ }
+ }; // End of struct instrDesc
+
+ void dispIns(instrDesc* id);
+
+ void appendToCurIG(instrDesc* id);
+
+ /********************************************************************************************/
+
+ struct instrDescJmp : instrDesc
+ {
+ instrDescJmp* idjNext; // next jump in the group/method
+ insGroup* idjIG; // containing group
+
+ union {
+ BYTE* idjAddr; // address of jump ins (for patching)
+ } idjTemp;
+
+ unsigned idjOffs : 30; // Before jump emission, this is the byte offset within IG of the jump instruction.
+ // After emission, for forward jumps, this is the target offset -- in bytes from the
+ // beginning of the function -- of the target instruction of the jump, used to
+ // determine if this jump needs to be patched.
+ unsigned idjShort : 1; // is the jump known to be a short one?
+ unsigned idjKeepLong : 1; // should the jump be kept long? (used for
+ // hot to cold and cold to hot jumps)
+ };
+
+#if !defined(_TARGET_ARM64_) // This shouldn't be needed for ARM32, either, but I don't want to touch the ARM32 JIT.
+ struct instrDescLbl : instrDescJmp
+ {
+ emitLclVarAddr dstLclVar;
+ };
+#endif // !_TARGET_ARM64_
+
+ struct instrDescCns : instrDesc // large const
+ {
+ ssize_t idcCnsVal;
+ };
+
+ struct instrDescDsp : instrDesc // large displacement
+ {
+ ssize_t iddDspVal;
+ };
+
+ struct instrDescCnsDsp : instrDesc // large cons + disp
+ {
+ ssize_t iddcCnsVal;
+ int iddcDspVal;
+ };
+
+ struct instrDescAmd : instrDesc // large addrmode disp
+ {
+ ssize_t idaAmdVal;
+ };
+
+ struct instrDescCnsAmd : instrDesc // large cons + addrmode disp
+ {
+ ssize_t idacCnsVal;
+ ssize_t idacAmdVal;
+ };
+
+ struct instrDescCGCA : instrDesc // call with ...
+ {
+ VARSET_TP idcGCvars; // ... updated GC vars or
+ ssize_t idcDisp; // ... big addrmode disp
+ regMaskTP idcGcrefRegs; // ... gcref registers
+ regMaskTP idcByrefRegs; // ... byref registers
+ unsigned idcArgCnt; // ... lots of args or (<0 ==> caller pops args)
+
+#if MULTIREG_HAS_SECOND_GC_RET
+ // This method handle the GC-ness of the second register in a 2 register returned struct on System V.
+ GCtype idSecondGCref() const
+ {
+ return (GCtype)_idcSecondRetRegGCType;
+ }
+ void idSecondGCref(GCtype gctype)
+ {
+ _idcSecondRetRegGCType = gctype;
+ }
+
+ private:
+ // This member stores the GC-ness of the second register in a 2 register returned struct on System V.
+ // It is added to the call struct since it is not needed by the base instrDesc struct, which keeps GC-ness
+ // of the first register for the instCall nodes.
+ // The base instrDesc is very carefully kept to be no more than 128 bytes. There is no more space to add members
+ // for keeping GC-ness of the second return registers. It will also bloat the base struct unnecessarily
+ // since the GC-ness of the second register is only needed for call instructions.
+ // The base struct's member keeping the GC-ness of the first return register is _idGCref.
+ GCtype _idcSecondRetRegGCType : 2; // ... GC type for the second return register.
+#endif // MULTIREG_HAS_SECOND_GC_RET
+ };
+
+ struct instrDescArmFP : instrDesc
+ {
+ regNumber r1;
+ regNumber r2;
+ regNumber r3;
+ };
+
+ insUpdateModes emitInsUpdateMode(instruction ins);
+ insFormat emitInsModeFormat(instruction ins, insFormat base);
+
+ static const BYTE emitInsModeFmtTab[];
+#ifdef DEBUG
+ static const unsigned emitInsModeFmtCnt;
+#endif
+
+ size_t emitGetInstrDescSize(const instrDesc* id);
+ size_t emitGetInstrDescSizeSC(const instrDesc* id);
+
+ ssize_t emitGetInsCns(instrDesc* id);
+ ssize_t emitGetInsDsp(instrDesc* id);
+ ssize_t emitGetInsAmd(instrDesc* id);
+ ssize_t emitGetInsCnsDsp(instrDesc* id, ssize_t* dspPtr);
+ ssize_t emitGetInsSC(instrDesc* id);
+ ssize_t emitGetInsCIdisp(instrDesc* id);
+ unsigned emitGetInsCIargs(instrDesc* id);
+
+ // Return the argument count for a direct call "id".
+ int emitGetInsCDinfo(instrDesc* id);
+
+ unsigned emitInsCount;
+
+/************************************************************************/
+/* A few routines used for debug display purposes */
+/************************************************************************/
+
+#if defined(DEBUG) || EMITTER_STATS
+
+ static const char* emitIfName(unsigned f);
+
+#endif // defined(DEBUG) || EMITTER_STATS
+
+#ifdef DEBUG
+
+ unsigned emitVarRefOffs;
+
+ const char* emitRegName(regNumber reg, emitAttr size = EA_PTRSIZE, bool varName = true);
+ const char* emitFloatRegName(regNumber reg, emitAttr size = EA_PTRSIZE, bool varName = true);
+
+ const char* emitFldName(CORINFO_FIELD_HANDLE fieldVal);
+ const char* emitFncName(CORINFO_METHOD_HANDLE callVal);
+
+ void emitDispIGflags(unsigned flags);
+ void emitDispIG(insGroup* ig, insGroup* igPrev = nullptr, bool verbose = false);
+ void emitDispIGlist(bool verbose = false);
+ void emitDispGCinfo();
+ void emitDispClsVar(CORINFO_FIELD_HANDLE fldHnd, ssize_t offs, bool reloc = false);
+ void emitDispFrameRef(int varx, int disp, int offs, bool asmfm);
+ void emitDispInsOffs(unsigned offs, bool doffs);
+ void emitDispInsHex(BYTE* code, size_t sz);
+
+#else // !DEBUG
+#define emitVarRefOffs 0
+#endif // !DEBUG
+
+ /************************************************************************/
+ /* Method prolog and epilog */
+ /************************************************************************/
+
+ unsigned emitPrologEndPos;
+
+ unsigned emitEpilogCnt;
+ UNATIVE_OFFSET emitEpilogSize;
+
+#ifdef _TARGET_XARCH_
+
+ void emitStartExitSeq(); // Mark the start of the "return" sequence
+ emitLocation emitExitSeqBegLoc;
+ UNATIVE_OFFSET emitExitSeqSize; // minimum size of any return sequence - the 'ret' after the epilog
+
+#endif // _TARGET_XARCH_
+
+ insGroup* emitPlaceholderList; // per method placeholder list - head
+ insGroup* emitPlaceholderLast; // per method placeholder list - tail
+
+#ifdef JIT32_GCENCODER
+
+ // The x86 GC encoder needs to iterate over a list of epilogs to generate a table of
+ // epilog offsets. Epilogs always start at the beginning of an IG, so save the first
+ // IG of the epilog, and use it to find the epilog offset at the end of code generation.
+ struct EpilogList
+ {
+ EpilogList* elNext;
+ insGroup* elIG;
+ };
+
+ EpilogList* emitEpilogList; // per method epilog list - head
+ EpilogList* emitEpilogLast; // per method epilog list - tail
+
+public:
+ bool emitHasEpilogEnd();
+
+ size_t emitGenEpilogLst(size_t (*fp)(void*, unsigned), void* cp);
+
+#endif // JIT32_GCENCODER
+
+ void emitBegPrologEpilog(insGroup* igPh);
+ void emitEndPrologEpilog();
+
+ emitLocation emitEpilogBegLoc;
+
+ void emitBegFnEpilog(insGroup* igPh);
+ void emitEndFnEpilog();
+
+#if FEATURE_EH_FUNCLETS
+
+ void emitBegFuncletProlog(insGroup* igPh);
+ void emitEndFuncletProlog();
+
+ void emitBegFuncletEpilog(insGroup* igPh);
+ void emitEndFuncletEpilog();
+
+#endif // FEATURE_EH_FUNCLETS
+
+/************************************************************************/
+/* Members and methods used in PDB translation */
+/************************************************************************/
+
+#ifdef TRANSLATE_PDB
+
+ inline void SetIDSource(instrDesc* pID);
+ void MapCode(int ilOffset, BYTE* imgDest);
+ void MapFunc(int imgOff,
+ int procLen,
+ int dbgStart,
+ int dbgEnd,
+ short frameReg,
+ int stkAdjust,
+ int lvaCount,
+ OptJit::LclVarDsc* lvaTable,
+ bool framePtr);
+
+private:
+ int emitInstrDescILBase; // code offset of IL that produced this instruction desctriptor
+ int emitInstrDescILBase; // code offset of IL that produced this instruction desctriptor
+ static AddrMap* emitPDBOffsetTable; // translation table for mapping IL addresses to native addresses
+ static LocalMap* emitPDBLocalTable; // local symbol translation table
+ static bool emitIsPDBEnabled; // flag to disable PDB translation code when a PDB is not found
+ static BYTE* emitILBaseOfCode; // start of IL .text section
+ static BYTE* emitILMethodBase; // beginning of IL method (start of header)
+ static BYTE* emitILMethodStart; // beginning of IL method code (right after the header)
+ static BYTE* emitImgBaseOfCode; // start of the image .text section
+
+#endif
+
+ /************************************************************************/
+ /* Methods to record a code position and later convert to offset */
+ /************************************************************************/
+
+ unsigned emitFindInsNum(insGroup* ig, instrDesc* id);
+ UNATIVE_OFFSET emitFindOffset(insGroup* ig, unsigned insNum);
+
+/************************************************************************/
+/* Members and methods used to issue (encode) instructions. */
+/************************************************************************/
+
+#ifdef DEBUG
+ // If we have started issuing instructions from the list of instrDesc, this is set
+ bool emitIssuing;
+#endif
+
+ BYTE* emitCodeBlock; // Hot code block
+ BYTE* emitColdCodeBlock; // Cold code block
+ BYTE* emitConsBlock; // Read-only (constant) data block
+
+ UNATIVE_OFFSET emitTotalHotCodeSize;
+ UNATIVE_OFFSET emitTotalColdCodeSize;
+
+ UNATIVE_OFFSET emitCurCodeOffs(BYTE* dst)
+ {
+ size_t distance;
+ if ((dst >= emitCodeBlock) && (dst <= (emitCodeBlock + emitTotalHotCodeSize)))
+ {
+ distance = (dst - emitCodeBlock);
+ }
+ else
+ {
+ assert(emitFirstColdIG);
+ assert(emitColdCodeBlock);
+ assert((dst >= emitColdCodeBlock) && (dst <= (emitColdCodeBlock + emitTotalColdCodeSize)));
+
+ distance = (dst - emitColdCodeBlock + emitTotalHotCodeSize);
+ }
+ noway_assert((UNATIVE_OFFSET)distance == distance);
+ return (UNATIVE_OFFSET)distance;
+ }
+
+ BYTE* emitOffsetToPtr(UNATIVE_OFFSET offset)
+ {
+ if (offset < emitTotalHotCodeSize)
+ {
+ return emitCodeBlock + offset;
+ }
+ else
+ {
+ assert(offset < (emitTotalHotCodeSize + emitTotalColdCodeSize));
+
+ return emitColdCodeBlock + (offset - emitTotalHotCodeSize);
+ }
+ }
+
+ BYTE* emitDataOffsetToPtr(UNATIVE_OFFSET offset)
+ {
+ assert(offset < emitDataSize());
+ return emitConsBlock + offset;
+ }
+
+ bool emitJumpCrossHotColdBoundary(size_t srcOffset, size_t dstOffset)
+ {
+ if (emitTotalColdCodeSize == 0)
+ {
+ return false;
+ }
+
+ assert(srcOffset < (emitTotalHotCodeSize + emitTotalColdCodeSize));
+ assert(dstOffset < (emitTotalHotCodeSize + emitTotalColdCodeSize));
+
+ return ((srcOffset < emitTotalHotCodeSize) != (dstOffset < emitTotalHotCodeSize));
+ }
+
+ unsigned char emitOutputByte(BYTE* dst, ssize_t val);
+ unsigned char emitOutputWord(BYTE* dst, ssize_t val);
+ unsigned char emitOutputLong(BYTE* dst, ssize_t val);
+ unsigned char emitOutputSizeT(BYTE* dst, ssize_t val);
+
+ size_t emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp);
+ size_t emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp);
+
+ bool emitHasFramePtr;
+
+#ifdef PSEUDORANDOM_NOP_INSERTION
+ bool emitInInstrumentation;
+#endif // PSEUDORANDOM_NOP_INSERTION
+
+ unsigned emitMaxTmpSize;
+
+#ifdef LEGACY_BACKEND
+ unsigned emitLclSize;
+ unsigned emitGrowableMaxByteOffs;
+ void emitTmpSizeChanged(unsigned tmpSize);
+#ifdef DEBUG
+ unsigned emitMaxByteOffsIdNum;
+#endif // DEBUG
+#endif // LEGACY_BACKEND
+
+#ifdef DEBUG
+ bool emitChkAlign; // perform some alignment checks
+#endif
+
+ insGroup* emitCurIG;
+
+ void emitSetShortJump(instrDescJmp* id);
+ void emitSetMediumJump(instrDescJmp* id);
+ UNATIVE_OFFSET emitSizeOfJump(instrDescJmp* jmp);
+ UNATIVE_OFFSET emitInstCodeSz(instrDesc* id);
+
+#ifndef LEGACY_BACKEND
+ CORINFO_FIELD_HANDLE emitLiteralConst(ssize_t cnsValIn, emitAttr attr = EA_8BYTE);
+ CORINFO_FIELD_HANDLE emitFltOrDblConst(GenTreeDblCon* tree, emitAttr attr = EA_UNKNOWN);
+ regNumber emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src);
+ regNumber emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src1, GenTree* src2);
+ void emitInsMov(instruction ins, emitAttr attr, GenTree* node);
+ insFormat emitMapFmtForIns(insFormat fmt, instruction ins);
+ insFormat emitMapFmtAtoM(insFormat fmt);
+ void emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt, instruction ins);
+ void spillIntArgRegsToShadowSlots();
+#endif // !LEGACY_BACKEND
+
+/************************************************************************/
+/* The logic that creates and keeps track of instruction groups */
+/************************************************************************/
+
+#ifdef _TARGET_ARM_
+// The only place where this limited instruction group size is a problem is
+// in the prolog, where we only support a single instruction group. We should really fix that.
+// ARM can require a bigger prolog instruction group. One scenario is where a
+// function uses all the incoming integer and single-precision floating-point arguments,
+// and must store them all to the frame on entry. If the frame is very large, we generate
+// ugly code like "movw r10, 0x488; add r10, sp; vstr s0, [r10]" for each store, which
+// eats up our insGroup buffer.
+#define SC_IG_BUFFER_SIZE (100 * sizeof(instrDesc) + 14 * TINY_IDSC_SIZE)
+#else // !_TARGET_ARM_
+#define SC_IG_BUFFER_SIZE (50 * sizeof(instrDesc) + 14 * TINY_IDSC_SIZE)
+#endif // !_TARGET_ARM_
+
+ size_t emitIGbuffSize;
+
+ insGroup* emitIGlist; // first instruction group
+ insGroup* emitIGlast; // last instruction group
+ insGroup* emitIGthis; // issued instruction group
+
+ insGroup* emitPrologIG; // prolog instruction group
+
+ instrDescJmp* emitJumpList; // list of local jumps in method
+ instrDescJmp* emitJumpLast; // last of local jumps in method
+ void emitJumpDistBind(); // Bind all the local jumps in method
+
+ void emitCheckFuncletBranch(instrDesc* jmp, insGroup* jmpIG); // Check for illegal branches between funclets
+
+ bool emitFwdJumps; // forward jumps present?
+ bool emitNoGCIG; // Are we generating IGF_NOGCINTERRUPT insGroups (for prologs, epilogs, etc.)
+ bool emitForceNewIG; // If we generate an instruction, and not another instruction group, force create a new emitAdd
+ // instruction group.
+
+ BYTE* emitCurIGfreeNext; // next available byte in buffer
+ BYTE* emitCurIGfreeEndp; // one byte past the last available byte in buffer
+ BYTE* emitCurIGfreeBase; // first byte address
+
+ unsigned emitCurIGinsCnt; // # of collected instr's in buffer
+ unsigned emitCurIGsize; // estimated code size of current group in bytes
+ UNATIVE_OFFSET emitCurCodeOffset; // current code offset within group
+ UNATIVE_OFFSET emitTotalCodeSize; // bytes of code in entire method
+
+ insGroup* emitFirstColdIG; // first cold instruction group
+
+ void emitSetFirstColdIGCookie(void* bbEmitCookie)
+ {
+ emitFirstColdIG = (insGroup*)bbEmitCookie;
+ }
+
+ int emitOffsAdj; // current code offset adjustment
+
+ instrDescJmp* emitCurIGjmpList; // list of jumps in current IG
+
+ // emitPrev* and emitInit* are only used during code generation, not during
+ // emission (issuing), to determine what GC values to store into an IG.
+ // Note that only the Vars ones are actually used, apparently due to bugs
+ // in that tracking. See emitSavIG(): the important use of ByrefRegs is commented
+ // out, and GCrefRegs is always saved.
+
+ VARSET_TP emitPrevGCrefVars;
+ regMaskTP emitPrevGCrefRegs;
+ regMaskTP emitPrevByrefRegs;
+
+ VARSET_TP emitInitGCrefVars;
+ regMaskTP emitInitGCrefRegs;
+ regMaskTP emitInitByrefRegs;
+
+ // If this is set, we ignore comparing emitPrev* and emitInit* to determine
+ // whether to save GC state (to save space in the IG), and always save it.
+
+ bool emitForceStoreGCState;
+
+ // emitThis* variables are used during emission, to track GC updates
+ // on a per-instruction basis. During code generation, per-instruction
+ // tracking is done with variables gcVarPtrSetCur, gcRegGCrefSetCur,
+ // and gcRegByrefSetCur. However, these are also used for a slightly
+ // different purpose during code generation: to try to minimize the
+ // amount of GC data stored to an IG, by only storing deltas from what
+ // we expect to see at an IG boundary. Also, only emitThisGCrefVars is
+ // really the only one used; the others seem to be calculated, but not
+ // used due to bugs.
+
+ VARSET_TP emitThisGCrefVars;
+ regMaskTP emitThisGCrefRegs; // Current set of registers holding GC references
+ regMaskTP emitThisByrefRegs; // Current set of registers holding BYREF references
+
+ bool emitThisGCrefVset; // Is "emitThisGCrefVars" up to date?
+
+ regNumber emitSyncThisObjReg; // where is "this" enregistered for synchronized methods?
+
+#if MULTIREG_HAS_SECOND_GC_RET
+ void emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize);
+#endif // MULTIREG_HAS_SECOND_GC_RET
+
+ static void emitEncodeCallGCregs(regMaskTP regs, instrDesc* id);
+ static unsigned emitDecodeCallGCregs(instrDesc* id);
+
+ unsigned emitNxtIGnum;
+
+ // random nop insertion to break up nop sleds
+ unsigned emitNextNop;
+ bool emitRandomNops;
+ void emitEnableRandomNops()
+ {
+ emitRandomNops = true;
+ }
+ void emitDisableRandomNops()
+ {
+ emitRandomNops = false;
+ }
+
+ insGroup* emitAllocAndLinkIG();
+ insGroup* emitAllocIG();
+ void emitInitIG(insGroup* ig);
+ void emitInsertIGAfter(insGroup* insertAfterIG, insGroup* ig);
+
+ void emitNewIG();
+ void emitDisableGC();
+ void emitEnableGC();
+ void emitGenIG(insGroup* ig);
+ insGroup* emitSavIG(bool emitAdd = false);
+ void emitNxtIG(bool emitAdd = false);
+
+ bool emitCurIGnonEmpty()
+ {
+ return (emitCurIG && emitCurIGfreeNext > emitCurIGfreeBase);
+ }
+
+ instrDesc* emitLastIns;
+
+#ifdef DEBUG
+ void emitCheckIGoffsets();
+#endif
+
+ // Terminates any in-progress instruction group, making the current IG a new empty one.
+ // Mark this instruction group as having a label; return the the new instruction group.
+ // Sets the emitter's record of the currently live GC variables
+ // and registers. The "isFinallyTarget" parameter indicates that the current location is
+ // the start of a basic block that is returned to after a finally clause in non-exceptional execution.
+ void* emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, BOOL isFinallyTarget = FALSE);
+
+#ifdef _TARGET_ARMARCH_
+
+ void emitGetInstrDescs(insGroup* ig, instrDesc** id, int* insCnt);
+
+ bool emitGetLocationInfo(emitLocation* emitLoc, insGroup** pig, instrDesc** pid, int* pinsRemaining = NULL);
+
+ bool emitNextID(insGroup*& ig, instrDesc*& id, int& insRemaining);
+
+ typedef void (*emitProcessInstrFunc_t)(instrDesc* id, void* context);
+
+ void emitWalkIDs(emitLocation* locFrom, emitProcessInstrFunc_t processFunc, void* context);
+
+ static void emitGenerateUnwindNop(instrDesc* id, void* context);
+
+#endif // _TARGET_ARMARCH_
+
+#if defined(_TARGET_ARM_)
+ emitter::insFormat emitInsFormat(instruction ins);
+ size_t emitInsCode(instruction ins, insFormat fmt);
+#endif
+
+#ifdef _TARGET_X86_
+ void emitMarkStackLvl(unsigned stackLevel);
+#endif
+
+ int emitNextRandomNop();
+
+ void* emitAllocInstr(size_t sz, emitAttr attr);
+
+ instrDesc* emitAllocInstr(emitAttr attr)
+ {
+ return (instrDesc*)emitAllocInstr(sizeof(instrDesc), attr);
+ }
+
+ instrDescJmp* emitAllocInstrJmp()
+ {
+ return (instrDescJmp*)emitAllocInstr(sizeof(instrDescJmp), EA_1BYTE);
+ }
+
+#if !defined(_TARGET_ARM64_)
+ instrDescLbl* emitAllocInstrLbl()
+ {
+ return (instrDescLbl*)emitAllocInstr(sizeof(instrDescLbl), EA_4BYTE);
+ }
+#endif // !_TARGET_ARM64_
+
+ instrDescCns* emitAllocInstrCns(emitAttr attr)
+ {
+ return (instrDescCns*)emitAllocInstr(sizeof(instrDescCns), attr);
+ }
+ instrDescCns* emitAllocInstrCns(emitAttr attr, int cns)
+ {
+ instrDescCns* result = (instrDescCns*)emitAllocInstr(sizeof(instrDescCns), attr);
+ result->idSetIsLargeCns();
+ result->idcCnsVal = cns;
+ return result;
+ }
+
+ instrDescDsp* emitAllocInstrDsp(emitAttr attr)
+ {
+ return (instrDescDsp*)emitAllocInstr(sizeof(instrDescDsp), attr);
+ }
+
+ instrDescCnsDsp* emitAllocInstrCnsDsp(emitAttr attr)
+ {
+ return (instrDescCnsDsp*)emitAllocInstr(sizeof(instrDescCnsDsp), attr);
+ }
+
+ instrDescAmd* emitAllocInstrAmd(emitAttr attr)
+ {
+ return (instrDescAmd*)emitAllocInstr(sizeof(instrDescAmd), attr);
+ }
+
+ instrDescCnsAmd* emitAllocInstrCnsAmd(emitAttr attr)
+ {
+ return (instrDescCnsAmd*)emitAllocInstr(sizeof(instrDescCnsAmd), attr);
+ }
+
+ instrDescCGCA* emitAllocInstrCGCA(emitAttr attr)
+ {
+ return (instrDescCGCA*)emitAllocInstr(sizeof(instrDescCGCA), attr);
+ }
+
+ instrDesc* emitNewInstrTiny(emitAttr attr);
+ instrDesc* emitNewInstrSmall(emitAttr attr);
+ instrDesc* emitNewInstr(emitAttr attr = EA_4BYTE);
+ instrDesc* emitNewInstrSC(emitAttr attr, ssize_t cns);
+ instrDesc* emitNewInstrCns(emitAttr attr, ssize_t cns);
+ instrDesc* emitNewInstrDsp(emitAttr attr, ssize_t dsp);
+ instrDesc* emitNewInstrCnsDsp(emitAttr attr, ssize_t cns, int dsp);
+ instrDescJmp* emitNewInstrJmp();
+
+#if !defined(_TARGET_ARM64_)
+ instrDescLbl* emitNewInstrLbl();
+#endif // !_TARGET_ARM64_
+
+ static const BYTE emitFmtToOps[];
+
+#ifdef DEBUG
+ static const unsigned emitFmtCount;
+#endif
+
+ bool emitIsTinyInsDsc(instrDesc* id);
+ bool emitIsScnsInsDsc(instrDesc* id);
+
+ size_t emitSizeOfInsDsc(instrDesc* id);
+
+ /************************************************************************/
+ /* The following keeps track of stack-based GC values */
+ /************************************************************************/
+
+ unsigned emitTrkVarCnt;
+ int* emitGCrFrameOffsTab; // Offsets of tracked stack ptr vars (varTrkIndex -> stkOffs)
+
+ unsigned emitGCrFrameOffsCnt; // Number of tracked stack ptr vars
+ int emitGCrFrameOffsMin; // Min offset of a tracked stack ptr var
+ int emitGCrFrameOffsMax; // Max offset of a tracked stack ptr var
+ bool emitContTrkPtrLcls; // All lcl between emitGCrFrameOffsMin/Max are only tracked stack ptr vars
+ varPtrDsc** emitGCrFrameLiveTab; // Cache of currently live varPtrs (stkOffs -> varPtrDsc)
+
+ int emitArgFrameOffsMin;
+ int emitArgFrameOffsMax;
+
+ int emitLclFrameOffsMin;
+ int emitLclFrameOffsMax;
+
+ int emitSyncThisObjOffs; // what is the offset of "this" for synchronized methods?
+
+public:
+ void emitSetFrameRangeGCRs(int offsLo, int offsHi);
+ void emitSetFrameRangeLcls(int offsLo, int offsHi);
+ void emitSetFrameRangeArgs(int offsLo, int offsHi);
+
+ static instruction emitJumpKindToIns(emitJumpKind jumpKind);
+ static emitJumpKind emitInsToJumpKind(instruction ins);
+ static emitJumpKind emitReverseJumpKind(emitJumpKind jumpKind);
+
+#ifdef _TARGET_ARM_
+ static unsigned emitJumpKindCondCode(emitJumpKind jumpKind);
+#endif
+
+#ifdef DEBUG
+ void emitInsSanityCheck(instrDesc* id);
+#endif
+
+#ifdef _TARGET_ARMARCH_
+ // Returns true if instruction "id->idIns()" writes to a register that might be used to contain a GC
+ // pointer. This exempts the SP and PC registers, and floating point registers. Memory access
+ // instructions that pre- or post-increment their memory address registers are *not* considered to write
+ // to GC registers, even if that memory address is a by-ref: such an instruction cannot change the GC
+ // status of that register, since it must be a byref before and remains one after.
+ //
+ // This may return false positives.
+ bool emitInsMayWriteToGCReg(instrDesc* id);
+
+ // Returns "true" if instruction "id->idIns()" writes to a LclVar stack location.
+ bool emitInsWritesToLclVarStackLoc(instrDesc* id);
+
+ // Returns true if the instruction may write to more than one register.
+ bool emitInsMayWriteMultipleRegs(instrDesc* id);
+#endif // _TARGET_ARMARCH_
+
+ /************************************************************************/
+ /* The following is used to distinguish helper vs non-helper calls */
+ /************************************************************************/
+
+ static bool emitNoGChelper(unsigned IHX);
+
+ /************************************************************************/
+ /* The following logic keeps track of live GC ref values */
+ /************************************************************************/
+
+ bool emitFullGCinfo; // full GC pointer maps?
+ bool emitFullyInt; // fully interruptible code?
+
+#if EMIT_TRACK_STACK_DEPTH
+ unsigned emitCntStackDepth; // 0 in prolog/epilog, One DWORD elsewhere
+ unsigned emitMaxStackDepth; // actual computed max. stack depth
+#endif
+
+ /* Stack modelling wrt GC */
+
+ bool emitSimpleStkUsed; // using the "simple" stack table?
+
+ union {
+ struct // if emitSimpleStkUsed==true
+ {
+#define BITS_IN_BYTE (8)
+#define MAX_SIMPLE_STK_DEPTH (BITS_IN_BYTE * sizeof(unsigned))
+
+ unsigned emitSimpleStkMask; // bit per pushed dword (if it fits. Lowest bit <==> last pushed arg)
+ unsigned emitSimpleByrefStkMask; // byref qualifier for emitSimpleStkMask
+ } u1;
+
+ struct // if emitSimpleStkUsed==false
+ {
+ BYTE emitArgTrackLcl[16]; // small local table to avoid malloc
+ BYTE* emitArgTrackTab; // base of the argument tracking stack
+ BYTE* emitArgTrackTop; // top of the argument tracking stack
+ USHORT emitGcArgTrackCnt; // count of pending arg records (stk-depth for frameless methods, gc ptrs on stk
+ // for framed methods)
+ } u2;
+ };
+
+ unsigned emitCurStackLvl; // amount of bytes pushed on stack
+
+#if EMIT_TRACK_STACK_DEPTH
+ /* Functions for stack tracking */
+
+ void emitStackPush(BYTE* addr, GCtype gcType);
+
+ void emitStackPushN(BYTE* addr, unsigned count);
+
+ void emitStackPop(BYTE* addr, bool isCall, unsigned char callInstrSize, unsigned count = 1);
+
+ void emitStackKillArgs(BYTE* addr, unsigned count, unsigned char callInstrSize);
+
+ void emitRecordGCcall(BYTE* codePos, unsigned char callInstrSize);
+
+ // Helpers for the above
+
+ void emitStackPushLargeStk(BYTE* addr, GCtype gcType, unsigned count = 1);
+ void emitStackPopLargeStk(BYTE* addr, bool isCall, unsigned char callInstrSize, unsigned count = 1);
+#endif // EMIT_TRACK_STACK_DEPTH
+
+ /* Liveness of stack variables, and registers */
+
+ void emitUpdateLiveGCvars(int offs, BYTE* addr, bool birth);
+ void emitUpdateLiveGCvars(VARSET_VALARG_TP vars, BYTE* addr);
+ void emitUpdateLiveGCregs(GCtype gcType, regMaskTP regs, BYTE* addr);
+
+#ifdef DEBUG
+ const char* emitGetFrameReg();
+ void emitDispRegSet(regMaskTP regs);
+ void emitDispVarSet();
+#endif
+
+ void emitGCregLiveUpd(GCtype gcType, regNumber reg, BYTE* addr);
+ void emitGCregLiveSet(GCtype gcType, regMaskTP mask, BYTE* addr, bool isThis);
+ void emitGCregDeadUpdMask(regMaskTP, BYTE* addr);
+ void emitGCregDeadUpd(regNumber reg, BYTE* addr);
+ void emitGCregDeadSet(GCtype gcType, regMaskTP mask, BYTE* addr);
+
+ void emitGCvarLiveUpd(int offs, int varNum, GCtype gcType, BYTE* addr);
+ void emitGCvarLiveSet(int offs, GCtype gcType, BYTE* addr, ssize_t disp = -1);
+ void emitGCvarDeadUpd(int offs, BYTE* addr);
+ void emitGCvarDeadSet(int offs, BYTE* addr, ssize_t disp = -1);
+
+ GCtype emitRegGCtype(regNumber reg);
+
+ // We have a mixture of code emission methods, some of which return the size of the emitted instruction,
+ // requiring the caller to add this to the current code pointer (dst += <call to emit code>), others of which
+ // return the updated code pointer (dst = <call to emit code>). Sometimes we'd like to get the size of
+ // the generated instruction for the latter style. This method accomplishes that --
+ // "emitCodeWithInstructionSize(dst, <call to emitCode>, &instrSize)" will do the call, and set
+ // "*instrSize" to the after-before code pointer difference. Returns the result of the call. (And
+ // asserts that the instruction size fits in an unsigned char.)
+ static BYTE* emitCodeWithInstructionSize(BYTE* codePtrBefore, BYTE* newCodePointer, unsigned char* instrSize);
+
+ /************************************************************************/
+ /* The following logic keeps track of initialized data sections */
+ /************************************************************************/
+
+ /* One of these is allocated for every blob of initialized data */
+
+ struct dataSection
+ {
+ enum sectionType
+ {
+ data,
+ blockAbsoluteAddr,
+ blockRelative32
+ };
+
+ dataSection* dsNext;
+ UNATIVE_OFFSET dsSize;
+ sectionType dsType;
+ // variable-sized array used to store the constant data
+ // or BasicBlock* array in the block cases.
+ BYTE dsCont[0];
+ };
+
+ /* These describe the entire initialized/uninitialized data sections */
+
+ struct dataSecDsc
+ {
+ dataSection* dsdList;
+ dataSection* dsdLast;
+ UNATIVE_OFFSET dsdOffs;
+ };
+
+ dataSecDsc emitConsDsc;
+
+ dataSection* emitDataSecCur;
+
+ void emitOutputDataSec(dataSecDsc* sec, BYTE* dst);
+
+ /************************************************************************/
+ /* Handles to the current class and method. */
+ /************************************************************************/
+
+ COMP_HANDLE emitCmpHandle;
+
+ /************************************************************************/
+ /* Helpers for interface to EE */
+ /************************************************************************/
+
+ void emitRecordRelocation(void* location, /* IN */
+ void* target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum = 0, /* IN */
+ INT32 addlDelta = 0); /* IN */
+
+ void emitRecordCallSite(ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO* callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle); /* IN */
+
+#ifdef DEBUG
+ // This is a scratch buffer used to minimize the number of sig info structs
+ // we have to allocate for recordCallSite.
+ CORINFO_SIG_INFO* emitScratchSigInfo;
+#endif // DEBUG
+
+/************************************************************************/
+/* Logic to collect and display statistics */
+/************************************************************************/
+
+#if EMITTER_STATS
+
+ friend void emitterStats(FILE* fout);
+ friend void emitterStaticStats(FILE* fout);
+
+ static size_t emitSizeMethod;
+
+ static unsigned emitTotalInsCnt;
+
+ static unsigned emitTotalIGcnt; // total number of insGroup allocated
+ static unsigned emitTotalPhIGcnt; // total number of insPlaceholderGroupData allocated
+ static unsigned emitTotalIGicnt;
+ static size_t emitTotalIGsize;
+ static unsigned emitTotalIGmcnt; // total method count
+ static unsigned emitTotalIGjmps;
+ static unsigned emitTotalIGptrs;
+
+ static size_t emitTotMemAlloc;
+
+ static unsigned emitSmallDspCnt;
+ static unsigned emitLargeDspCnt;
+
+ static unsigned emitSmallCnsCnt;
+#define SMALL_CNS_TSZ 256
+ static unsigned emitSmallCns[SMALL_CNS_TSZ];
+ static unsigned emitLargeCnsCnt;
+
+ static unsigned emitIFcounts[IF_COUNT];
+
+#endif // EMITTER_STATS
+
+/*************************************************************************
+ *
+ * Define any target-dependent emitter members.
+ */
+
+#include "emitdef.h"
+
+ // It would be better if this were a constructor, but that would entail revamping the allocation
+ // infrastructure of the entire JIT...
+ void Init()
+ {
+ VarSetOps::AssignNoCopy(emitComp, emitPrevGCrefVars, VarSetOps::MakeEmpty(emitComp));
+ VarSetOps::AssignNoCopy(emitComp, emitInitGCrefVars, VarSetOps::MakeEmpty(emitComp));
+ VarSetOps::AssignNoCopy(emitComp, emitThisGCrefVars, VarSetOps::MakeEmpty(emitComp));
+ }
+};
+
+/*****************************************************************************
+ *
+ * Define any target-dependent inlines.
+ */
+
+#include "emitinl.h"
+
+inline void emitter::instrDesc::checkSizes()
+{
+#ifdef DEBUG
+#if HAS_TINY_DESC
+ C_ASSERT(TINY_IDSC_SIZE == (offsetof(instrDesc, _idDebugOnlyInfo) + sizeof(instrDescDebugInfo*)));
+#else // !tiny
+ C_ASSERT(SMALL_IDSC_SIZE == (offsetof(instrDesc, _idDebugOnlyInfo) + sizeof(instrDescDebugInfo*)));
+#endif
+#endif
+ C_ASSERT(SMALL_IDSC_SIZE == offsetof(instrDesc, _idAddrUnion));
+}
+
+/*****************************************************************************
+ *
+ * Returns true if the given instruction descriptor is a "tiny" or a "small
+ * constant" one (i.e. one of the descriptors that don't have all instrDesc
+ * fields allocated).
+ */
+
+inline bool emitter::emitIsTinyInsDsc(instrDesc* id)
+{
+ return id->idIsTiny();
+}
+
+inline bool emitter::emitIsScnsInsDsc(instrDesc* id)
+{
+ return id->idIsSmallDsc();
+}
+
+/*****************************************************************************
+ *
+ * Given an instruction, return its "update mode" (RD/WR/RW).
+ */
+
+inline insUpdateModes emitter::emitInsUpdateMode(instruction ins)
+{
+#ifdef DEBUG
+ assert((unsigned)ins < emitInsModeFmtCnt);
+#endif
+ return (insUpdateModes)emitInsModeFmtTab[ins];
+}
+
+/*****************************************************************************
+ *
+ * Return the number of epilog blocks generated so far.
+ */
+
+inline unsigned emitter::emitGetEpilogCnt()
+{
+ return emitEpilogCnt;
+}
+
+/*****************************************************************************
+ *
+ * Return the current size of the specified data section.
+ */
+
+inline UNATIVE_OFFSET emitter::emitDataSize()
+{
+ return emitConsDsc.dsdOffs;
+}
+
+/*****************************************************************************
+ *
+ * Return a handle to the current position in the output stream. This can
+ * be later converted to an actual code offset in bytes.
+ */
+
+inline void* emitter::emitCurBlock()
+{
+ return emitCurIG;
+}
+
+/*****************************************************************************
+ *
+ * The emitCurOffset() method returns a cookie that identifies the current
+ * position in the instruction stream. Due to things like scheduling (and
+ * the fact that the final size of some instructions cannot be known until
+ * the end of code generation), we return a value with the instruction number
+ * and its estimated offset to the caller.
+ */
+
+inline unsigned emitGetInsNumFromCodePos(unsigned codePos)
+{
+ return (codePos & 0xFFFF);
+}
+
+inline unsigned emitGetInsOfsFromCodePos(unsigned codePos)
+{
+ return (codePos >> 16);
+}
+
+inline unsigned emitter::emitCurOffset()
+{
+ unsigned codePos = emitCurIGinsCnt + (emitCurIGsize << 16);
+
+ assert(emitGetInsOfsFromCodePos(codePos) == emitCurIGsize);
+ assert(emitGetInsNumFromCodePos(codePos) == emitCurIGinsCnt);
+
+ // printf("[IG=%02u;ID=%03u;OF=%04X] => %08X\n", emitCurIG->igNum, emitCurIGinsCnt, emitCurIGsize, codePos);
+
+ return codePos;
+}
+
+extern const unsigned short emitTypeSizes[TYP_COUNT];
+
+template <class T>
+inline emitAttr emitTypeSize(T type)
+{
+ assert(TypeGet(type) < TYP_COUNT);
+ assert(emitTypeSizes[TypeGet(type)] > 0);
+ return (emitAttr)emitTypeSizes[TypeGet(type)];
+}
+
+extern const unsigned short emitTypeActSz[TYP_COUNT];
+
+inline emitAttr emitActualTypeSize(var_types type)
+{
+ assert(type < TYP_COUNT);
+ assert(emitTypeActSz[type] > 0);
+ return (emitAttr)emitTypeActSz[type];
+}
+
+/*****************************************************************************
+ *
+ * Convert between an operand size in bytes and a smaller encoding used for
+ * storage in instruction descriptors.
+ */
+
+/* static */ inline emitter::opSize emitter::emitEncodeSize(emitAttr size)
+{
+ assert(size == EA_1BYTE || size == EA_2BYTE || size == EA_4BYTE || size == EA_8BYTE || size == EA_16BYTE ||
+ size == EA_32BYTE);
+
+ return emitSizeEncode[((int)size) - 1];
+}
+
+/* static */ inline emitAttr emitter::emitDecodeSize(emitter::opSize ensz)
+{
+ assert(((unsigned)ensz) < OPSZ_COUNT);
+
+ return emitSizeDecode[ensz];
+}
+
+/*****************************************************************************
+ *
+ * Little helpers to allocate various flavors of instructions.
+ */
+
+inline emitter::instrDesc* emitter::emitNewInstrTiny(emitAttr attr)
+{
+ instrDesc* id;
+
+ id = (instrDesc*)emitAllocInstr(TINY_IDSC_SIZE, attr);
+ id->idSetIsTiny();
+
+ return id;
+}
+
+inline emitter::instrDesc* emitter::emitNewInstrSmall(emitAttr attr)
+{
+ instrDesc* id;
+
+ // This is larger than the Tiny Descr
+ id = (instrDesc*)emitAllocInstr(SMALL_IDSC_SIZE, attr);
+ id->idSetIsSmallDsc();
+
+ return id;
+}
+
+inline emitter::instrDesc* emitter::emitNewInstr(emitAttr attr)
+{
+ // This is larger than the Small Descr
+ return emitAllocInstr(attr);
+}
+
+inline emitter::instrDescJmp* emitter::emitNewInstrJmp()
+{
+ return emitAllocInstrJmp();
+}
+
+#if !defined(_TARGET_ARM64_)
+inline emitter::instrDescLbl* emitter::emitNewInstrLbl()
+{
+ return emitAllocInstrLbl();
+}
+#endif // !_TARGET_ARM64_
+
+inline emitter::instrDesc* emitter::emitNewInstrDsp(emitAttr attr, ssize_t dsp)
+{
+ if (dsp == 0)
+ {
+ instrDesc* id = emitAllocInstr(attr);
+
+#if EMITTER_STATS
+ emitSmallDspCnt++;
+#endif
+
+ return id;
+ }
+ else
+ {
+ instrDescDsp* id = emitAllocInstrDsp(attr);
+
+ id->idSetIsLargeDsp();
+ id->iddDspVal = dsp;
+
+#if EMITTER_STATS
+ emitLargeDspCnt++;
+#endif
+
+ return id;
+ }
+}
+
+/*****************************************************************************
+ *
+ * Allocate an instruction descriptor for an instruction with a constant operand.
+ * The instruction descriptor uses the idAddrUnion to save additional info
+ * so the smallest size that this can be is sizeof(instrDesc).
+ * Note that this very similar to emitter::emitNewInstrSC(), except it never
+ * allocates a small descriptor.
+ */
+inline emitter::instrDesc* emitter::emitNewInstrCns(emitAttr attr, ssize_t cns)
+{
+ if (instrDesc::fitsInSmallCns(cns))
+ {
+ instrDesc* id = emitAllocInstr(attr);
+
+ id->idSmallCns(cns);
+
+#if EMITTER_STATS
+ emitSmallCnsCnt++;
+ if (cns - ID_MIN_SMALL_CNS >= SMALL_CNS_TSZ)
+ emitSmallCns[SMALL_CNS_TSZ - 1]++;
+ else
+ emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
+#endif
+
+ return id;
+ }
+ else
+ {
+ instrDescCns* id = emitAllocInstrCns(attr);
+
+ id->idSetIsLargeCns();
+ id->idcCnsVal = cns;
+
+#if EMITTER_STATS
+ emitLargeCnsCnt++;
+#endif
+
+ return id;
+ }
+}
+
+/*****************************************************************************
+ *
+ * Get the instrDesc size, general purpose version
+ *
+ */
+
+inline size_t emitter::emitGetInstrDescSize(const instrDesc* id)
+{
+ if (id->idIsTiny())
+ {
+ return TINY_IDSC_SIZE;
+ }
+
+ if (id->idIsSmallDsc())
+ {
+ return SMALL_IDSC_SIZE;
+ }
+
+ if (id->idIsLargeCns())
+ {
+ return sizeof(instrDescCns);
+ }
+
+ return sizeof(instrDesc);
+}
+
+/*****************************************************************************
+ *
+ * Allocate an instruction descriptor for an instruction with a small integer
+ * constant operand. This is the same as emitNewInstrCns() except that here
+ * any constant that is small enough for instrDesc::fitsInSmallCns() only gets
+ * allocated SMALL_IDSC_SIZE bytes (and is thus a small descriptor, whereas
+ * emitNewInstrCns() always allocates at least sizeof(instrDesc).
+ */
+
+inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, ssize_t cns)
+{
+ instrDesc* id;
+
+ if (instrDesc::fitsInSmallCns(cns))
+ {
+ id = (instrDesc*)emitAllocInstr(SMALL_IDSC_SIZE, attr);
+
+ id->idSmallCns(cns);
+ id->idSetIsSmallDsc();
+ }
+ else
+ {
+ id = (instrDesc*)emitAllocInstr(sizeof(instrDescCns), attr);
+
+ id->idSetIsLargeCns();
+ ((instrDescCns*)id)->idcCnsVal = cns;
+ }
+
+ return id;
+}
+
+/*****************************************************************************
+ *
+ * Get the instrDesc size for something that contains a constant
+ */
+
+inline size_t emitter::emitGetInstrDescSizeSC(const instrDesc* id)
+{
+ if (id->idIsSmallDsc())
+ {
+ return SMALL_IDSC_SIZE;
+ }
+ else if (id->idIsLargeCns())
+ {
+ return sizeof(instrDescCns);
+ }
+ else
+ {
+ return sizeof(instrDesc);
+ }
+}
+
+/*****************************************************************************
+ *
+ * The following helpers should be used to access the various values that
+ * get stored in different places within the instruction descriptor.
+ */
+
+inline ssize_t emitter::emitGetInsCns(instrDesc* id)
+{
+ return id->idIsLargeCns() ? ((instrDescCns*)id)->idcCnsVal : id->idSmallCns();
+}
+
+inline ssize_t emitter::emitGetInsDsp(instrDesc* id)
+{
+ if (id->idIsLargeDsp())
+ {
+ if (id->idIsLargeCns())
+ {
+ return ((instrDescCnsDsp*)id)->iddcDspVal;
+ }
+ return ((instrDescDsp*)id)->iddDspVal;
+ }
+ return 0;
+}
+
+inline ssize_t emitter::emitGetInsCnsDsp(instrDesc* id, ssize_t* dspPtr)
+{
+ if (id->idIsLargeCns())
+ {
+ if (id->idIsLargeDsp())
+ {
+ *dspPtr = ((instrDescCnsDsp*)id)->iddcDspVal;
+ return ((instrDescCnsDsp*)id)->iddcCnsVal;
+ }
+ else
+ {
+ *dspPtr = 0;
+ return ((instrDescCns*)id)->idcCnsVal;
+ }
+ }
+ else
+ {
+ if (id->idIsLargeDsp())
+ {
+ *dspPtr = ((instrDescDsp*)id)->iddDspVal;
+ return id->idSmallCns();
+ }
+ else
+ {
+ *dspPtr = 0;
+ return id->idSmallCns();
+ }
+ }
+}
+
+/*****************************************************************************
+ *
+ * Get hold of the argument count for an indirect call.
+ */
+
+inline unsigned emitter::emitGetInsCIargs(instrDesc* id)
+{
+ if (id->idIsLargeCall())
+ {
+ return ((instrDescCGCA*)id)->idcArgCnt;
+ }
+ else
+ {
+ assert(id->idIsLargeDsp() == false);
+ assert(id->idIsLargeCns() == false);
+
+ ssize_t cns = emitGetInsCns(id);
+ assert((unsigned)cns == (size_t)cns);
+ return (unsigned)cns;
+ }
+}
+
+/*****************************************************************************
+ *
+ * Returns true if the given register contains a live GC ref.
+ */
+
+inline GCtype emitter::emitRegGCtype(regNumber reg)
+{
+ assert(emitIssuing);
+
+ if ((emitThisGCrefRegs & genRegMask(reg)) != 0)
+ {
+ return GCT_GCREF;
+ }
+ else if ((emitThisByrefRegs & genRegMask(reg)) != 0)
+ {
+ return GCT_BYREF;
+ }
+ else
+ {
+ return GCT_NONE;
+ }
+}
+
+#ifdef DEBUG
+
+#if EMIT_TRACK_STACK_DEPTH
+#define CHECK_STACK_DEPTH() assert((int)emitCurStackLvl >= 0)
+#else
+#define CHECK_STACK_DEPTH()
+#endif
+
+#endif // DEBUG
+
+/*****************************************************************************
+ *
+ * Return true when a given code offset is properly aligned for the target
+ */
+
+inline bool IsCodeAligned(UNATIVE_OFFSET offset)
+{
+ return ((offset & (CODE_ALIGN - 1)) == 0);
+}
+
+// Static:
+inline BYTE* emitter::emitCodeWithInstructionSize(BYTE* codePtrBefore, BYTE* newCodePointer, unsigned char* instrSize)
+{
+ // DLD: Perhaps this method should return the instruction size, and we should do dst += <that size>
+ // as is done in other cases?
+ assert(newCodePointer >= codePtrBefore);
+ ClrSafeInt<unsigned char> callInstrSizeSafe = ClrSafeInt<unsigned char>(newCodePointer - codePtrBefore);
+ assert(!callInstrSizeSafe.IsOverflow());
+ *instrSize = callInstrSizeSafe.Value();
+ return newCodePointer;
+}
+
+/*****************************************************************************
+ *
+ * Add a new IG to the current list, and get it ready to receive code.
+ */
+
+inline void emitter::emitNewIG()
+{
+ insGroup* ig = emitAllocAndLinkIG();
+
+ /* It's linked in. Now, set it up to accept code */
+
+ emitGenIG(ig);
+}
+
+// Start a new instruction group that is not interruptable
+inline void emitter::emitDisableGC()
+{
+ emitNoGCIG = true;
+
+ if (emitCurIGnonEmpty())
+ {
+ emitNxtIG(true);
+ }
+ else
+ {
+ emitCurIG->igFlags |= IGF_NOGCINTERRUPT;
+ }
+}
+
+// Start a new instruction group that is interruptable
+inline void emitter::emitEnableGC()
+{
+ emitNoGCIG = false;
+
+ // The next time an instruction needs to be generated, force a new instruction group.
+ // It will be an emitAdd group in that case. Note that the next thing we see might be
+ // a label, which will force a non-emitAdd group.
+ //
+ // Note that we can't just create a new instruction group here, because we don't know
+ // if there are going to be any instructions added to it, and we don't support empty
+ // instruction groups.
+ emitForceNewIG = true;
+}
+
+/*****************************************************************************/
+#endif // _EMIT_H_
+/*****************************************************************************/