Merge pull request #6297 from CarolEidt/MorphGenTreeRefactors
authorCarol Eidt <carol.eidt@microsoft.com>
Thu, 21 Jul 2016 04:19:14 +0000 (21:19 -0700)
committerGitHub <noreply@github.com>
Thu, 21 Jul 2016 04:19:14 +0000 (21:19 -0700)
More Struct-related Refactorings

45 files changed:
src/ToolBox/SOS/Strike/disasm.cpp
src/ToolBox/SOS/Strike/disasm.h
src/ToolBox/SOS/Strike/disasmARM.cpp
src/ToolBox/SOS/Strike/disasmARM64.cpp
src/ToolBox/SOS/Strike/exts.h
src/ToolBox/SOS/Strike/strike.cpp
src/debug/daccess/daccess.cpp
src/debug/daccess/enummem.cpp
src/debug/daccess/nidump.cpp
src/debug/daccess/request.cpp
src/gc/gc.cpp
src/gcdump/gcdump.cpp
src/gcdump/gcdumpnonx86.cpp
src/gcinfo/gcinfodumper.cpp
src/inc/eetwain.h
src/inc/gcdecoder.cpp
src/inc/gcdump.h
src/inc/gcinfo.h
src/inc/gcinfodecoder.h
src/inc/gcinfodumper.h
src/inc/gcinfotypes.h
src/jit/codegencommon.cpp
src/jit/codegeninterface.h
src/jit/codegenxarch.cpp
src/jit/emitxarch.cpp
src/jit/gcencode.cpp
src/jit/gentree.cpp
src/jit/gentree.h
src/jit/jitgcinfo.h
src/jit/lower.h
src/jit/lowerxarch.cpp
src/jit/lsra.cpp
src/jit/lsra.h
src/pal/src/exception/seh-unwind.cpp
src/pal/src/exception/seh.cpp
src/vm/codeman.cpp
src/vm/codeman.h
src/vm/debughelp.cpp
src/vm/eedbginterfaceimpl.cpp
src/vm/eetwain.cpp
src/vm/gccover.cpp
src/vm/gccover.h
src/vm/gcenv.ee.cpp
src/vm/gcinfodecoder.cpp
src/vm/stackwalk.h

index 097f4cd..e141f80 100644 (file)
@@ -9,6 +9,7 @@
 // ==--==
 
 #include "strike.h"
+#include "gcinfo.h"
 #include "util.h"
 #include <dbghelp.h>
 #include <limits.h>
@@ -1058,10 +1059,11 @@ void PrintNothing (const char *fmt, ...)
 ///
 /// 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;
@@ -1107,17 +1109,17 @@ LPCSTR AMD64Machine::s_SPName           = "RSP";
 ///
 /// 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
index 6972c39..59fc168 100644 (file)
@@ -159,7 +159,7 @@ public:
     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()  {}
@@ -225,7 +225,7 @@ public:
     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()  {}
@@ -293,7 +293,7 @@ public:
     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()  {}
@@ -357,7 +357,7 @@ public:
     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()  {}
index 80dce71..a82d4b9 100644 (file)
@@ -607,7 +607,7 @@ BOOL ARMMachine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CRO
 ///
 /// 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)
@@ -615,10 +615,10 @@ void ARMMachine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrint
         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
 }
 
index 2c581bc..4ac8c59 100644 (file)
@@ -377,16 +377,16 @@ BOOL ARM64Machine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, C
 ///
 /// 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);
 }
 
index baef6d7..36b5230 100644 (file)
@@ -23,7 +23,6 @@
 #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>
@@ -43,6 +42,8 @@
 // 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))
@@ -386,7 +387,7 @@ public:
 
     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()           {}
index 3b0086f..df4a184 100644 (file)
@@ -8017,10 +8017,10 @@ DECLARE_API(GCInfo)
 
     // 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;
 }
@@ -8101,8 +8101,8 @@ void DecodeGCTableEntry (const char *fmt, ...)
 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);
index 4f500d9..ba3995b 100644 (file)
@@ -2339,7 +2339,7 @@ namespace serialization { namespace bin {
     };
 
     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
     };
@@ -2347,7 +2347,7 @@ namespace serialization { namespace bin {
     // 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
     };
@@ -6012,7 +6012,7 @@ ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc,
         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)
index f88fb62..068c2f2 100644 (file)
@@ -979,10 +979,11 @@ HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags,
                                 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);
                                 }
                             }
index 6de9ec0..d151a54 100644 (file)
@@ -3093,7 +3093,8 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
     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)
@@ -3108,10 +3109,10 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
     {
         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
 
@@ -3119,7 +3120,7 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
 #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
 
@@ -9436,10 +9437,10 @@ void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNC
     {
         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
 
index 9e86476..62dd5f5 100644 (file)
@@ -1148,7 +1148,7 @@ ClrDataAccess::GetCodeHeaderData(CLRDATA_ADDRESS ip, struct DacpCodeHeaderData *
 
         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);
 
index ae4c557..a3792e1 100644 (file)
@@ -15342,21 +15342,6 @@ void gc_heap::gc1()
     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))
@@ -29848,6 +29833,7 @@ size_t  gc_heap::compute_in (int gen_number)
     }
 
     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]);
@@ -30020,6 +30006,8 @@ void gc_heap::compute_new_dynamic_data (int 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)
@@ -30035,6 +30023,7 @@ void gc_heap::compute_new_dynamic_data (int gen_number)
         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;
index d2fda04..1c512c8 100644 (file)
@@ -18,8 +18,9 @@
 
 
 
-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)
 {
@@ -32,7 +33,7 @@ GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool 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));
 
@@ -42,7 +43,7 @@ PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
         unsigned        count;
         int             cBytesLeft;
 
-        for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = table; 
+        for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = gcInfoBlock;
              count > 0; 
              count--, pCurPos++, cBytesLeft--)
         {
@@ -60,7 +61,7 @@ PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
         gcPrintf("| ");
     }
 
-    return  table + cDumpBytes;
+    return  gcInfoBlock + cDumpBytes;
 }
 
 /*****************************************************************************/
index 8167d3a..b6562d9 100644 (file)
@@ -78,8 +78,9 @@ PCSTR GetRegName (UINT32 regnum)
 /*****************************************************************************/
 
 
-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)
 {
@@ -270,11 +271,12 @@ BOOL StackSlotStateChangeCallback (
 }
 
     
-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
@@ -439,7 +441,7 @@ size_t      GCDump::DumpGCTable(PTR_CBYTE      table,
     UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
     gcPrintf("Code size: %x\n", cbEncodedMethodSize);
 
-    GcInfoDumper dumper(table);
+    GcInfoDumper dumper(gcInfoToken);
 
     GcInfoDumpState state;
     state.LastCodeOffset = -1;
index 432e706..4e31871 100644 (file)
@@ -21,9 +21,9 @@
 #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;
 }
@@ -492,7 +492,7 @@ GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges (
     //
     // Decode header information
     //
-    GcInfoDecoder hdrdecoder(m_pbGCInfo,
+    GcInfoDecoder hdrdecoder(m_gcTable,
                              (GcInfoDecoderFlags)(  DECODE_SECURITY_OBJECT
                                                   | DECODE_CODE_LENGTH
                                                   | DECODE_GC_LIFETIMES
@@ -617,11 +617,11 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
     //
 
 #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(&regdisp,
                     0,
                     &LivePointerCallback,
@@ -646,7 +646,7 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
     {
         BOOL fNewInterruptible = FALSE;
 
-        GcInfoDecoder decoder1(m_pbGCInfo,
+        GcInfoDecoder decoder1(m_gcTable,
                                (GcInfoDecoderFlags)(  DECODE_SECURITY_OBJECT
                                                     | DECODE_CODE_LENGTH
                                                     | DECODE_VARARG
@@ -680,7 +680,7 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
         }
 #endif
         
-        GcInfoDecoder decoder2(m_pbGCInfo,
+        GcInfoDecoder decoder2(m_gcTable,
                                (GcInfoDecoderFlags)(  DECODE_SECURITY_OBJECT
                                                     | DECODE_CODE_LENGTH
                                                     | DECODE_VARARG
index a7bab87..6e183c5 100644 (file)
@@ -30,6 +30,7 @@
 #include "corjit.h"     // For NativeVarInfo
 #include "stackwalktypes.h"
 #include "bitvector.h"
+#include "gcinfotypes.h"
 
 #if !defined(_TARGET_X86_)
 #define USE_GC_INFO_DECODER
@@ -218,7 +219,7 @@ virtual bool IsGcSafe(EECodeInfo     *pCodeInfo,
 */
 virtual unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
                                                   unsigned endOffset,
-                                                  PTR_VOID methodInfoPtr) = 0;
+                                                  GCInfoToken gcInfoToken) = 0;
 #endif // _TARGET_AMD64_ && _DEBUG
 
 /*
@@ -293,7 +294,7 @@ virtual bool IsInSynchronizedRegion(
   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)
@@ -447,7 +448,7 @@ bool IsGcSafe(  EECodeInfo     *pCodeInfo,
 virtual
 unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
                                           unsigned endOffset,
-                                          PTR_VOID methodInfoPtr);
+                                          GCInfoToken gcInfoToken);
 #endif // _TARGET_AMD64_ && _DEBUG
 
 /*
@@ -551,8 +552,7 @@ bool IsInSynchronizedRegion(
   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)
index 7472c9a..d337fae 100644 (file)
@@ -18,7 +18,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
 /* This file is shared between the VM and JIT/IL and SOS/Strike directories */
 
-#include "gcinfo.h"
+#include "gcinfotypes.h"
 
 /*****************************************************************************/
 /*
index aded6bb..cd73940 100644 (file)
@@ -17,7 +17,7 @@
 #define __GCDUMP_H__
 /*****************************************************************************/
 
-#include "gcinfo.h"     // For InfoHdr
+#include "gcinfotypes.h"     // For InfoHdr
 
 #ifndef FASTCALL
 #ifndef FEATURE_PAL
@@ -32,7 +32,8 @@ class GCDump
 {
 public:
 
-    GCDump                          (bool           encBytes     = true, 
+    GCDump                          (UINT32         gcInfoVersion,
+                                     bool           encBytes     = true, 
                                      unsigned       maxEncBytes  = 5, 
                                      bool           dumpCodeOffs = true);
 
@@ -44,7 +45,7 @@ public:
      * 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);
@@ -52,13 +53,12 @@ public:
 
     /*-------------------------------------------------------------------------
      * 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
@@ -79,6 +79,7 @@ public:
 public:
        typedef void (*printfFtn)(const char* fmt, ...);
        printfFtn gcPrintf;     
+    UINT32              gcInfoVersion;
     //-------------------------------------------------------------------------
 protected:
 
@@ -89,7 +90,7 @@ 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);
index bb80620..500e1b7 100644 (file)
@@ -8,11 +8,8 @@
 #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.
 
@@ -31,234 +28,32 @@ const unsigned  byref_OFFSET_FLAG  = 0x1;  // the offset is an interior ptr
 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_
index 52e8ed8..466ac15 100644 (file)
@@ -11,7 +11,7 @@
 #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))
@@ -433,12 +433,11 @@ public:
 
     // 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
     //------------------------------------------------------------------------
@@ -538,6 +537,7 @@ private:
 #ifdef _DEBUG
     GcInfoDecoderFlags m_Flags;
     PTR_CBYTE m_GcInfoAddress;
+    UINT32 m_Version;
 #endif
 
 #ifdef VERIFY_GCINFO
index 64801b0..296dd29 100644 (file)
@@ -18,7 +18,7 @@ class GcInfoDumper
 {
 public:
 
-    GcInfoDumper (PTR_CBYTE pbGCInfo);
+    GcInfoDumper (GCInfoToken gcInfoToken);
     ~GcInfoDumper ();
 
     // Returns TRUE to stop decoding.
@@ -80,7 +80,7 @@ private:
         UINT marked;
     };
 
-    PTR_CBYTE m_pbGCInfo;
+    GCInfoToken m_gcTable;
     UINT32 m_StackBaseRegister;
     UINT32 m_SizeOfEditAndContinuePreservedArea;
     LivePointerRecord *m_pRecords;
index a54cec3..fc624b2 100644 (file)
@@ -6,6 +6,8 @@
 #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;
@@ -84,24 +86,24 @@ inline UINT32 CeilOfLog2(size_t x)
 
 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
@@ -113,11 +115,10 @@ const char* const GcStackSlotBaseNames[] =
 };
 #endif
 
-
 enum GcSlotState
 {
-    GC_SLOT_DEAD                = 0x0,
-    GC_SLOT_LIVE                = 0x1,
+    GC_SLOT_DEAD = 0x0,
+    GC_SLOT_LIVE = 0x1,
 };
 
 struct GcStackSlot
@@ -135,6 +136,238 @@ 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)
@@ -144,7 +377,6 @@ struct GcStackSlot
 #define NO_GENERICS_INST_CONTEXT  (-1)
 #define NO_PSP_SYM                (-1)
 
-
 #if defined(_TARGET_AMD64_)
 
 #ifndef TARGET_POINTER_SIZE
index 1196e61..ffd5b70 100755 (executable)
@@ -1398,6 +1398,29 @@ regNumber           CodeGenInterface::genGetThisArgReg(GenTreePtr  call)
     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_
index cb4774d..d321b57 100644 (file)
@@ -313,6 +313,9 @@ public:
     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:
index c7fc1fd..fedee03 100755 (executable)
@@ -1341,7 +1341,7 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
         {
             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
@@ -1466,8 +1466,8 @@ void CodeGen::genCodeForBinary(GenTree* treeNode)
     // 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;
@@ -5148,7 +5148,11 @@ void CodeGen::genConsumeRegs(GenTree* tree)
 
     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());
         }
index 388a51d..632cc02 100644 (file)
@@ -2893,7 +2893,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
     {
         dblConst = src->AsDblCon();
     }
-    
+
     // find local field if any
     GenTreeLclFld* lclField = nullptr;    
     if (src->isContainedLclField())
@@ -2918,9 +2918,25 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
         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)
         {
@@ -2959,7 +2975,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
     // 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)
     {
@@ -2971,12 +2987,22 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
         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))
             {
@@ -2993,7 +3019,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
         }
         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())
@@ -3011,6 +3037,11 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
             }
         }
 
+        if (tmpDsc != nullptr)
+        {
+            emitComp->tmpRlsTemp(tmpDsc);
+        }
+
         return dst->gtRegNum;
     }
 
index c3ae12b..fb033dd 100644 (file)
@@ -21,7 +21,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
 #endif
 
-#include "gcinfo.h"
+#include "gcinfotypes.h"
 
 #ifdef JIT32_GCENCODER
 
@@ -3236,7 +3236,7 @@ unsigned            GCInfo::gcInfoBlockHdrDump(const BYTE*  table,
                                                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");
@@ -3252,7 +3252,7 @@ unsigned            GCInfo::gcDumpPtrTable(const BYTE*    table,
 {
     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);
@@ -3268,7 +3268,7 @@ void                GCInfo::gcFindPtrsInFrame(const void* infoBlock,
                                               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);
index 67440f2..5c7b49a 100644 (file)
@@ -13319,13 +13319,22 @@ GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps *pOper)
 // 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())
index b9b97ef..48a04b6 100644 (file)
@@ -495,7 +495,9 @@ public:
 
     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.
@@ -503,7 +505,7 @@ public:
 
     bool isContainedMemoryOp() const        
     { 
-        return (isContained() && isMemoryOp()) || isContainedLclVar(); 
+        return (isContained() && isMemoryOp()) || isContainedLclVar() || isContainedSpillTemp()
     }
 
     regNumber GetRegNum() const
@@ -665,11 +667,13 @@ public:
     #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
@@ -4495,6 +4499,19 @@ GenTreeBlkOp::HasGCPtr()
     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;
+}
 
 /*****************************************************************************/
 
index 480f884..e5092cf 100644 (file)
@@ -8,11 +8,11 @@
 
 #ifndef _JITGCINFO_H_
 #define _JITGCINFO_H_
-#include "gcinfo.h"
+
+#include "gcinfotypes.h"
 
 #ifndef JIT32_GCENCODER
 #include "gcinfoencoder.h"
-#include "gcinfotypes.h"
 #endif
 
 /*****************************************************************************/
index 4baeb7e..9f62978 100644 (file)
@@ -143,8 +143,62 @@ private:
     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);
index d41239c..8353f2c 100644 (file)
@@ -561,7 +561,7 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt)
                 info->srcCount = 2;
                 info->dstCount = 0;
             
-                GenTreePtr other = nullptr;
+                GenTreePtr other;
                 if (CheckImmedAndMakeContained(tree, node->gtIndex))
                 {
                     other = node->gtArrLen;
@@ -579,17 +579,17 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt)
                     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;
@@ -2087,7 +2087,8 @@ Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
             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;
@@ -2128,7 +2129,8 @@ Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
         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);
     }
 }
 
@@ -2167,7 +2169,7 @@ Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
         {
             // Mark the operand as reg optional since codegen can still 
             // generate code if op1 is on stack.
-            TryToSetRegOptional(op1);
+            SetRegOptional(op1);
         }
         break;
 
@@ -2503,7 +2505,7 @@ Lowering::TreeNodeInfoInitCast(GenTree* tree)
             {
                 // Mark castOp as reg optional to indicate codegen
                 // can still generate code if it is on stack.
-                TryToSetRegOptional(castOp);
+                SetRegOptional(castOp);
             }
         }
     }
@@ -2861,18 +2863,16 @@ void Lowering::LowerCmp(GenTreePtr tree)
         {
             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;
@@ -2950,6 +2950,7 @@ void Lowering::LowerCmp(GenTreePtr tree)
                 }
             }
         }
+
         if (op1CanBeContained)
         {
             if (op1->isMemoryOp())
@@ -2957,7 +2958,9 @@ void Lowering::LowerCmp(GenTreePtr tree)
                 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")
                 //
@@ -3083,6 +3086,7 @@ void Lowering::LowerCmp(GenTreePtr tree)
                             }
                             // Mark the 'op1' (the GT_AND) operand as contained
                             MakeSrcContained(tree, op1);
+                            op1IsMadeContained = true;
 
                             // During Codegen we will now generate "test andOp1, andOp2CnsVal"
                         }
@@ -3127,8 +3131,8 @@ void Lowering::LowerCmp(GenTreePtr tree)
                         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.
@@ -3150,6 +3154,7 @@ void Lowering::LowerCmp(GenTreePtr tree)
                                 if (castOp1->isMemoryOp())
                                 {
                                     MakeSrcContained(tree, op1);
+                                    op1IsMadeContained = true;
                                 }
                             }
                         }
@@ -3163,6 +3168,12 @@ void Lowering::LowerCmp(GenTreePtr tree)
 #endif
                     }
                 }
+
+                // If not made contained, op1 can be marked as reg-optional.
+                if (!op1IsMadeContained)
+                {
+                    SetRegOptional(op1);
+                }
             }
         }
     }
@@ -3181,12 +3192,7 @@ void Lowering::LowerCmp(GenTreePtr tree)
             // 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))
@@ -3717,6 +3723,10 @@ void Lowering::SetMulOpCounts(GenTreePtr tree)
     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
@@ -3740,26 +3750,25 @@ void Lowering::SetMulOpCounts(GenTreePtr tree)
         // 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
@@ -3770,11 +3779,12 @@ void Lowering::SetMulOpCounts(GenTreePtr tree)
         }
 
         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.
@@ -3790,17 +3800,28 @@ void Lowering::SetMulOpCounts(GenTreePtr tree)
     //
     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);
         }
     }
@@ -3869,67 +3890,128 @@ bool Lowering:: IsContainableImmed(GenTree* parentNode, GenTree* childNode)
     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_
index 9be61e4..266d68e 100644 (file)
@@ -752,6 +752,7 @@ LinearScan::newRefPosition(regNumber reg,
     newRP->registerAssignment = mask;
 
     newRP->setMultiRegIdx(0);
+    newRP->setAllocateIfProfitable(0);
 
     associateRefPosWithInterval(newRP);
 
@@ -835,6 +836,7 @@ LinearScan::newRefPosition(Interval* theInterval,
     newRP->registerAssignment = mask;
 
     newRP->setMultiRegIdx(multiRegIdx);
+    newRP->setAllocateIfProfitable(0);
 
     associateRefPosWithInterval(newRP);
 
@@ -3023,6 +3025,7 @@ LinearScan::buildRefPositionsForNode(GenTree *tree,
             pos->isLocalDefUse = true;
             bool isLastUse = ((tree->gtFlags & GTF_VAR_DEATH) != 0);
             pos->lastUse = isLastUse; 
+            pos->setAllocateIfProfitable(tree->IsRegOptional());
             DBEXEC(VERBOSE, pos->dump());
             return;
         }
@@ -3216,6 +3219,7 @@ LinearScan::buildRefPositionsForNode(GenTree *tree,
             prefSrcInterval = i;
         }
 
+        bool regOptionalAtUse = useNode->IsRegOptional();
         bool isLastUse = true;
         if (isCandidateLocalRef(useNode))
         {
@@ -3224,7 +3228,7 @@ LinearScan::buildRefPositionsForNode(GenTree *tree,
         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;
         }
 
@@ -3260,7 +3264,15 @@ LinearScan::buildRefPositionsForNode(GenTree *tree,
             pos->delayRegFree = true;
         }
 
-        if (isLastUse) pos->lastUse = true;
+        if (isLastUse)
+        {
+            pos->lastUse = true;
+        }
+
+        if (regOptionalAtUse)
+        {
+            pos->setAllocateIfProfitable(1);
+        }
     }
     JITDUMP("\n");
     
@@ -5145,6 +5157,26 @@ LinearScan::allocateBusyReg(Interval* current,
             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;
+                }
             }
         }
 
@@ -7395,7 +7427,11 @@ LinearScan::recordMaxSpill()
 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)
@@ -7406,8 +7442,8 @@ LinearScan::updateMaxSpill(RefPosition* refPosition)
             // 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))
             {
@@ -7419,7 +7455,7 @@ LinearScan::updateMaxSpill(RefPosition* refPosition)
                 GenTreePtr treeNode = refPosition->treeNode;
                 if (treeNode == nullptr)
                 {
-                    assert(RefTypeIsUse(refPosition->refType));
+                    assert(RefTypeIsUse(refType));
                     treeNode = interval->firstRefPosition->treeNode;
                 }
                 assert(treeNode != nullptr);
@@ -7451,6 +7487,17 @@ LinearScan::updateMaxSpill(RefPosition* refPosition)
                 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]);
         }
     }
@@ -7669,18 +7716,20 @@ LinearScan::resolveRegisters()
             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
@@ -7688,6 +7737,7 @@ LinearScan::resolveRegisters()
                     assert(currentRefPosition->refType == RefTypeDef);
                     varDsc->lvRegNum = REG_STK;
                 }
+
                 JITDUMP("No tree node to write back to\n");
                 continue;
             }
@@ -7784,7 +7834,25 @@ LinearScan::resolveRegisters()
                         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;
+                                }
+                            }
                         }
                     }
 
index 11af9be..9ce2bd7 100644 (file)
@@ -1372,22 +1372,29 @@ public:
                ) && !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.
index c00be51..24eebbb 100644 (file)
@@ -26,8 +26,7 @@ Abstract:
 #include "pal/context.h"
 #include "pal.h"
 #include <dlfcn.h>
-#include <exception>
-    
 #if HAVE_LIBUNWIND_H
 #ifndef __linux__
 #define UNW_LOCAL_ONLY
index 5aaa18f..5320ecd 100644 (file)
@@ -39,7 +39,37 @@ Abstract:
 #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;
 
index 7eea254..b66215f 100644 (file)
@@ -3015,7 +3015,7 @@ void * EEJitManager::allocCodeFragmentBlock(size_t blockSize, unsigned alignment
 #endif // !DACCESS_COMPILE
 
 
-PTR_VOID EEJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken EEJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
 {
     CONTRACTL {
         NOTHROW;
@@ -3024,7 +3024,8 @@ PTR_VOID EEJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
         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
@@ -5035,7 +5036,7 @@ NativeImageJitManager::NativeImageJitManager()
 
 #endif // #ifndef DACCESS_COMPILE
 
-PTR_VOID NativeImageJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken NativeImageJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
 {
     CONTRACTL {
         NOTHROW;
@@ -5060,7 +5061,8 @@ PTR_VOID NativeImageJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
     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)
@@ -5681,7 +5683,7 @@ void NativeImageJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& Method
     //
 
     methodRegionInfo->hotStartAddress  = JitTokenToStartAddress(MethodToken);
-    methodRegionInfo->hotSize          = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+    methodRegionInfo->hotSize          = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
     methodRegionInfo->coldStartAddress = 0;
     methodRegionInfo->coldSize         = 0;
 
@@ -6274,14 +6276,17 @@ PTR_MethodDesc MethodIterator::GetMethodDesc()
     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()
@@ -6359,8 +6364,8 @@ void MethodIterator::GetMethodRegionInfo(IJitManager::MethodRegionInfo *methodRe
 
     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)
@@ -6408,6 +6413,24 @@ ReadyToRunInfo * ReadyToRunJitManager::JitTokenToReadyToRunInfo(const METHODTOKE
     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 {
@@ -6433,7 +6456,7 @@ TADDR ReadyToRunJitManager::JitTokenToStartAddress(const METHODTOKEN& MethodToke
         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;
@@ -6458,7 +6481,10 @@ PTR_VOID ReadyToRunJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
     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)
@@ -6863,7 +6889,7 @@ void ReadyToRunJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& MethodT
     // 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;
 }
index 855c151..ae86a25 100644 (file)
@@ -24,9 +24,10 @@ Abstract:
     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())
@@ -66,6 +67,7 @@ Abstract:
 #include "debuginfostore.h"
 #include "shash.h"
 #include "pedecoder.h"
+#include "gcinfo.h"
 
 class MethodDesc;
 class ICorJitCompiler;
@@ -113,6 +115,7 @@ enum StubCodeBlockKind : int
 // 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;
@@ -735,7 +738,11 @@ public:
                                         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);
 
@@ -965,7 +972,7 @@ public:
     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);
@@ -1486,7 +1493,7 @@ inline void EEJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& MethodTo
     } CONTRACTL_END;
 
     methodRegionInfo->hotStartAddress  = JitTokenToStartAddress(MethodToken);
-    methodRegionInfo->hotSize          = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+    methodRegionInfo->hotSize          = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
     methodRegionInfo->coldStartAddress = 0;
     methodRegionInfo->coldSize         = 0;
 }
@@ -1543,7 +1550,7 @@ public:
                                         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);
@@ -1638,6 +1645,8 @@ public:
     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);
@@ -1653,7 +1662,7 @@ public:
                                         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);
@@ -1754,10 +1763,15 @@ public:
         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)
@@ -1824,7 +1838,7 @@ class MethodSectionIterator;
 //
 //  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:
@@ -1852,7 +1866,7 @@ private:
     BOOL Next();
 
     PTR_MethodDesc GetMethodDesc();
-    PTR_VOID GetGCInfo();
+    GCInfoToken GetGCInfoToken();
     TADDR GetMethodStartAddress();
     TADDR GetMethodColdStartAddress();
     ULONG GetHotCodeSize();
index 7e4455a..df76945 100644 (file)
@@ -1198,24 +1198,24 @@ void DumpGCInfo(MethodDesc* method)
     _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)
index 53cb288..93decc9 100644 (file)
@@ -665,10 +665,8 @@ size_t EEDbgInterfaceImpl::GetFunctionSize(MethodDesc *pFD)
         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
 
index 69eb177..82b76f6 100644 (file)
@@ -11,8 +11,6 @@
 
 #define RETURN_ADDR_OFFS        1       // in DWORDS
 
-#include "gcinfo.h"
-
 #ifdef USE_GC_INFO_DECODER
 #include "gcinfodecoder.h"
 #endif
@@ -942,14 +940,14 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT         pCtx,
 
     // 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)
         );
@@ -1437,8 +1435,10 @@ bool EECodeManager::IsGcSafe( EECodeInfo     *pCodeInfo,
         GC_NOTRIGGER;
     } CONTRACTL_END;
 
+    GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
+
     GcInfoDecoder gcInfoDecoder(
-            dac_cast<PTR_CBYTE>(pCodeInfo->GetGCInfo()),
+            gcInfoToken,
             DECODE_INTERRUPTIBILITY,
             dwRelOffset
             );
@@ -1502,13 +1502,11 @@ bool FindEndOfLastInterruptibleRegionCB (
 */
 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);
 
@@ -4758,7 +4756,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
         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
@@ -4770,7 +4768,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
     if (flags & ActiveStackFrame)
     {
         GcInfoDecoder _gcInfoDecoder(
-                            gcInfoAddr,
+                            gcInfoToken,
                             DECODE_INTERRUPTIBILITY,
                             curOffs
                             );
@@ -4778,7 +4776,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
         {
             // 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);
@@ -4791,7 +4789,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
     if (flags & ActiveStackFrame)
     {
         GcInfoDecoder _gcInfoDecoder(
-                            gcInfoAddr,
+                            gcInfoToken,
                             DECODE_INTERRUPTIBILITY,
                             curOffs
                             );
@@ -4839,7 +4837,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
         // We've been given an override offset for GC Info
 #ifdef _DEBUG
         GcInfoDecoder _gcInfoDecoder(
-                            gcInfoAddr,
+                            gcInfoToken,
                             DECODE_CODE_LENGTH,
                             0
                             );
@@ -4884,7 +4882,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
 
 
     GcInfoDecoder gcInfoDecoder(
-                        gcInfoAddr,
+                        gcInfoToken,
                         GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
                         curOffs
                         );
@@ -5027,7 +5025,7 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
     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));
 
@@ -5035,7 +5033,7 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
     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);
 
@@ -5051,10 +5049,8 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
     }
 #elif defined(USE_GC_INFO_DECODER) && !defined(CROSSGEN_COMPILE)
 
-    BYTE* gcInfoAddr = (BYTE*) methodInfoPtr;
-
     GcInfoDecoder gcInfoDecoder(
-            gcInfoAddr,
+            gcInfoToken,
             DECODE_SECURITY_OBJECT,
             0
             );
@@ -5270,11 +5266,10 @@ GenericParamContextType EECodeManager::GetParamContextType(PREGDISPLAY     pCont
     }
     // 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
             );
@@ -5363,11 +5358,10 @@ PTR_VOID EECodeManager::GetExactGenericsToken(SIZE_T          baseStackSlot,
     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
             );
@@ -5432,7 +5426,7 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY     pContext,
 
     _ASSERTE(sizeof(CodeManStateBuf) <= sizeof(pState->stateBuf));
 
-    PTR_VOID       methodInfoPtr = pCodeInfo->GetGCInfo();
+    GCInfoToken    gcInfoToken = pCodeInfo->GetGCInfoToken();
     unsigned       relOffset = pCodeInfo->GetRelOffset();
 
 #if defined(_TARGET_X86_)
@@ -5440,7 +5434,7 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY     pContext,
     
     /* 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);
 
@@ -5459,22 +5453,20 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY     pContext,
     }
     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
             );
@@ -5567,7 +5559,7 @@ bool  EECodeManager::IsInSynchronizedRegion(
  *
  *  Returns the size of a given function.
  */
-size_t EECodeManager::GetFunctionSize(PTR_VOID  methodInfoPtr)
+size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken)
 {
     CONTRACTL {
         NOTHROW;
@@ -5577,16 +5569,15 @@ size_t EECodeManager::GetFunctionSize(PTR_VOID  methodInfoPtr)
 
 #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
             );
index 2dd7c9e..3220cdd 100644 (file)
@@ -79,7 +79,7 @@ void SetupAndSprinkleBreakpoints(
 
     gcCover->methodRegion      = methodRegionInfo;
     gcCover->codeMan           = pCodeInfo->GetCodeManager();
-    gcCover->gcInfo            = pCodeInfo->GetGCInfo();
+    gcCover->gcInfoToken           = pCodeInfo->GetGCInfoToken();
     gcCover->callerThread      = 0;
     gcCover->doingEpilogChecks = true;    
 
@@ -286,7 +286,7 @@ class GCCoverageRangeEnumerator
 private:
 
     ICodeManager *m_pCodeManager;
-    LPVOID m_pvGCInfo;
+    GCInfoToken m_pvGCTable;
     BYTE *m_codeStart;
     BYTE *m_codeEnd;
     BYTE *m_curFuncletEnd;
@@ -318,7 +318,7 @@ private:
         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)
         {
@@ -332,10 +332,10 @@ private:
 
 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;
@@ -458,9 +458,9 @@ void GCCoverageInfo::SprinkleBreakpoints(
 
 
 #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
 
@@ -582,7 +582,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
 #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
@@ -632,7 +632,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
         }
     }
 
-    GcInfoDecoder safePointDecoder((const BYTE*)gcInfo, (GcInfoDecoderFlags)0, 0);
+    GcInfoDecoder safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
     
     assert(methodRegion.hotSize > 0);
 
@@ -1469,7 +1469,7 @@ void DoGcStress (PCONTEXT regs, MethodDesc *pMD)
     /* 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
index 0308f47..b2dedef 100644 (file)
@@ -26,7 +26,7 @@ public:
 
         // 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
index aa1edbb..5ecae4f 100644 (file)
@@ -132,7 +132,7 @@ inline bool SafeToReportGenericParamContext(CrawlFrame* pCF)
 
 #else  // USE_GC_INFO_DECODER
 
-    GcInfoDecoder gcInfoDecoder((PTR_CBYTE)pCF->GetGCInfo(), 
+    GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(),
             DECODE_PROLOG_LENGTH, 
             0);
     UINT32 prologLength = gcInfoDecoder.GetPrologSize();
@@ -199,8 +199,8 @@ bool FindFirstInterruptiblePointStateCB(
 // 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;
@@ -281,9 +281,9 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
 #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
                                     );
index 5a3bbd9..b2f5640 100644 (file)
@@ -6,7 +6,6 @@
 #include "common.h"
 #include "gcinfodecoder.h"
 
-
 #ifdef USE_GC_INFO_DECODER
 
 #ifndef CHECK_APP_DOMAIN
@@ -84,11 +83,11 @@ bool GcInfoDecoder::SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset,
 
 
 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            
@@ -97,13 +96,14 @@ GcInfoDecoder::GcInfoDecoder(
             , 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
index 3d6dbdc..004d673 100644 (file)
@@ -324,6 +324,13 @@ public:
         return &codeInfo;
     }
 
+    GCInfoToken GetGCInfoToken()
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        _ASSERTE(isFrameless);
+        return codeInfo.GetGCInfoToken();
+    }
+
     PTR_VOID GetGCInfo()
     {
         LIMITED_METHOD_DAC_CONTRACT;