// ==--==
#include "strike.h"
+#include "gcinfo.h"
#include "util.h"
#include <dbghelp.h>
#include <limits.h>
///
/// Dump X86 GCInfo header and table
///
-void X86Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
X86GCDump::InfoHdr header;
- X86GCDump::GCDump gcDump(encBytes, 5, true);
+ X86GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ BYTE* pTable = dac_cast<PTR_BYTE>(gcInfoToken.Info);
if (bPrintHeader)
{
gcDump.gcPrintf = gcPrintf;
///
/// Dump AMD64 GCInfo table
///
-void AMD64Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
if (bPrintHeader)
{
ExtOut("Pointer table:\n");
}
- GCDump gcDump(encBytes, 5, true);
+ GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
}
#endif // SOS_TARGET_AMD64
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
X86Machine() {}
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
ARMMachine() {}
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
AMD64Machine() {}
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs);}
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
ARM64Machine() {}
///
/// Dump ARM GCInfo table
///
-void ARMMachine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
#ifndef FEATURE_PAL
if (bPrintHeader)
ExtOut("Pointer table:\n");
}
- ARMGCDump::GCDump gcDump(encBytes, 5, true);
+ ARMGCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
#endif // !FEATURE_PAL
}
///
/// Dump ARM GCInfo table
///
-void ARM64Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void ARM64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
if (bPrintHeader)
{
ExtOut("Pointer table:\n");
}
- ARM64GCDump::GCDump gcDump(encBytes, 5, true);
+ ARM64GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
}
#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
#endif
#include "strike.h"
-
#include <wdbgexts.h>
#include <dbgeng.h>
#include <stdio.h>
// the DAC to read the DAC-ized data structures.
#include "daccess.h"
+#include "gcinfo.h"
+
// Convert between CLRDATA_ADDRESS and TADDR.
#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
typedef void (*printfFtn)(const char* fmt, ...);
// Dumps the GCInfo
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
protected:
IMachine() {}
// Mutable table pointer since we need to pass the appropriate
// offset into the table to DumpGCTable.
- BYTE *pTable = table;
+ GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
- g_targetMachine->DumpGCInfo(pTable, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
+ g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
return Status;
}
VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
{
GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
-
- g_targetMachine->DumpGCInfo(pInfo->table, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
+ GCInfoToken gcInfoToken = { pInfo, GCINFO_VERSION };
+ g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
pInfo->fDoneDecoding = true;
SwitchToFiber(pInfo->pvMainFiber);
};
template <typename _Ty>
- class is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type>
+ struct is_blittable<_Ty, typename std::enable_if<std::is_arithmetic<_Ty>::value>::type>
: std::true_type
{ // determines whether _Ty is blittable
};
// allow types to declare themselves blittable by including a static bool
// member "is_blittable".
template <typename _Ty>
- class is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type>
+ struct is_blittable<_Ty, typename std::enable_if<_Ty::is_blittable>::type>
: std::true_type
{ // determines whether _Ty is blittable
};
EECodeInfo codeInfo(methodStart);
_ASSERTE(codeInfo.IsValid());
- TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfo());
+ TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
*extents = new (nothrow) METH_EXTENTS;
if (!*extents)
codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo);
// The stackwalker needs GC info to find the parent 'stack pointer' or PSP
- PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(codeInfo.GetGCInfo());
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info);
if (pGCInfo != NULL)
{
- GcInfoDecoder gcDecoder(pGCInfo, DECODE_PSP_SYM, 0);
+ GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0);
DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true);
}
}
unsigned gcInfoSize = UINT_MAX;
//parse GCInfo for size information.
- PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(mi.GetGCInfo());
+ GCInfoToken gcInfoToken = mi.GetGCInfoToken();
+ PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(gcInfoToken.Info);
void (* stringOutFn)(const char *, ...);
IF_OPT(GC_INFO)
{
PTR_CBYTE curGCInfoPtr = gcInfo;
g_holdStringOutData.Clear();
- GCDump gcDump;
+ GCDump gcDump(gcInfoToken.Version);
gcDump.gcPrintf = stringOutFn;
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
- GcInfoDecoder gcInfoDecoder(curGCInfoPtr, DECODE_CODE_LENGTH, 0);
+ GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH, 0);
methodSize = gcInfoDecoder.GetCodeLength();
#endif
#ifdef _TARGET_X86_
InfoHdr hdr;
stringOutFn( "method info Block:\n" );
- curGCInfoPtr += gcDump.DumpInfoHdr(curGCInfoPtr, &hdr, &methodSize, 0);
+ curGCInfoPtr += gcDump.DumpInfoHdr(PTR_CBYTE(gcInfoToken.Info), &hdr, &methodSize, 0);
stringOutFn( "\n" );
#endif
{
PTR_CBYTE curGCInfoPtr = gcInfo;
g_holdStringOutData.Clear();
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = stringOutFn;
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
- GcInfoDecoder gcInfoDecoder(curGCInfoPtr, DECODE_CODE_LENGTH, 0);
+ GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, GCINFO_VERSION }, DECODE_CODE_LENGTH, 0);
methodSize = gcInfoDecoder.GetCodeLength();
#endif
codeHeaderData->MethodStart =
(CLRDATA_ADDRESS) codeInfo.GetStartAddress();
- size_t methodSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfo());
+ size_t methodSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
_ASSERTE(FitsIn<DWORD>(methodSize));
codeHeaderData->MethodSize = static_cast<DWORD>(methodSize);
assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
#endif //BACKGROUND_GC
- int bottom_gen = 0;
-#ifdef BACKGROUND_GC
- if (settings.concurrent)
- {
- bottom_gen = max_generation;
- }
-#endif //BACKGROUND_GC
- {
- for (int gen_number = bottom_gen; gen_number <= max_generation+1; gen_number++)
- {
- dynamic_data* dd = dynamic_data_of (gen_number);
- dd_new_allocation(dd) = dd_gc_new_allocation (dd);
- }
- }
-
if (fgn_maxgen_percent)
{
if (settings.condemned_generation == (max_generation - 1))
}
dd_gc_new_allocation (dd) -= in;
+ dd_new_allocation (dd) = dd_gc_new_allocation (dd);
gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
+ dd_new_allocation (dd) = dd_gc_new_allocation (dd);
+
//update counter
dd_promoted_size (dd) = out;
if (gen_number == max_generation)
dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
get_alignment_constant (FALSE));
+ dd_new_allocation (dd) = dd_gc_new_allocation (dd);
gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
gen_data->size_after = total_gen_size;
-GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
- : fDumpEncBytes (encBytes ),
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion (gcInfoVer),
+ fDumpEncBytes (encBytes ),
cMaxEncBytes (maxEncBytes ),
fDumpCodeOffsets(dumpCodeOffs)
{
* Display the byte encodings for the given range of the GC tables.
*/
-PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
+PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE gcInfoBlock, int cDumpBytes)
{
_ASSERTE((cDumpBytes >= 0) && (cMaxEncBytes < 256));
unsigned count;
int cBytesLeft;
- for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = table;
+ for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = gcInfoBlock;
count > 0;
count--, pCurPos++, cBytesLeft--)
{
gcPrintf("| ");
}
- return table + cDumpBytes;
+ return gcInfoBlock + cDumpBytes;
}
/*****************************************************************************/
/*****************************************************************************/
-GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
- : fDumpEncBytes (encBytes ),
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion(gcInfoVer),
+ fDumpEncBytes (encBytes ),
cMaxEncBytes (maxEncBytes ),
fDumpCodeOffsets(dumpCodeOffs)
{
}
-size_t GCDump::DumpGCTable(PTR_CBYTE table,
+size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
unsigned methodSize,
bool verifyGCTables)
{
- GcInfoDecoder hdrdecoder(table,
+ GCInfoToken gcInfoToken = { dac_cast<PTR_VOID>(gcInfoBlock), gcInfoVersion };
+ GcInfoDecoder hdrdecoder(gcInfoToken,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_GS_COOKIE
| DECODE_CODE_LENGTH
UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
gcPrintf("Code size: %x\n", cbEncodedMethodSize);
- GcInfoDumper dumper(table);
+ GcInfoDumper dumper(gcInfoToken);
GcInfoDumpState state;
state.LastCodeOffset = -1;
#error pick suitable ADDRESS_SPACING for platform
#endif
-GcInfoDumper::GcInfoDumper (PTR_CBYTE pbGCInfo)
+GcInfoDumper::GcInfoDumper (GCInfoToken gcInfoToken)
{
- m_pbGCInfo = pbGCInfo;
+ m_gcTable = gcInfoToken;
m_pRecords = NULL;
m_gcInfoSize = 0;
}
//
// Decode header information
//
- GcInfoDecoder hdrdecoder(m_pbGCInfo,
+ GcInfoDecoder hdrdecoder(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_GC_LIFETIMES
//
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
- GcInfoDecoder safePointDecoder(m_pbGCInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(m_gcTable, (GcInfoDecoderFlags)0, 0);
#endif
{
- GcInfoDecoder untrackedDecoder(m_pbGCInfo, DECODE_GC_LIFETIMES, 0);
+ GcInfoDecoder untrackedDecoder(m_gcTable, DECODE_GC_LIFETIMES, 0);
untrackedDecoder.EnumerateUntrackedSlots(®disp,
0,
&LivePointerCallback,
{
BOOL fNewInterruptible = FALSE;
- GcInfoDecoder decoder1(m_pbGCInfo,
+ GcInfoDecoder decoder1(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_VARARG
}
#endif
- GcInfoDecoder decoder2(m_pbGCInfo,
+ GcInfoDecoder decoder2(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_VARARG
#include "corjit.h" // For NativeVarInfo
#include "stackwalktypes.h"
#include "bitvector.h"
+#include "gcinfotypes.h"
#if !defined(_TARGET_X86_)
#define USE_GC_INFO_DECODER
*/
virtual unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr) = 0;
+ GCInfoToken gcInfoToken) = 0;
#endif // _TARGET_AMD64_ && _DEBUG
/*
not take procedure splitting into account). For the actual size of
the hot region call IJitManager::JitTokenToMethodHotSize.
*/
-virtual size_t GetFunctionSize(PTR_VOID methodInfoPtr) = 0;
+virtual size_t GetFunctionSize(GCInfoToken gcInfoToken) = 0;
/*
Returns the size of the frame (barring localloc)
virtual
unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr);
+ GCInfoToken gcInfoToken);
#endif // _TARGET_AMD64_ && _DEBUG
/*
Returns the size of a given function.
*/
virtual
-size_t GetFunctionSize(
- PTR_VOID methodInfoPtr);
+size_t GetFunctionSize(GCInfoToken gcInfoToken);
/*
Returns the size of the frame (barring localloc)
/* This file is shared between the VM and JIT/IL and SOS/Strike directories */
-#include "gcinfo.h"
+#include "gcinfotypes.h"
/*****************************************************************************/
/*
#define __GCDUMP_H__
/*****************************************************************************/
-#include "gcinfo.h" // For InfoHdr
+#include "gcinfotypes.h" // For InfoHdr
#ifndef FASTCALL
#ifndef FEATURE_PAL
{
public:
- GCDump (bool encBytes = true,
+ GCDump (UINT32 gcInfoVersion,
+ bool encBytes = true,
unsigned maxEncBytes = 5,
bool dumpCodeOffs = true);
* Return value : Size in bytes of the header encoding
*/
- unsigned FASTCALL DumpInfoHdr (PTR_CBYTE table,
+ unsigned FASTCALL DumpInfoHdr (PTR_CBYTE gcInfoBlock,
InfoHdr * header, /* OUT */
unsigned * methodSize, /* OUT */
bool verifyGCTables = false);
/*-------------------------------------------------------------------------
* Dumps the GC tables to 'stdout'
- * table : Ptr to the start of the table part of the GC info.
- * This immediately follows the GCinfo header
+ * table : The GCInfoToken
* verifyGCTables : If the JIT has been compiled with VERIFY_GC_TABLES
* Return value : Size in bytes of the GC table encodings
*/
- size_t FASTCALL DumpGCTable (PTR_CBYTE table,
+ size_t FASTCALL DumpGCTable (PTR_CBYTE gcInfoBlock,
#ifdef _TARGET_X86_
const InfoHdr& header,
#endif
public:
typedef void (*printfFtn)(const char* fmt, ...);
printfFtn gcPrintf;
+ UINT32 gcInfoVersion;
//-------------------------------------------------------------------------
protected:
/* Helper methods */
- PTR_CBYTE DumpEncoding(PTR_CBYTE table,
+ PTR_CBYTE DumpEncoding(PTR_CBYTE gcInfoBlock,
int cDumpBytes);
void DumpOffset (unsigned o);
void DumpOffsetEx(unsigned o);
#define _GCINFO_H_
/*****************************************************************************/
-#include <stdlib.h> // For memcmp()
-#include "windef.h" // For BYTE
#include "daccess.h"
-
-#include "bitvector.h" // for ptrArgTP
+#include "windef.h" // For BYTE
// Some declarations in this file are used on non-x86 platforms, but most are x86-specific.
const unsigned pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr
const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this"
-#ifdef _TARGET_X86_
-
-#ifndef FASTCALL
-#define FASTCALL __fastcall
-#endif
+//-----------------------------------------------------------------------------
+// The current GCInfo Version
+//-----------------------------------------------------------------------------
-// we use offsetof to get the offset of a field
-#include <stddef.h> // offsetof
-#ifndef offsetof
-#define offsetof(s,m) ((size_t)&(((s *)0)->m))
-#endif
-
-enum infoHdrAdjustConstants {
- // Constants
- SET_FRAMESIZE_MAX = 7,
- SET_ARGCOUNT_MAX = 8, // Change to 6
- SET_PROLOGSIZE_MAX = 16,
- SET_EPILOGSIZE_MAX = 10, // Change to 6
- SET_EPILOGCNT_MAX = 4,
- SET_UNTRACKED_MAX = 3
-};
+#define GCINFO_VERSION 1
+//-----------------------------------------------------------------------------
+// GCInfoToken: A wrapper that contains the GcInfo data and version number.
//
-// Enum to define the 128 codes that are used to incrementally adjust the InfoHdr structure
+// The version# is not stored in the GcInfo structure -- because it is
+// wasteful to store the version once for every method.
+// Instead, the version# istracked per range-section of generated/loaded methods.
//
-enum infoHdrAdjust {
-
- SET_FRAMESIZE = 0, // 0x00
- SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08
- SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11
- SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22
- SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d
- SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37
-
- FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1,
-
- FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b
- FLIP_ESI_SAVED, // 0x3c
- FLIP_EBX_SAVED, // 0x3d
- FLIP_EBP_SAVED, // 0x3e
- FLIP_EBP_FRAME, // 0x3f
- FLIP_INTERRUPTIBLE, // 0x40
- FLIP_DOUBLE_ALIGN, // 0x41
- FLIP_SECURITY, // 0x42
- FLIP_HANDLERS, // 0x43
- FLIP_LOCALLOC, // 0x44
- FLIP_EDITnCONTINUE, // 0x45
- FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding
- FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding
- FLIP_VARARGS, // 0x48
- FLIP_PROF_CALLBACKS, // 0x49
- FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding
- FLIP_SYNC, // 0x4B
- FLIP_HAS_GENERICS_CONTEXT,// 0x4C
- FLIP_GENERICS_CONTEXT_IS_METHODDESC,// 0x4D
-
- // 0x4E .. 0x4f unused
-
- NEXT_FOUR_START = 0x50,
- NEXT_FOUR_FRAMESIZE = 0x50,
- NEXT_FOUR_ARGCOUNT = 0x60,
- NEXT_THREE_PROLOGSIZE = 0x70,
- NEXT_THREE_EPILOGSIZE = 0x78
-};
-
-#define HAS_UNTRACKED ((unsigned int) -1)
-#define HAS_VARPTR ((unsigned int) -1)
-// 0 is not a valid offset for EBP-frames as all locals are at a negative offset
-// For ESP frames, the cookie is above (at a higher address than) the buffers,
-// and so cannot be at offset 0.
-#define INVALID_GS_COOKIE_OFFSET 0
-// Temporary value to indicate that the offset needs to be read after the header
-#define HAS_GS_COOKIE_OFFSET ((unsigned int) -1)
-
-// 0 is not a valid sync offset
-#define INVALID_SYNC_OFFSET 0
-// Temporary value to indicate that the offset needs to be read after the header
-#define HAS_SYNC_OFFSET ((unsigned int) -1)
-
-#define INVALID_ARGTAB_OFFSET 0
-
-#include <pshpack1.h>
-
-// Working set optimization: saving 12 * 128 = 1536 bytes in infoHdrShortcut
-struct InfoHdr;
-
-struct InfoHdrSmall {
- unsigned char prologSize; // 0
- unsigned char epilogSize; // 1
- unsigned char epilogCount : 3; // 2 [0:2]
- unsigned char epilogAtEnd : 1; // 2 [3]
- unsigned char ediSaved : 1; // 2 [4] which callee-saved regs are pushed onto stack
- unsigned char esiSaved : 1; // 2 [5]
- unsigned char ebxSaved : 1; // 2 [6]
- unsigned char ebpSaved : 1; // 2 [7]
- unsigned char ebpFrame : 1; // 3 [0] locals accessed relative to ebp
- unsigned char interruptible : 1; // 3 [1] is intr. at all points (except prolog/epilog), not just call-sites
- unsigned char doubleAlign : 1; // 3 [2] uses double-aligned stack (ebpFrame will be false)
- unsigned char security : 1; // 3 [3] has slot for security object
- unsigned char handlers : 1; // 3 [4] has callable handlers
- unsigned char localloc : 1; // 3 [5] uses localloc
- unsigned char editNcontinue : 1; // 3 [6] was JITed in EnC mode
- unsigned char varargs : 1; // 3 [7] function uses varargs calling convention
- unsigned char profCallbacks : 1; // 4 [0]
- unsigned char genericsContext : 1;//4 [1] function reports a generics context parameter is present
- unsigned char genericsContextIsMethodDesc : 1;//4[2]
- unsigned short argCount; // 5,6 in bytes
- unsigned int frameSize; // 7,8,9,10 in bytes
- unsigned int untrackedCnt; // 11,12,13,14
- unsigned int varPtrTableSize; // 15.16,17,18
-
- // Checks whether "this" is compatible with "target".
- // It is not an exact bit match as "this" could have some
- // marker/place-holder values, which will have to be written out
- // after the header.
-
- bool isHeaderMatch(const InfoHdr& target) const;
-};
-
-
-struct InfoHdr : public InfoHdrSmall {
- // 0 (zero) means that there is no GuardStack cookie
- // The cookie is either at ESP+gsCookieOffset or EBP-gsCookieOffset
- unsigned int gsCookieOffset; // 19,20,21,22
- unsigned int syncStartOffset; // 23,24,25,26
- unsigned int syncEndOffset; // 27,28,29,30
-
- // 31 bytes total
-
- // Checks whether "this" is compatible with "target".
- // It is not an exact bit match as "this" could have some
- // marker/place-holder values, which will have to be written out
- // after the header.
-
- bool isHeaderMatch(const InfoHdr& target) const
- {
-#ifdef _ASSERTE
- // target cannot have place-holder values.
- _ASSERTE(target.untrackedCnt != HAS_UNTRACKED &&
- target.varPtrTableSize != HAS_VARPTR &&
- target.gsCookieOffset != HAS_GS_COOKIE_OFFSET &&
- target.syncStartOffset != HAS_SYNC_OFFSET);
-#endif
-
- // compare two InfoHdr's up to but not including the untrackCnt field
- if (memcmp(this, &target, offsetof(InfoHdr, untrackedCnt)) != 0)
- return false;
-
- if (untrackedCnt != target.untrackedCnt) {
- if (target.untrackedCnt <= SET_UNTRACKED_MAX)
- return false;
- else if (untrackedCnt != HAS_UNTRACKED)
- return false;
- }
-
- if (varPtrTableSize != target.varPtrTableSize) {
- if ((varPtrTableSize != 0) != (target.varPtrTableSize != 0))
- return false;
- }
-
- if ((gsCookieOffset == INVALID_GS_COOKIE_OFFSET) !=
- (target.gsCookieOffset == INVALID_GS_COOKIE_OFFSET))
- return false;
-
- if ((syncStartOffset == INVALID_SYNC_OFFSET) !=
- (target.syncStartOffset == INVALID_SYNC_OFFSET))
- return false;
-
- return true;
- }
-};
-
-
-union CallPattern {
- struct {
- unsigned char argCnt;
- unsigned char regMask; // EBP=0x8, EBX=0x4, ESI=0x2, EDI=0x1
- unsigned char argMask;
- unsigned char codeDelta;
- } fld;
- unsigned val;
-};
-
-#include <poppack.h>
-
-#define IH_MAX_PROLOG_SIZE (51)
-
-extern const InfoHdrSmall infoHdrShortcut[];
-extern int infoHdrLookup[];
-
-inline void GetInfoHdr(int index, InfoHdr * header)
+// The GCInfo version is computed as :
+// 1) The current GCINFO_VERSION for JITted and Ngened images
+// 2) A function of the Ready - to - run major version stored in READYTORUN_HEADER
+// for ready - to - run images.ReadyToRunJitManager::JitTokenToGCInfoVersion()
+// provides the GcInfo version for any Method.Currently, there's only one
+// version of GCInfo.
+//-----------------------------------------------------------------------------
+
+struct GCInfoToken
{
- * ((InfoHdrSmall *) header) = infoHdrShortcut[index];
-
- header->gsCookieOffset = 0;
- header->syncStartOffset = 0;
- header->syncEndOffset = 0;
-}
-
-PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, InfoHdr* header);
-
-BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached);
-BYTE FASTCALL encodeHeaderNext (const InfoHdr& header, InfoHdr* state);
-
-size_t FASTCALL decodeUnsigned (PTR_CBYTE src, unsigned* value);
-size_t FASTCALL decodeUDelta (PTR_CBYTE src, unsigned* value, unsigned lastValue);
-size_t FASTCALL decodeSigned (PTR_CBYTE src, int * value);
-
-#define CP_MAX_CODE_DELTA (0x23)
-#define CP_MAX_ARG_CNT (0x02)
-#define CP_MAX_ARG_MASK (0x00)
-
-extern const unsigned callPatternTable[];
-extern const unsigned callCommonDelta[];
-
-
-int FASTCALL lookupCallPattern(unsigned argCnt,
- unsigned regMask,
- unsigned argMask,
- unsigned codeDelta);
-
-void FASTCALL decodeCallPattern(int pattern,
- unsigned * argCnt,
- unsigned * regMask,
- unsigned * argMask,
- unsigned * codeDelta);
-
-#endif // _TARGET_86_ || _TARGET_ARM_
+ PTR_VOID Info;
+ UINT32 Version;
+};
/*****************************************************************************/
#endif //_GCINFO_H_
#ifndef _GC_INFO_DECODER_
#define _GC_INFO_DECODER_
-#include "daccess.h"
+#include "gcinfotypes.h"
#define _max(a, b) (((a) > (b)) ? (a) : (b))
#define _min(a, b) (((a) < (b)) ? (a) : (b))
// If you are not insterested in interruptibility or gc lifetime information, pass 0 as instructionOffset
GcInfoDecoder(
- PTR_CBYTE gcInfoAddr,
+ GCInfoToken gcInfoToken,
GcInfoDecoderFlags flags,
UINT32 instructionOffset = 0
);
-
//------------------------------------------------------------------------
// Interruptibility
//------------------------------------------------------------------------
#ifdef _DEBUG
GcInfoDecoderFlags m_Flags;
PTR_CBYTE m_GcInfoAddress;
+ UINT32 m_Version;
#endif
#ifdef VERIFY_GCINFO
{
public:
- GcInfoDumper (PTR_CBYTE pbGCInfo);
+ GcInfoDumper (GCInfoToken gcInfoToken);
~GcInfoDumper ();
// Returns TRUE to stop decoding.
UINT marked;
};
- PTR_CBYTE m_pbGCInfo;
+ GCInfoToken m_gcTable;
UINT32 m_StackBaseRegister;
UINT32 m_SizeOfEditAndContinuePreservedArea;
LivePointerRecord *m_pRecords;
#ifndef __GCINFOTYPES_H__
#define __GCINFOTYPES_H__
+#include "gcinfo.h"
+
// This file is included when building an "alt jit". In that case, we are doing a cross-compile:
// we may be building the ARM jit on x86, for example. We generally make that work by conditionalizing on
// a _TARGET_XXX_ variable that we explicitly set in the build, rather than the _XXX_ variable implicitly
__forceinline size_t SAFE_SHIFT_LEFT(size_t x, size_t count)
{
_ASSERTE(count <= BITS_PER_SIZE_T);
- return (x << 1) << (count-1);
+ return (x << 1) << (count - 1);
}
__forceinline size_t SAFE_SHIFT_RIGHT(size_t x, size_t count)
{
_ASSERTE(count <= BITS_PER_SIZE_T);
- return (x >> 1) >> (count-1);
+ return (x >> 1) >> (count - 1);
}
inline UINT32 CeilOfLog2(size_t x)
{
_ASSERTE(x > 0);
- UINT32 result = (x & (x-1)) ? 1 : 0;
- while(x != 1)
+ UINT32 result = (x & (x - 1)) ? 1 : 0;
+ while (x != 1)
{
result++;
x >>= 1;
enum GcSlotFlags
{
- GC_SLOT_BASE = 0x0,
- GC_SLOT_INTERIOR = 0x1,
- GC_SLOT_PINNED = 0x2,
- GC_SLOT_UNTRACKED = 0x4,
+ GC_SLOT_BASE = 0x0,
+ GC_SLOT_INTERIOR = 0x1,
+ GC_SLOT_PINNED = 0x2,
+ GC_SLOT_UNTRACKED = 0x4,
// For internal use by the encoder/decoder
- GC_SLOT_IS_REGISTER = 0x8,
- GC_SLOT_IS_DELETED = 0x10,
+ GC_SLOT_IS_REGISTER = 0x8,
+ GC_SLOT_IS_DELETED = 0x10,
};
enum GcStackSlotBase
{
- GC_CALLER_SP_REL = 0x0,
- GC_SP_REL = 0x1,
- GC_FRAMEREG_REL = 0x2,
+ GC_CALLER_SP_REL = 0x0,
+ GC_SP_REL = 0x1,
+ GC_FRAMEREG_REL = 0x2,
- GC_SPBASE_FIRST = GC_CALLER_SP_REL,
- GC_SPBASE_LAST = GC_FRAMEREG_REL,
+ GC_SPBASE_FIRST = GC_CALLER_SP_REL,
+ GC_SPBASE_LAST = GC_FRAMEREG_REL,
};
#ifdef _DEBUG
};
#endif
-
enum GcSlotState
{
- GC_SLOT_DEAD = 0x0,
- GC_SLOT_LIVE = 0x1,
+ GC_SLOT_DEAD = 0x0,
+ GC_SLOT_LIVE = 0x1,
};
struct GcStackSlot
}
};
+#ifdef _TARGET_X86_
+
+#include <stdlib.h> // For memcmp()
+#include "bitvector.h" // for ptrArgTP
+
+#ifndef FASTCALL
+#define FASTCALL __fastcall
+#endif
+
+// we use offsetof to get the offset of a field
+#include <stddef.h> // offsetof
+#ifndef offsetof
+#define offsetof(s,m) ((size_t)&(((s *)0)->m))
+#endif
+
+enum infoHdrAdjustConstants {
+ // Constants
+ SET_FRAMESIZE_MAX = 7,
+ SET_ARGCOUNT_MAX = 8, // Change to 6
+ SET_PROLOGSIZE_MAX = 16,
+ SET_EPILOGSIZE_MAX = 10, // Change to 6
+ SET_EPILOGCNT_MAX = 4,
+ SET_UNTRACKED_MAX = 3
+};
+
+//
+// Enum to define the 128 codes that are used to incrementally adjust the InfoHdr structure
+//
+enum infoHdrAdjust {
+
+ SET_FRAMESIZE = 0, // 0x00
+ SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08
+ SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11
+ SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22
+ SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d
+ SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37
+
+ FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1,
+
+ FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b
+ FLIP_ESI_SAVED, // 0x3c
+ FLIP_EBX_SAVED, // 0x3d
+ FLIP_EBP_SAVED, // 0x3e
+ FLIP_EBP_FRAME, // 0x3f
+ FLIP_INTERRUPTIBLE, // 0x40
+ FLIP_DOUBLE_ALIGN, // 0x41
+ FLIP_SECURITY, // 0x42
+ FLIP_HANDLERS, // 0x43
+ FLIP_LOCALLOC, // 0x44
+ FLIP_EDITnCONTINUE, // 0x45
+ FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding
+ FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding
+ FLIP_VARARGS, // 0x48
+ FLIP_PROF_CALLBACKS, // 0x49
+ FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding
+ FLIP_SYNC, // 0x4B
+ FLIP_HAS_GENERICS_CONTEXT,// 0x4C
+ FLIP_GENERICS_CONTEXT_IS_METHODDESC,// 0x4D
+
+ // 0x4E .. 0x4f unused
+
+ NEXT_FOUR_START = 0x50,
+ NEXT_FOUR_FRAMESIZE = 0x50,
+ NEXT_FOUR_ARGCOUNT = 0x60,
+ NEXT_THREE_PROLOGSIZE = 0x70,
+ NEXT_THREE_EPILOGSIZE = 0x78
+};
+
+#define HAS_UNTRACKED ((unsigned int) -1)
+#define HAS_VARPTR ((unsigned int) -1)
+// 0 is not a valid offset for EBP-frames as all locals are at a negative offset
+// For ESP frames, the cookie is above (at a higher address than) the buffers,
+// and so cannot be at offset 0.
+#define INVALID_GS_COOKIE_OFFSET 0
+// Temporary value to indicate that the offset needs to be read after the header
+#define HAS_GS_COOKIE_OFFSET ((unsigned int) -1)
+
+// 0 is not a valid sync offset
+#define INVALID_SYNC_OFFSET 0
+// Temporary value to indicate that the offset needs to be read after the header
+#define HAS_SYNC_OFFSET ((unsigned int) -1)
+
+#define INVALID_ARGTAB_OFFSET 0
+
+#include <pshpack1.h>
+
+// Working set optimization: saving 12 * 128 = 1536 bytes in infoHdrShortcut
+struct InfoHdr;
+
+struct InfoHdrSmall {
+ unsigned char prologSize; // 0
+ unsigned char epilogSize; // 1
+ unsigned char epilogCount : 3; // 2 [0:2]
+ unsigned char epilogAtEnd : 1; // 2 [3]
+ unsigned char ediSaved : 1; // 2 [4] which callee-saved regs are pushed onto stack
+ unsigned char esiSaved : 1; // 2 [5]
+ unsigned char ebxSaved : 1; // 2 [6]
+ unsigned char ebpSaved : 1; // 2 [7]
+ unsigned char ebpFrame : 1; // 3 [0] locals accessed relative to ebp
+ unsigned char interruptible : 1; // 3 [1] is intr. at all points (except prolog/epilog), not just call-sites
+ unsigned char doubleAlign : 1; // 3 [2] uses double-aligned stack (ebpFrame will be false)
+ unsigned char security : 1; // 3 [3] has slot for security object
+ unsigned char handlers : 1; // 3 [4] has callable handlers
+ unsigned char localloc : 1; // 3 [5] uses localloc
+ unsigned char editNcontinue : 1; // 3 [6] was JITed in EnC mode
+ unsigned char varargs : 1; // 3 [7] function uses varargs calling convention
+ unsigned char profCallbacks : 1; // 4 [0]
+ unsigned char genericsContext : 1;//4 [1] function reports a generics context parameter is present
+ unsigned char genericsContextIsMethodDesc : 1;//4[2]
+ unsigned short argCount; // 5,6 in bytes
+ unsigned int frameSize; // 7,8,9,10 in bytes
+ unsigned int untrackedCnt; // 11,12,13,14
+ unsigned int varPtrTableSize; // 15.16,17,18
+
+ // Checks whether "this" is compatible with "target".
+ // It is not an exact bit match as "this" could have some
+ // marker/place-holder values, which will have to be written out
+ // after the header.
+
+ bool isHeaderMatch(const InfoHdr& target) const;
+};
+
+
+struct InfoHdr : public InfoHdrSmall {
+ // 0 (zero) means that there is no GuardStack cookie
+ // The cookie is either at ESP+gsCookieOffset or EBP-gsCookieOffset
+ unsigned int gsCookieOffset; // 19,20,21,22
+ unsigned int syncStartOffset; // 23,24,25,26
+ unsigned int syncEndOffset; // 27,28,29,30
+
+ // 31 bytes total
+
+ // Checks whether "this" is compatible with "target".
+ // It is not an exact bit match as "this" could have some
+ // marker/place-holder values, which will have to be written out
+ // after the header.
+
+ bool isHeaderMatch(const InfoHdr& target) const
+ {
+#ifdef _ASSERTE
+ // target cannot have place-holder values.
+ _ASSERTE(target.untrackedCnt != HAS_UNTRACKED &&
+ target.varPtrTableSize != HAS_VARPTR &&
+ target.gsCookieOffset != HAS_GS_COOKIE_OFFSET &&
+ target.syncStartOffset != HAS_SYNC_OFFSET);
+#endif
+
+ // compare two InfoHdr's up to but not including the untrackCnt field
+ if (memcmp(this, &target, offsetof(InfoHdr, untrackedCnt)) != 0)
+ return false;
+
+ if (untrackedCnt != target.untrackedCnt) {
+ if (target.untrackedCnt <= SET_UNTRACKED_MAX)
+ return false;
+ else if (untrackedCnt != HAS_UNTRACKED)
+ return false;
+ }
+
+ if (varPtrTableSize != target.varPtrTableSize) {
+ if ((varPtrTableSize != 0) != (target.varPtrTableSize != 0))
+ return false;
+ }
+
+ if ((gsCookieOffset == INVALID_GS_COOKIE_OFFSET) !=
+ (target.gsCookieOffset == INVALID_GS_COOKIE_OFFSET))
+ return false;
+
+ if ((syncStartOffset == INVALID_SYNC_OFFSET) !=
+ (target.syncStartOffset == INVALID_SYNC_OFFSET))
+ return false;
+
+ return true;
+ }
+};
+
+
+union CallPattern {
+ struct {
+ unsigned char argCnt;
+ unsigned char regMask; // EBP=0x8, EBX=0x4, ESI=0x2, EDI=0x1
+ unsigned char argMask;
+ unsigned char codeDelta;
+ } fld;
+ unsigned val;
+};
+
+#include <poppack.h>
+
+#define IH_MAX_PROLOG_SIZE (51)
+
+extern const InfoHdrSmall infoHdrShortcut[];
+extern int infoHdrLookup[];
+
+inline void GetInfoHdr(int index, InfoHdr * header)
+{
+ *((InfoHdrSmall *)header) = infoHdrShortcut[index];
+
+ header->gsCookieOffset = 0;
+ header->syncStartOffset = 0;
+ header->syncEndOffset = 0;
+}
+
+PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, InfoHdr* header);
+
+BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached);
+BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state);
+
+size_t FASTCALL decodeUnsigned(PTR_CBYTE src, unsigned* value);
+size_t FASTCALL decodeUDelta(PTR_CBYTE src, unsigned* value, unsigned lastValue);
+size_t FASTCALL decodeSigned(PTR_CBYTE src, int * value);
+
+#define CP_MAX_CODE_DELTA (0x23)
+#define CP_MAX_ARG_CNT (0x02)
+#define CP_MAX_ARG_MASK (0x00)
+
+extern const unsigned callPatternTable[];
+extern const unsigned callCommonDelta[];
+
+
+int FASTCALL lookupCallPattern(unsigned argCnt,
+ unsigned regMask,
+ unsigned argMask,
+ unsigned codeDelta);
+
+void FASTCALL decodeCallPattern(int pattern,
+ unsigned * argCnt,
+ unsigned * regMask,
+ unsigned * argMask,
+ unsigned * codeDelta);
+
+#endif // _TARGET_86_
+
// Stack offsets must be 8-byte aligned, so we use this unaligned
// offset to represent that the method doesn't have a security object
#define NO_SECURITY_OBJECT (-1)
#define NO_GENERICS_INST_CONTEXT (-1)
#define NO_PSP_SYM (-1)
-
#if defined(_TARGET_AMD64_)
#ifndef TARGET_POINTER_SIZE
return REG_ARG_0;
}
+//----------------------------------------------------------------------
+// getSpillTempDsc: get the TempDsc corresponding to a spilled tree.
+//
+// Arguments:
+// tree - spilled GenTree node
+//
+// Return Value:
+// TempDsc corresponding to tree
+TempDsc* CodeGenInterface::getSpillTempDsc(GenTree* tree)
+{
+ // tree must be in spilled state.
+ assert((tree->gtFlags & GTF_SPILLED) != 0);
+
+ // Get the tree's SpillDsc.
+ RegSet::SpillDsc* prevDsc;
+ RegSet::SpillDsc* spillDsc = regSet.rsGetSpillInfo(tree, tree->gtRegNum, &prevDsc);
+ assert(spillDsc != nullptr);
+
+ // Get the temp desc.
+ TempDsc* temp = regSet.rsGetSpillTempWord(tree->gtRegNum, spillDsc, prevDsc);
+ return temp;
+}
+
#ifdef _TARGET_XARCH_
#ifdef _TARGET_AMD64_
void SpillFloat (regNumber reg, bool bIsCall = false);
#endif // LEGACY_BACKEND
+ // The following method is used by xarch emitter for handling contained tree temps.
+ TempDsc* getSpillTempDsc(GenTree* tree);
+
public:
emitter* getEmitter() { return m_cgEmitter; }
protected:
{
emit->emitInsBinary(genGetInsForOper(treeNode->gtOper, targetType), size, treeNode, divisor);
}
- else if (divisor->gtRegNum == targetReg)
+ else if (!divisor->isContained() && divisor->gtRegNum == targetReg)
{
// It is not possible to generate 2-operand divss or divsd where reg2 = reg1 / reg2
// because divss/divsd reg1, reg2 will over-write reg1. Therefore, in case of AMD64
// The arithmetic node must be sitting in a register (since it's not contained)
noway_assert(targetReg != REG_NA);
- regNumber op1reg = op1->gtRegNum;
- regNumber op2reg = op2->gtRegNum;
+ regNumber op1reg = op1->isContained() ? REG_NA: op1->gtRegNum;
+ regNumber op2reg = op2->isContained() ? REG_NA: op2->gtRegNum;
GenTreePtr dst;
GenTreePtr src;
if (tree->isContained())
{
- if (tree->isIndir())
+ if (tree->isContainedSpillTemp())
+ {
+ // spill temps are un-tracked and hence no need to update life
+ }
+ else if (tree->isIndir())
{
genConsumeAddress(tree->AsIndir()->Addr());
}
{
dblConst = src->AsDblCon();
}
-
+
// find local field if any
GenTreeLclFld* lclField = nullptr;
if (src->isContainedLclField())
lclVar = dst->AsLclVar();
}
+ // find contained spill tmp if any
+ TempDsc* tmpDsc = nullptr;
+ if (src->isContainedSpillTemp())
+ {
+ assert(src->IsRegOptional());
+ tmpDsc = codeGen->getSpillTempDsc(src);
+ }
+ else if (dst->isContainedSpillTemp())
+ {
+ assert(dst->IsRegOptional());
+ tmpDsc = codeGen->getSpillTempDsc(dst);
+ }
+
// First handle the simple non-memory cases
//
- if ((mem == nullptr) && (lclField == nullptr) && (lclVar == nullptr))
+ if ((mem == nullptr) &&
+ (lclField == nullptr) &&
+ (lclVar == nullptr) &&
+ (tmpDsc == nullptr))
{
if (intConst != nullptr)
{
// Next handle the cases where we have a stack based local memory operand.
//
unsigned varNum = BAD_VAR_NUM;
- unsigned offset = (unsigned) -1;
+ unsigned offset = (unsigned)-1;
if (lclField != nullptr)
{
varNum = lclVar->AsLclVarCommon()->GetLclNum();
offset = 0;
}
+ else if (tmpDsc != nullptr)
+ {
+ varNum = tmpDsc->tdTempNum();
+ offset = 0;
+ }
- if (varNum != BAD_VAR_NUM)
+ // Spill temp numbers are negative and start with -1
+ // which also happens to be BAD_VAR_NUM. For this reason
+ // we also need to check 'tmpDsc != nullptr' here.
+ if (varNum != BAD_VAR_NUM ||
+ tmpDsc != nullptr)
{
// Is the memory op in the source position?
if (src->isContainedLclField() ||
- src->isContainedLclVar())
+ src->isContainedLclVar() ||
+ src->isContainedSpillTemp())
{
if (instrHasImplicitRegPairDest(ins))
{
}
else // The memory op is in the dest position.
{
- assert(dst->gtRegNum == REG_NA);
+ assert(dst->gtRegNum == REG_NA || dst->IsRegOptional());
// src could be int or reg
if (src->isContainedIntOrIImmed())
}
}
+ if (tmpDsc != nullptr)
+ {
+ emitComp->tmpRlsTemp(tmpDsc);
+ }
+
return dst->gtRegNum;
}
#endif
-#include "gcinfo.h"
+#include "gcinfotypes.h"
#ifdef JIT32_GCENCODER
InfoHdr* header,
unsigned* methodSize)
{
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
printf("Method info block:\n");
{
printf("Pointer table:\n");
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
return gcDump.DumpGCTable(table, header, methodSize, verifyGCTables);
const void* codeBlock,
unsigned offs)
{
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
gcDump.DumpPtrsInFrame((const BYTE*)infoBlock, (const BYTE*)codeBlock, offs, verifyGCTables);
// until after the LSRA phase has allocated physical registers to the treenodes.
bool GenTree::isContained() const
{
+ if (isContainedSpillTemp())
+ {
+ return true;
+ }
+
if (gtHasReg())
+ {
return false;
+ }
// these actually produce a register (the flags reg, we just don't model it)
// and are a separate instruction from the branch that consumes the result
if (OperKind() & GTK_RELOP)
+ {
return false;
+ }
// TODO-Cleanup : this is not clean, would be nice to have some way of marking this.
switch (OperGet())
bool isContainedLclField() const { return isContained() && isLclField(); }
- bool isContainedLclVar() const { return isContained() && (OperGet() == GT_LCL_VAR); }
+ bool isContainedLclVar() const { return isContained() && (OperGet() == GT_LCL_VAR); }
+
+ bool isContainedSpillTemp() const;
// Indicates whether it is a memory op.
// Right now it includes Indir and LclField ops.
bool isContainedMemoryOp() const
{
- return (isContained() && isMemoryOp()) || isContainedLclVar();
+ return (isContained() && isMemoryOp()) || isContainedLclVar() || isContainedSpillTemp();
}
regNumber GetRegNum() const
#define GTF_REVERSE_OPS 0x00000020 // operand op2 should be evaluated before op1 (normally, op1 is evaluated first and op2 is evaluated second)
#define GTF_REG_VAL 0x00000040 // operand is sitting in a register (or part of a TYP_LONG operand is sitting in a register)
- #define GTF_SPILLED 0x00000080 // the value has been spilled
- #define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled
+ #define GTF_SPILLED 0x00000080 // the value has been spilled
#ifdef LEGACY_BACKEND
- #define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled
+ #define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled
+ #define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled
+#else
+ #define GTF_NOREG_AT_USE 0x00000100 // tree node is in memory at the point of use
#endif // LEGACY_BACKEND
#define GTF_REDINDEX_CHECK 0x00000100 // Used for redundant range checks. Disjoint from GTF_SPILLED_OPER
return false;
}
+inline bool GenTree::isContainedSpillTemp() const
+{
+#if !defined(LEGACY_BACKEND)
+ // If spilled and no reg at use, then it is treated as contained.
+ if (((gtFlags & GTF_SPILLED) != 0) &&
+ ((gtFlags & GTF_NOREG_AT_USE) != 0))
+ {
+ return true;
+ }
+#endif //!LEGACY_BACKEND
+
+ return false;
+}
/*****************************************************************************/
#ifndef _JITGCINFO_H_
#define _JITGCINFO_H_
-#include "gcinfo.h"
+
+#include "gcinfotypes.h"
#ifndef JIT32_GCENCODER
#include "gcinfoencoder.h"
-#include "gcinfotypes.h"
#endif
/*****************************************************************************/
void TreeNodeInfoInit(GenTreePtr* tree, GenTree* parent);
#if defined(_TARGET_XARCH_)
void TreeNodeInfoInitSimple(GenTree* tree);
- void SetRegOptionalForBinOp(GenTree* tree);
- void TryToSetRegOptional(GenTree* operand);
+
+ //----------------------------------------------------------------------
+ // SetRegOptional - sets a bit to indicate to LSRA that register
+ // for a given tree node is optional for codegen purpose. If no
+ // register is allocated to such a tree node, its parent node treats
+ // it as a contained memory operand during codegen.
+ //
+ // Arguments:
+ // tree - GenTree node
+ //
+ // Returns
+ // None
+ void SetRegOptional(GenTree* tree)
+ {
+ tree->gtLsraInfo.regOptional = true;
+ }
+
+ GenTree* PreferredRegOptionalOperand(GenTree* tree);
+
+ // ------------------------------------------------------------------
+ // SetRegOptionalBinOp - Indicates which of the operands of a bin-op
+ // register requirement is optional. Xarch instruction set allows
+ // either of op1 or op2 of binary operation (e.g. add, mul etc) to be
+ // a memory operand. This routine provides info to register allocator
+ // which of its operands optionally require a register. Lsra might not
+ // allocate a register to RefTypeUse positions of such operands if it
+ // is beneficial. In such a case codegen will treat them as memory
+ // operands.
+ //
+ // Arguments:
+ // tree - Gentree of a bininary operation.
+ //
+ // Returns
+ // None.
+ //
+ // Note: On xarch at most only one of the operands will be marked as
+ // reg optional, even when both operands could be considered register
+ // optional.
+ void SetRegOptionalForBinOp(GenTree* tree)
+ {
+ assert(GenTree::OperIsBinary(tree->OperGet()));
+
+ GenTree* op1 = tree->gtGetOp1();
+ GenTree* op2 = tree->gtGetOp2();
+
+ if (tree->OperIsCommutative() &&
+ tree->TypeGet() == op1->TypeGet())
+ {
+ GenTree* preferredOp = PreferredRegOptionalOperand(tree);
+ SetRegOptional(preferredOp);
+ }
+ else if (tree->TypeGet() == op2->TypeGet())
+ {
+ SetRegOptional(op2);
+ }
+ }
#endif // defined(_TARGET_XARCH_)
void TreeNodeInfoInitReturn(GenTree* tree);
void TreeNodeInfoInitShiftRotate(GenTree* tree);
info->srcCount = 2;
info->dstCount = 0;
- GenTreePtr other = nullptr;
+ GenTreePtr other;
if (CheckImmedAndMakeContained(tree, node->gtIndex))
{
other = node->gtArrLen;
other = node->gtArrLen;
}
- if (other->isMemoryOp())
+ if (node->gtIndex->TypeGet() == node->gtArrLen->TypeGet())
{
- if (node->gtIndex->TypeGet() == node->gtArrLen->TypeGet())
+ if (other->isMemoryOp())
{
MakeSrcContained(tree, other);
}
- }
- else
- {
- // since 'other' operand is not contained, we can mark it as reg optional
- TryToSetRegOptional(other);
+ else
+ {
+ // We can mark 'other' as reg optional, since it is not contained.
+ SetRegOptional(other);
+ }
}
}
break;
else
{
// If there are no containable operands, we can make an operand reg optional.
- SetRegOptionalForBinOp(tree);
+ // SSE2 allows only op2 to be a memory-op.
+ SetRegOptional(op2);
}
return;
op2->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_RAX | RBM_RDX));
// If there are no containable operands, we can make an operand reg optional.
- SetRegOptionalForBinOp(tree);
+ // Div instruction allows only op2 to be a memory op.
+ SetRegOptional(op2);
}
}
{
// Mark the operand as reg optional since codegen can still
// generate code if op1 is on stack.
- TryToSetRegOptional(op1);
+ SetRegOptional(op1);
}
break;
{
// Mark castOp as reg optional to indicate codegen
// can still generate code if it is on stack.
- TryToSetRegOptional(castOp);
+ SetRegOptional(castOp);
}
}
}
{
MakeSrcContained(tree, otherOp);
}
- else if (otherOp->isMemoryOp())
+ else if (otherOp->isMemoryOp() &&
+ ((otherOp == op2) || IsSafeToContainMem(tree, otherOp)))
{
- if ((otherOp == op2) || IsSafeToContainMem(tree, otherOp))
- {
- MakeSrcContained(tree, otherOp);
- }
+ MakeSrcContained(tree, otherOp);
}
else
{
- // Mark otherOp as reg optional to indicate codgen can still generate
- // code even if otherOp is on stack.
- TryToSetRegOptional(otherOp);
+ // SSE2 allows only otherOp to be a memory-op. Since otherOp is not
+ // contained, we can mark it reg-optional.
+ SetRegOptional(otherOp);
}
return;
}
}
}
+
if (op1CanBeContained)
{
if (op1->isMemoryOp())
MakeSrcContained(tree, op1);
}
else
- {
+ {
+ bool op1IsMadeContained = false;
+
// When op1 is a GT_AND we can often generate a single "test" instruction
// instead of two instructions (an "and" instruction followed by a "cmp"/"test")
//
}
// Mark the 'op1' (the GT_AND) operand as contained
MakeSrcContained(tree, op1);
+ op1IsMadeContained = true;
// During Codegen we will now generate "test andOp1, andOp2CnsVal"
}
assert(!castOp1->gtOverflowEx()); // Must not be an overflow checking operation
GenTreePtr removeTreeNode = op1;
- GenTreePtr removeTreeNodeChild = castOp1;
tree->gtOp.gtOp1 = castOp1;
+ op1 = castOp1;
castOp1->gtType = TYP_UBYTE;
// trim down the value if castOp1 is an int constant since its type changed to UBYTE.
if (castOp1->isMemoryOp())
{
MakeSrcContained(tree, op1);
+ op1IsMadeContained = true;
}
}
}
#endif
}
}
+
+ // If not made contained, op1 can be marked as reg-optional.
+ if (!op1IsMadeContained)
+ {
+ SetRegOptional(op1);
+ }
}
}
}
// One of op1 or op2 could be marked as reg optional
// to indicate that codgen can still generate code
// if one of them is on stack.
- TryToSetRegOptional(op2);
-
- if (!op2->IsRegOptional())
- {
- TryToSetRegOptional(op1);
- }
+ SetRegOptional(PreferredRegOptionalOperand(tree));
}
if (varTypeIsSmall(op1Type) && varTypeIsUnsigned(op1Type))
bool requiresOverflowCheck = tree->gtOverflowEx();
bool useLeaEncoding = false;
GenTreePtr memOp = nullptr;
+
+ bool hasImpliedFirstOperand = false;
+ GenTreeIntConCommon* imm = nullptr;
+ GenTreePtr other = nullptr;
// There are three forms of x86 multiply:
// one-op form: RDX:RAX = RAX * r/m
// In LSRA we set the kill set for this operation to RBM_RAX|RBM_RDX
//
info->setDstCandidates(m_lsra,RBM_RAX);
+ hasImpliedFirstOperand = true;
}
else if (tree->gtOper == GT_MULHI)
{
// have to use the encoding:RDX:RAX = RAX * rm
info->setDstCandidates(m_lsra, RBM_RAX);
+ hasImpliedFirstOperand = true;
}
else if (IsContainableImmed(tree, op2) || IsContainableImmed(tree, op1))
{
- GenTreeIntConCommon* imm;
- GenTreePtr other;
-
if (IsContainableImmed(tree, op2))
{
imm = op2->AsIntConCommon();
- other = op1;
+ other = op1;
}
else
{
imm = op1->AsIntConCommon();
- other = op2;
+ other = op2;
}
// CQ: We want to rewrite this into a LEA
}
MakeSrcContained(tree, imm); // The imm is always contained
- if (other->isIndir())
+ if (other->isMemoryOp())
{
memOp = other; // memOp may be contained below
}
}
+
// We allow one operand to be a contained memory operand.
// The memory op type must match with the 'tree' type.
// This is because during codegen we use 'tree' type to derive EmitTypeSize.
//
if (!useLeaEncoding)
{
- if (memOp != nullptr)
+ if ((memOp != nullptr) &&
+ (memOp->TypeGet() == tree->TypeGet()) &&
+ IsSafeToContainMem(tree, memOp))
{
- if ((memOp->TypeGet() == tree->TypeGet()) &&
- IsSafeToContainMem(tree, memOp))
- {
- MakeSrcContained(tree, memOp);
- }
+ MakeSrcContained(tree, memOp);
+ }
+ else if (imm != nullptr)
+ {
+ // Has a contained immediate operand.
+ // Only 'other' operand can be marked as reg optional.
+ assert(other != nullptr);
+ SetRegOptional(other);
+ }
+ else if (hasImpliedFirstOperand)
+ {
+ // Only op2 can be marke as reg optional.
+ SetRegOptional(op2);
}
else
{
- // If there are no containable operands, we can make an operand reg optional.
+ // If there are no containable operands, we can make either of op1 or op2
+ // as reg optional.
SetRegOptionalForBinOp(tree);
}
}
return true;
}
-//----------------------------------------------------------------------
-// TryToSetRegOptional - sets a bit to indicate to LSRA that register
-// for a given tree node is optional for codegen purpose. If no
-// register is allocated to such a tree node, its parent node treats
-// it as a contained memory operand during codegen.
-//
-// Arguments:
-// tree - GenTree node
+//-----------------------------------------------------------------------
+// PreferredRegOptionalOperand: returns one of the operands of given
+// binary oper that is to be preferred for marking as reg optional.
//
-// Returns
-// None
-//
-// Note: Right now a tree node is marked as reg optional only
-// if is it a GT_LCL_VAR. This routine needs to be modified if
-// in future if lower/codegen needs to support other tree node
-// types.
-void Lowering::TryToSetRegOptional(GenTree* tree)
-{
- if (tree->OperGet() == GT_LCL_VAR)
- {
- tree->gtLsraInfo.regOptional = true;
- }
-}
-
-// ------------------------------------------------------------------
-// SetRegOptionalBinOp - Indicates which of the operands of a bin-op
-// register requirement is optional. Xarch instruction set allows
-// either of op1 or op2 of binary operation (e.g. add, mul etc) to be
-// a memory operand. This routine provides info to register allocator
-// which of its operands optionally require a register. Lsra might not
-// allocate a register to RefTypeUse positions of such operands if it
-// is beneficial. In such a case codegen will treat them as memory
-// operands.
+// Since only one of op1 or op2 can be a memory operand on xarch, only
+// one of them have to be marked as reg optional. Since Lower doesn't
+// know apriori which of op1 or op2 is not likely to get a register, it
+// has to make a guess. This routine encapsulates heuristics that
+// guess whether it is likely to be beneficial to mark op1 or op2 as
+// reg optional.
+//
//
// Arguments:
-// tree - Gentree of a bininary operation.
+// tree - a binary-op tree node that is either commutative
+// or a compare oper.
//
-// Returns
-// None.
-//
-// Note: On xarch at most only one of the operands will be marked as
-// reg optional, even when both operands could be considered register
-// optional.
-void Lowering::SetRegOptionalForBinOp(GenTree* tree)
+// Returns:
+// Returns op1 or op2 of tree node that is preferred for
+// marking as reg optional.
+//
+// Note: if the tree oper is neither commutative nor a compare oper
+// then only op2 can be reg optional on xarch and hence no need to
+// call this routine.
+GenTree* Lowering::PreferredRegOptionalOperand(GenTree* tree)
{
assert(GenTree::OperIsBinary(tree->OperGet()));
+ assert(tree->OperIsCommutative() || tree->OperIsCompare());
GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
+ GenTree* preferredOp = nullptr;
- if (tree->TypeGet() == op2->TypeGet())
+ // This routine uses the following heuristics:
+ //
+ // a) If both are tracked locals, marking the one with lower weighted
+ // ref count as reg-optional would likely be beneficial as it has
+ // higher probability of not getting a register.
+ //
+ // b) op1 = tracked local and op2 = untracked local: LSRA creates two
+ // ref positions for op2: a def and use position. op2's def position
+ // requires a reg and it is allocated a reg by spilling another
+ // interval (if required) and that could be even op1. For this reason
+ // it is beneficial to mark op1 as reg optional.
+ //
+ // TODO: It is not always mandatory for a def position of an untracked
+ // local to be allocated a register if it is on rhs of an assignment
+ // and its use position is reg-optional and has not been assigned a
+ // register. Reg optional def positions is currently not yet supported.
+ //
+ // c) op1 = untracked local and op2 = tracked local: marking op1 as
+ // reg optional is beneficial, since its use position is less likely
+ // to get a register.
+ //
+ // d) If both are untracked locals (i.e. treated like tree temps by
+ // LSRA): though either of them could be marked as reg optional,
+ // marking op1 as reg optional is likely to be beneficial because
+ // while allocating op2's def position, there is a possibility of
+ // spilling op1's def and in which case op1 is treated as contained
+ // memory operand rather than requiring to reload.
+ //
+ // e) If only one of them is a local var, prefer to mark it as
+ // reg-optional. This is heuristic is based on the results
+ // obtained against CQ perf benchmarks.
+ //
+ // f) If neither of them are local vars (i.e. tree temps), prefer to
+ // mark op1 as reg optional for the same reason as mentioned in (d) above.
+ if (op1->OperGet() == GT_LCL_VAR &&
+ op2->OperGet() == GT_LCL_VAR)
{
- TryToSetRegOptional(op2);
- }
+ LclVarDsc* v1 = comp->lvaTable + op1->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* v2 = comp->lvaTable + op2->AsLclVarCommon()->GetLclNum();
- if (!op2->IsRegOptional() &&
- tree->OperIsCommutative() &&
- tree->TypeGet() == op1->TypeGet())
+ if (v1->lvTracked && v2->lvTracked)
+ {
+ // Both are tracked locals. The one with lower weight is less likely
+ // to get a register and hence beneficial to mark the one with lower
+ // weight as reg optional.
+ if (v1->lvRefCntWtd < v2->lvRefCntWtd)
+ {
+ preferredOp = op1;
+ }
+ else
+ {
+ preferredOp = op2;
+ }
+ }
+ else if (v2->lvTracked)
+ {
+ // v1 is an untracked lcl and it is use position is less likely to
+ // get a register.
+ preferredOp = op1;
+ }
+ else if (v1->lvTracked)
+ {
+ // v2 is an untracked lcl and its def position always
+ // needs a reg. Hence it is better to mark v1 as
+ // reg optional.
+ preferredOp = op1;
+ }
+ else
+ {
+ preferredOp = op1;;
+ }
+ }
+ else if (op1->OperGet() == GT_LCL_VAR)
+ {
+ preferredOp = op1;
+ }
+ else if (op2->OperGet() == GT_LCL_VAR)
{
- TryToSetRegOptional(op1);
+ preferredOp = op2;
}
+ else
+ {
+ // Neither of the operands is a local, prefer marking
+ // operand that is evaluated first as reg optional
+ // since its use position is less likely to get a register.
+ bool reverseOps = ((tree->gtFlags & GTF_REVERSE_OPS) != 0);
+ preferredOp = reverseOps ? op2 : op1;
+ }
+
+ return preferredOp;
}
#endif // _TARGET_XARCH_
newRP->registerAssignment = mask;
newRP->setMultiRegIdx(0);
+ newRP->setAllocateIfProfitable(0);
associateRefPosWithInterval(newRP);
newRP->registerAssignment = mask;
newRP->setMultiRegIdx(multiRegIdx);
+ newRP->setAllocateIfProfitable(0);
associateRefPosWithInterval(newRP);
pos->isLocalDefUse = true;
bool isLastUse = ((tree->gtFlags & GTF_VAR_DEATH) != 0);
pos->lastUse = isLastUse;
+ pos->setAllocateIfProfitable(tree->IsRegOptional());
DBEXEC(VERBOSE, pos->dump());
return;
}
prefSrcInterval = i;
}
+ bool regOptionalAtUse = useNode->IsRegOptional();
bool isLastUse = true;
if (isCandidateLocalRef(useNode))
{
else
{
// For non-localVar uses we record nothing,
- // as nothing needs to be written back to the tree)
+ // as nothing needs to be written back to the tree.
useNode = nullptr;
}
pos->delayRegFree = true;
}
- if (isLastUse) pos->lastUse = true;
+ if (isLastUse)
+ {
+ pos->lastUse = true;
+ }
+
+ if (regOptionalAtUse)
+ {
+ pos->setAllocateIfProfitable(1);
+ }
}
JITDUMP("\n");
else
{
isBetterLocation = (nextLocation > farthestLocation);
+
+ if (nextLocation > farthestLocation)
+ {
+ isBetterLocation = true;
+ }
+ else if (nextLocation == farthestLocation)
+ {
+ // Both weight and distance are equal.
+ // Prefer that ref position which is marked both reload and
+ // allocate if profitable. These ref positions don't need
+ // need to be spilled as they are already in memory and
+ // codegen considers them as contained memory operands.
+ isBetterLocation = (recentAssignedRef != nullptr) &&
+ recentAssignedRef->reload &&
+ recentAssignedRef->AllocateIfProfitable();
+ }
+ else
+ {
+ isBetterLocation = false;
+ }
}
}
void
LinearScan::updateMaxSpill(RefPosition* refPosition)
{
- if (refPosition->spillAfter || refPosition->reload)
+ RefType refType = refPosition->refType;
+
+ if (refPosition->spillAfter ||
+ refPosition->reload ||
+ (refPosition->AllocateIfProfitable() && refPosition->assignedReg() == REG_NA))
{
Interval* interval = refPosition->getInterval();
if (!interval->isLocalVar)
// 8-byte non-GC items, and 16-byte or 32-byte SIMD vectors.
// LSRA is agnostic to those choices but needs
// to know what they are here.
- RefType refType = refPosition->refType;
var_types typ;
+
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
if ((refType == RefTypeUpperVectorSaveDef) || (refType == RefTypeUpperVectorSaveUse))
{
GenTreePtr treeNode = refPosition->treeNode;
if (treeNode == nullptr)
{
- assert(RefTypeIsUse(refPosition->refType));
+ assert(RefTypeIsUse(refType));
treeNode = interval->firstRefPosition->treeNode;
}
assert(treeNode != nullptr);
assert(currentSpill[typ] > 0);
currentSpill[typ]--;
}
+ else if (refPosition->AllocateIfProfitable() &&
+ refPosition->assignedReg() == REG_NA)
+ {
+ // A spill temp not getting reloaded into a reg because it is
+ // marked as allocate if profitable and getting used from its
+ // memory location. To properly account max spill for typ we
+ // decrement spill count.
+ assert(RefTypeIsUse(refType));
+ assert(currentSpill[typ] > 0);
+ currentSpill[typ]--;
+ }
JITDUMP(" Max spill for %s is %d\n", varTypeName(typ), maxSpill[typ]);
}
}
if (treeNode == nullptr)
{
// This is either a use, a dead def, or a field of a struct
- Interval * interval = currentRefPosition->getInterval();
+ Interval* interval = currentRefPosition->getInterval();
assert(currentRefPosition->refType == RefTypeUse ||
currentRefPosition->registerAssignment == RBM_NONE ||
interval->isStructField);
+
// TODO-Review: Need to handle the case where any of the struct fields
// are reloaded/spilled at this use
assert(!interval->isStructField ||
(currentRefPosition->reload == false &&
currentRefPosition->spillAfter == false));
+
if (interval->isLocalVar && !interval->isStructField)
{
- LclVarDsc * varDsc = interval->getLocalVar(compiler);
+ LclVarDsc* varDsc = interval->getLocalVar(compiler);
// This must be a dead definition. We need to mark the lclVar
// so that it's not considered a candidate for lvRegister, as
assert(currentRefPosition->refType == RefTypeDef);
varDsc->lvRegNum = REG_STK;
}
+
JITDUMP("No tree node to write back to\n");
continue;
}
if (INDEBUG(alwaysInsertReload() ||)
nextRefPosition->assignedReg() != currentRefPosition->assignedReg())
{
- insertCopyOrReload(treeNode, currentRefPosition->getMultiRegIdx(), nextRefPosition);
+ if (nextRefPosition->assignedReg() != REG_NA)
+ {
+ insertCopyOrReload(treeNode, currentRefPosition->getMultiRegIdx(), nextRefPosition);
+ }
+ else
+ {
+ assert(nextRefPosition->AllocateIfProfitable());
+
+ // In case of tree temps, if def is spilled and use didn't
+ // get a register, set a flag on tree node to be treated as
+ // contained at the point of its use.
+ if (currentRefPosition->spillAfter &&
+ currentRefPosition->refType == RefTypeDef &&
+ nextRefPosition->refType == RefTypeUse)
+ {
+ assert(nextRefPosition->treeNode == nullptr);
+ treeNode->gtFlags |= GTF_NOREG_AT_USE;
+ }
+ }
}
}
) && !AllocateIfProfitable();
}
- // Returns true whether this ref position is to be allocated
- // a reg only if it is profitable. Currently these are the
+ // Indicates whether this ref position is to be allocated
+ // a reg only if profitable. Currently these are the
// ref positions that lower/codegen has indicated as reg
// optional and is considered a contained memory operand if
// no reg is allocated.
+ unsigned allocRegIfProfitable : 1;
+
+ void setAllocateIfProfitable(unsigned val)
+ {
+ allocRegIfProfitable = val;
+ }
+
+ // Returns true whether this ref position is to be allocated
+ // a reg only if it is profitable.
bool AllocateIfProfitable()
{
// TODO-CQ: Right now if a ref position is marked as
// copyreg or movereg, then it is not treated as
// 'allocate if profitable'. This is an implementation
// limitation that needs to be addressed.
- return (refType == RefTypeUse) &&
- !copyReg &&
- !moveReg &&
- (treeNode != nullptr) &&
- treeNode->IsRegOptional();
+ return allocRegIfProfitable &&
+ !copyReg &&
+ !moveReg;
}
// Used by RefTypeDef/Use positions of a multi-reg call node.
#include "pal/context.h"
#include "pal.h"
#include <dlfcn.h>
-#include <exception>
-
+
#if HAVE_LIBUNWIND_H
#ifndef __linux__
#define UNW_LOCAL_ONLY
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
-#include <utility>
+
+// Define the std::move so that we don't have to include the <utility> header
+// which on some platforms pulls in STL stuff that collides with PAL stuff.
+// The std::move is needed to enable using move constructor and assignment operator
+// for PAL_SEHException.
+namespace std
+{
+ template<typename T>
+ struct remove_reference
+ {
+ typedef T type;
+ };
+
+ template<typename T>
+ struct remove_reference<T&>
+ {
+ typedef T type;
+ };
+
+ template<typename T>
+ struct remove_reference<T&&>
+ {
+ typedef T type;
+ };
+
+ template<class T> inline
+ typename remove_reference<T>::type&& move(T&& arg)
+ { // forward arg as movable
+ return ((typename remove_reference<T>::type&&)arg);
+ }
+}
using namespace CorUnix;
#endif // !DACCESS_COMPILE
-PTR_VOID EEJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken EEJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
SUPPORTS_DAC;
} CONTRACTL_END;
- return GetCodeHeader(MethodToken)->GetGCInfo();
+ // The JIT-ed code always has the current version of GCInfo
+ return{ GetCodeHeader(MethodToken)->GetGCInfo(), GCINFO_VERSION };
}
// creates an enumeration and returns the number of EH clauses
#endif // #ifndef DACCESS_COMPILE
-PTR_VOID NativeImageJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken NativeImageJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
PTR_VOID pUnwindData = GetUnwindDataBlob(baseAddress, pRuntimeFunction, &nUnwindDataSize);
// GCInfo immediatelly follows unwind data
- return dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ // GCInfo from an NGEN-ed image is always the current version
+ return{ dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize, GCINFO_VERSION };
}
unsigned NativeImageJitManager::InitializeEHEnumeration(const METHODTOKEN& MethodToken, EH_CLAUSE_ENUMERATOR* pEnumState)
//
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
return NativeUnwindInfoLookupTable::GetMethodDesc(m_pNgenLayout, GetRuntimeFunction(), m_ModuleBase);
}
-PTR_VOID MethodIterator::GetGCInfo()
+GCInfoToken MethodIterator::GetGCInfoToken()
{
LIMITED_METHOD_CONTRACT;
// get the gc info from the RT function
SIZE_T size;
PTR_VOID pUnwindData = GetUnwindDataBlob(m_ModuleBase, GetRuntimeFunction(), &size);
- return (PTR_VOID)((PTR_BYTE)pUnwindData + size);
+ PTR_VOID gcInfo = (PTR_VOID)((PTR_BYTE)pUnwindData + size);
+ // MethodIterator is used to iterate over methods of an NgenImage.
+ // So, GcInfo version is always current
+ return{ gcInfo, GCINFO_VERSION };
}
TADDR MethodIterator::GetMethodStartAddress()
methodRegionInfo->hotStartAddress = GetMethodStartAddress();
methodRegionInfo->coldStartAddress = GetMethodColdStartAddress();
-
- methodRegionInfo->hotSize = ExecutionManager::GetNativeImageJitManager()->GetCodeManager()->GetFunctionSize(GetGCInfo());
+ GCInfoToken gcInfoToken = GetGCInfoToken();
+ methodRegionInfo->hotSize = ExecutionManager::GetNativeImageJitManager()->GetCodeManager()->GetFunctionSize(gcInfoToken);
methodRegionInfo->coldSize = 0;
if (methodRegionInfo->coldStartAddress != NULL)
return dac_cast<PTR_Module>(MethodToken.m_pRangeSection->pHeapListOrZapModule)->GetReadyToRunInfo();
}
+UINT32 ReadyToRunJitManager::JitTokenToGCInfoVersion(const METHODTOKEN& MethodToken)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ HOST_NOCALLS;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ READYTORUN_HEADER * header = JitTokenToReadyToRunInfo(MethodToken)->GetImage()->GetReadyToRunHeader();
+ UINT32 gcInfoVersion = header->MajorVersion;
+
+ // Currently there's only one version of GCInfo.
+ _ASSERTE(gcInfoVersion == GCINFO_VERSION);
+
+ return gcInfoVersion;
+}
+
PTR_RUNTIME_FUNCTION ReadyToRunJitManager::JitTokenToRuntimeFunction(const METHODTOKEN& MethodToken)
{
CONTRACTL {
RUNTIME_FUNCTION__BeginAddress(dac_cast<PTR_RUNTIME_FUNCTION>(MethodToken.m_pCodeHeader));
}
-PTR_VOID ReadyToRunJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken ReadyToRunJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
PTR_VOID pUnwindData = GetUnwindDataBlob(baseAddress, pRuntimeFunction, &nUnwindDataSize);
// GCInfo immediatelly follows unwind data
- return dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ PTR_BYTE gcInfo = dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ UINT32 gcInfoVersion = JitTokenToGCInfoVersion(MethodToken);
+
+ return{ gcInfo, gcInfoVersion };
}
unsigned ReadyToRunJitManager::InitializeEHEnumeration(const METHODTOKEN& MethodToken, EH_CLAUSE_ENUMERATOR* pEnumState)
// READYTORUN: FUTURE: Hot-cold spliting
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
}
An IJitManager knows about which method bodies live in each RangeSection.
It can handle methods of one given CodeType. It can map a method body to
a MethodDesc. It knows where the GCInfo about the method lives.
- Today, we have 2 IJitManagers viz.
+ Today, we have three IJitManagers viz.
1. EEJitManager for JITcompiled code generated by clrjit.dll
2. NativeImageJitManager for ngenned code.
+ 3. ReadyToRunJitManager for version resiliant ReadyToRun code
An ICodeManager knows how to crack a specific format of GCInfo. There is
a default format (handled by ExecutionManager::GetDefaultCodeManager())
#include "debuginfostore.h"
#include "shash.h"
#include "pedecoder.h"
+#include "gcinfo.h"
class MethodDesc;
class ICorJitCompiler;
// Method header which exists just before the code.
// Every IJitManager could have its own format for the header.
// Today CodeHeader is used by the EEJitManager.
+// The GCInfo version is always current GCINFO_VERSION in this header.
#ifdef USE_INDIRECT_CODEHEADER
typedef DPTR(struct _hpRealCodeHdr) PTR_RealCodeHeader;
CrawlFrame *pCf)=0;
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken)=0;
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken)=0;
+ PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken)
+ {
+ return GetGCInfoToken(MethodToken).Info;
+ }
TADDR JitTokenToModuleBase(const METHODTOKEN& MethodToken);
virtual TypeHandle ResolveEHClause(EE_ILEXCEPTION_CLAUSE* pEHClause,
CrawlFrame *pCf);
#endif // !DACCESS_COMPILE
- PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#endif // !CROSSGEN_COMPILE
#if !defined DACCESS_COMPILE && !defined CROSSGEN_COMPILE
void RemoveJitData(CodeHeader * pCHdr, size_t GCinfo_len, size_t EHinfo_len);
} CONTRACTL_END;
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
}
CrawlFrame *pCf);
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#if defined(WIN64EXCEPTIONS)
virtual PTR_RUNTIME_FUNCTION LazyGetFunctionEntry(EECodeInfo * pCodeInfo);
virtual PCODE GetCodeAddressForRelOffset(const METHODTOKEN& MethodToken, DWORD relOffset);
static ReadyToRunInfo * JitTokenToReadyToRunInfo(const METHODTOKEN& MethodToken);
+ static UINT32 JitTokenToGCInfoVersion(const METHODTOKEN& MethodToken);
+
static PTR_RUNTIME_FUNCTION JitTokenToRuntimeFunction(const METHODTOKEN& MethodToken);
virtual TADDR JitTokenToStartAddress(const METHODTOKEN& MethodToken);
CrawlFrame *pCf);
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#if defined(WIN64EXCEPTIONS)
virtual PTR_RUNTIME_FUNCTION LazyGetFunctionEntry(EECodeInfo * pCodeInfo);
return m_relOffset;
}
- PTR_VOID GetGCInfo()
+ GCInfoToken GetGCInfoToken()
{
WRAPPER_NO_CONTRACT;
- return GetJitManager()->GetGCInfo(GetMethodToken());
+ return GetJitManager()->GetGCInfoToken(GetMethodToken());
+ }
+
+ PTR_VOID GetGCInfo()
+ {
+ return GetGCInfoToken().Info;
}
void GetMethodRegionInfo(IJitManager::MethodRegionInfo *methodRegionInfo)
//
// MethodIterator class is used to iterate all the methods in an ngen image.
// It will match and report hot (and cold, if any) sections of a method at the same time.
-//
+// GcInfo version is always current
class MethodIterator
{
public:
BOOL Next();
PTR_MethodDesc GetMethodDesc();
- PTR_VOID GetGCInfo();
+ GCInfoToken GetGCInfoToken();
TADDR GetMethodStartAddress();
TADDR GetMethodColdStartAddress();
ULONG GetHotCodeSize();
_ASSERTE(codeInfo.GetRelOffset() == 0);
ICodeManager* codeMan = codeInfo.GetCodeManager();
- BYTE* table = (BYTE*) codeInfo.GetGCInfo();
+ GCInfoToken table = codeInfo.GetGCInfoToken();
unsigned methodSize = (unsigned)codeMan->GetFunctionSize(table);
- GCDump gcDump;
+ GCDump gcDump(table.Version);
+ PTR_CBYTE gcInfo = PTR_CBYTE(table.Info);
gcDump.gcPrintf = printfToDbgOut;
InfoHdr header;
printfToDbgOut ("Method info block:\n");
-
- table += gcDump.DumpInfoHdr(table, &header, &methodSize, 0);
+ gcInfo += gcDump.DumpInfoHdr(gcInfo, &header, &methodSize, 0);
printfToDbgOut ("\n");
printfToDbgOut ("Pointer table:\n");
- table += gcDump.DumpGCTable(table, header, methodSize, 0);
+ gcInfo += gcDump.DumpGCTable(gcInfo, header, methodSize, 0);
}
void DumpGCInfoMD(size_t method)
return 0;
EECodeInfo codeInfo(methodStart);
-
- PTR_VOID methodInfo = codeInfo.GetGCInfo();
-
- return codeInfo.GetCodeManager()->GetFunctionSize(methodInfo);
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ return codeInfo.GetCodeManager()->GetFunctionSize(gcInfoToken);
}
#endif //!DACCESS_COMPILE
#define RETURN_ADDR_OFFS 1 // in DWORDS
-#include "gcinfo.h"
-
#ifdef USE_GC_INFO_DECODER
#include "gcinfodecoder.h"
#endif
// GCInfo for old method
GcInfoDecoder oldGcDecoder(
- dac_cast<PTR_CBYTE>(pOldCodeInfo->GetGCInfo()),
+ pOldCodeInfo->GetGCInfoToken(),
GcInfoDecoderFlags(DECODE_SECURITY_OBJECT | DECODE_PSP_SYM | DECODE_EDIT_AND_CONTINUE),
0 // Instruction offset (not needed)
);
// GCInfo for new method
GcInfoDecoder newGcDecoder(
- dac_cast<PTR_CBYTE>(pNewCodeInfo->GetGCInfo()),
+ pNewCodeInfo->GetGCInfoToken(),
GcInfoDecoderFlags(DECODE_SECURITY_OBJECT | DECODE_PSP_SYM | DECODE_EDIT_AND_CONTINUE),
0 // Instruction offset (not needed)
);
GC_NOTRIGGER;
} CONTRACTL_END;
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
+
GcInfoDecoder gcInfoDecoder(
- dac_cast<PTR_CBYTE>(pCodeInfo->GetGCInfo()),
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
dwRelOffset
);
*/
unsigned EECodeManager::FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr)
+ GCInfoToken gcInfoToken)
{
#ifndef DACCESS_COMPILE
- BYTE* gcInfoAddr = (BYTE*) methodInfoPtr;
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_FOR_RANGES_CALLBACK,
0);
methodName, curOffs));
#endif
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCodeInfo->GetGCInfo());
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
#if defined(STRESS_HEAP) && defined(PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
#ifdef USE_GC_INFO_DECODER
if (flags & ActiveStackFrame)
{
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
curOffs
);
{
// This must be the offset after a call
#ifdef _DEBUG
- GcInfoDecoder _safePointDecoder(gcInfoAddr, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder _safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
_ASSERTE(_safePointDecoder.IsSafePoint(curOffs));
#endif
flags &= ~((unsigned)ActiveStackFrame);
if (flags & ActiveStackFrame)
{
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
curOffs
);
// We've been given an override offset for GC Info
#ifdef _DEBUG
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
curOffs
);
unsigned relOffset = pCF->GetRelOffset();
CodeManState* pState = pCF->GetCodeManState();
- PTR_VOID methodInfoPtr = pJitMan->GetGCInfo(methodToken);
+ GCInfoToken gcInfoToken = pJitMan->GetGCInfoToken(methodToken);
_ASSERTE(sizeof(CodeManStateBuf) <= sizeof(pState->stateBuf));
CodeManStateBuf * stateBuf = (CodeManStateBuf*)pState->stateBuf;
/* Extract the necessary information from the info block header */
- stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(methodInfoPtr, // <TODO>truncation</TODO>
+ stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(gcInfoToken.Info, // <TODO>truncation</TODO>
relOffset,
&stateBuf->hdrInfoBody);
}
#elif defined(USE_GC_INFO_DECODER) && !defined(CROSSGEN_COMPILE)
- BYTE* gcInfoAddr = (BYTE*) methodInfoPtr;
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_SECURITY_OBJECT,
0
);
}
// On x86 the generic param context parameter is never this.
#elif defined(USE_GC_INFO_DECODER)
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_GENERICS_INST_CONTEXT),
0
);
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_PSP_SYM | DECODE_GENERICS_INST_CONTEXT),
0
);
_ASSERTE(sizeof(CodeManStateBuf) <= sizeof(pState->stateBuf));
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
unsigned relOffset = pCodeInfo->GetRelOffset();
#if defined(_TARGET_X86_)
/* Extract the necessary information from the info block header */
hdrInfo * info = &stateBuf->hdrInfoBody;
- stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(methodInfoPtr, // <TODO>truncation</TODO>
+ stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(gcInfoToken.Info, // <TODO>truncation</TODO>
relOffset,
info);
}
else
{
- PTR_CBYTE table = PTR_CBYTE(methodInfoPtr) + stateBuf->hdrInfoSize;
+ PTR_CBYTE table = PTR_CBYTE(gcInfoToken.Info) + stateBuf->hdrInfoSize;
unsigned argSize = GetPushedArgSize(info, table, relOffset);
return PVOID(SIZE_T(pContext->Esp + argSize + info->gsCookieOffset));
}
#elif defined(USE_GC_INFO_DECODER) && !defined(CROSSGEN_COMPILE)
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
-
if (pCodeInfo->IsFunclet())
{
return NULL;
}
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_GS_COOKIE,
0
);
*
* Returns the size of a given function.
*/
-size_t EECodeManager::GetFunctionSize(PTR_VOID methodInfoPtr)
+size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken)
{
CONTRACTL {
NOTHROW;
#if defined(_TARGET_X86_)
hdrInfo info;
+ PTR_VOID methodInfoPtr = gcInfoToken.Info;
crackMethodInfoHdr(methodInfoPtr, 0, &info);
return info.methodSize;
#elif defined(USE_GC_INFO_DECODER)
- PTR_BYTE gcInfoAddr = PTR_BYTE(methodInfoPtr);
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
gcCover->methodRegion = methodRegionInfo;
gcCover->codeMan = pCodeInfo->GetCodeManager();
- gcCover->gcInfo = pCodeInfo->GetGCInfo();
+ gcCover->gcInfoToken = pCodeInfo->GetGCInfoToken();
gcCover->callerThread = 0;
gcCover->doingEpilogChecks = true;
private:
ICodeManager *m_pCodeManager;
- LPVOID m_pvGCInfo;
+ GCInfoToken m_pvGCTable;
BYTE *m_codeStart;
BYTE *m_codeEnd;
BYTE *m_curFuncletEnd;
unsigned ofsLastInterruptible = m_pCodeManager->FindEndOfLastInterruptibleRegion(
static_cast<unsigned int>(pCurFunclet - m_codeStart),
static_cast<unsigned int>(m_curFuncletEnd - m_codeStart),
- m_pvGCInfo);
+ m_pvGCTable);
if (ofsLastInterruptible)
{
public:
- GCCoverageRangeEnumerator (ICodeManager *pCodeManager, LPVOID pvGCInfo, BYTE *codeStart, SIZE_T codeSize)
+ GCCoverageRangeEnumerator (ICodeManager *pCodeManager, GCInfoToken pvGCTable, BYTE *codeStart, SIZE_T codeSize)
{
m_pCodeManager = pCodeManager;
- m_pvGCInfo = pvGCInfo;
+ m_pvGCTable = pvGCTable;
m_codeStart = codeStart;
m_codeEnd = codeStart + codeSize;
m_nextFunclet = codeStart;
#ifdef _TARGET_AMD64_
- GCCoverageRangeEnumerator rangeEnum(codeMan, gcInfo, codeStart, codeSize);
+ GCCoverageRangeEnumerator rangeEnum(codeMan, gcInfoToken, codeStart, codeSize);
- GcInfoDecoder safePointDecoder((const BYTE*)gcInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
bool fSawPossibleSwitch = false;
#endif
#ifdef _TARGET_X86_
// we will whack every instruction in the prolog and epilog to make certain
// our unwinding logic works there.
- if (codeMan->IsInPrologOrEpilog((cur - codeStart) + (DWORD)regionOffsetAdj, gcInfo, NULL)) {
+ if (codeMan->IsInPrologOrEpilog((cur - codeStart) + (DWORD)regionOffsetAdj, gcInfoToken.Info, NULL)) {
*cur = INTERRUPT_INSTR;
}
#endif
}
}
- GcInfoDecoder safePointDecoder((const BYTE*)gcInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
assert(methodRegion.hotSize > 0);
/* are we in a prolog or epilog? If so just test the unwind logic
but don't actually do a GC since the prolog and epilog are not
GC safe points */
- if (gcCover->codeMan->IsInPrologOrEpilog(offset, gcCover->gcInfo, NULL))
+ if (gcCover->codeMan->IsInPrologOrEpilog(offset, gcCover->gcInfoToken.Info, NULL))
{
// We are not at a GC safe point so we can't Suspend EE (Suspend EE will yield to GC).
// But we still have to update the GC Stress instruction. We do it directly without suspending
// Following 6 variables are for prolog / epilog walking coverage
ICodeManager* codeMan; // CodeMan for this method
- void* gcInfo; // gcInfo for this method
+ GCInfoToken gcInfoToken; // gcInfo for this method
Thread* callerThread; // Thread associated with context callerRegs
T_CONTEXT callerRegs; // register state when method was entered
#else // USE_GC_INFO_DECODER
- GcInfoDecoder gcInfoDecoder((PTR_CBYTE)pCF->GetGCInfo(),
+ GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(),
DECODE_PROLOG_LENGTH,
0);
UINT32 prologLength = gcInfoDecoder.GetPrologSize();
// the end is exclusive). Return -1 if no such point exists.
unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs)
{
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo());
- GcInfoDecoder gcInfoDecoder(gcInfoAddr, DECODE_FOR_RANGES_CALLBACK, 0);
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
+ GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK, 0);
FindFirstInterruptiblePointState state;
state.offs = offs;
#if defined(WIN64EXCEPTIONS)
if (pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting())
{
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo());
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
#include "common.h"
#include "gcinfodecoder.h"
-
#ifdef USE_GC_INFO_DECODER
#ifndef CHECK_APP_DOMAIN
GcInfoDecoder::GcInfoDecoder(
- PTR_CBYTE gcInfoAddr,
+ GCInfoToken gcInfoToken,
GcInfoDecoderFlags flags,
UINT32 breakOffset
)
- : m_Reader( gcInfoAddr
+ : m_Reader(dac_cast<PTR_CBYTE>(gcInfoToken.Info)
#ifdef VERIFY_GCINFO
+ sizeof(size_t)
#endif
, m_IsInterruptible(false)
#ifdef _DEBUG
, m_Flags( flags )
- , m_GcInfoAddress(gcInfoAddr)
+ , m_GcInfoAddress(dac_cast<PTR_CBYTE>(gcInfoToken.Info))
+ , m_Version(gcInfoToken.Version)
#endif
#ifdef VERIFY_GCINFO
- , m_DbgDecoder(gcInfoAddr+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[3])<<24)+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[2])<<16)+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[1])<<8)+
+ , m_DbgDecoder(dac_cast<PTR_CBYTE>(gcInfoToken.Info) +
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[3])<<24)+
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[2])<<16)+
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[1])<<8)+
((PTR_BYTE)(TADDR)gcInfoAddr)[0],
flags, breakOffset)
#endif
return &codeInfo;
}
+ GCInfoToken GetGCInfoToken()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(isFrameless);
+ return codeInfo.GetGCInfoToken();
+ }
+
PTR_VOID GetGCInfo()
{
LIMITED_METHOD_DAC_CONTRACT;