88#endif // debug_instrumented_return
99
1010#include " interpreter.h"
11+ #include " stackmap.h"
1112
1213#include < inttypes.h>
1314
@@ -832,7 +833,10 @@ void InterpCompiler::EmitCode()
832833 for (InterpInst *ins = bb->pFirstIns ; ins != NULL ; ins = ins->pNext )
833834 {
834835 if (InterpOpIsEmitNop (ins->opcode ))
836+ {
837+ ins->nativeOffset = (int32_t )(ip - m_pMethodCode);
835838 continue ;
839+ }
836840
837841 ip = EmitCodeIns (ip, ins, &relocs);
838842 }
@@ -862,25 +866,158 @@ void InterpCompiler::EmitCode()
862866 m_compHnd->setBoundaries (m_methodInfo->ftn , m_ILToNativeMapSize, m_pILToNativeMap);
863867}
864868
869+ #ifdef FEATURE_INTERPRETER
870+ class InterpGcSlotAllocator
871+ {
872+ InterpCompiler *m_compiler;
873+ InterpreterGcInfoEncoder *m_encoder;
874+ // [pObjects, pByrefs]
875+ GcSlotId *m_slotTables[2 ];
876+ unsigned m_slotTableSize;
877+
878+ #ifdef DEBUG
879+ bool m_verbose;
880+ #endif
881+
882+ GcSlotId* LocateGcSlotTableEntry (uint32_t offsetBytes, GcSlotFlags flags)
883+ {
884+ GcSlotId *slotTable = m_slotTables[(flags & GC_SLOT_INTERIOR) == GC_SLOT_INTERIOR];
885+ uint32_t slotIndex = offsetBytes / sizeof (void *);
886+ assert (slotIndex < m_slotTableSize);
887+ return &slotTable[slotIndex];
888+ }
889+
890+ public:
891+ InterpGcSlotAllocator (InterpCompiler *compiler, InterpreterGcInfoEncoder *encoder)
892+ : m_compiler(compiler)
893+ , m_encoder(encoder)
894+ , m_slotTableSize(compiler->m_totalVarsStackSize / sizeof (void *))
895+ #ifdef DEBUG
896+ , m_verbose(compiler->m_verbose)
897+ #endif
898+ {
899+ for (int i = 0 ; i < 2 ; i++)
900+ {
901+ m_slotTables[i] = new (compiler) GcSlotId[m_slotTableSize];
902+ // 0 is a valid slot id so default-initialize all the slots to 0xFFFFFFFF
903+ memset (m_slotTables[i], 0xFF , sizeof (GcSlotId) * m_slotTableSize);
904+ }
905+ }
906+
907+ void AllocateOrReuseGcSlot (uint32_t offsetBytes, GcSlotFlags flags)
908+ {
909+ GcSlotId *pSlot = LocateGcSlotTableEntry (offsetBytes, flags);
910+ bool allocateNewSlot = *pSlot == ((GcSlotId)-1 );
911+
912+ if (allocateNewSlot)
913+ {
914+ // Important to pass GC_FRAMEREG_REL, the default is broken due to GET_CALLER_SP being unimplemented
915+ *pSlot = m_encoder->GetStackSlotId (offsetBytes, flags, GC_FRAMEREG_REL);
916+ }
917+ else
918+ {
919+ assert ((flags & GC_SLOT_UNTRACKED) == 0 );
920+ }
921+
922+ INTERP_DUMP (
923+ " %s %s%sgcslot %u at %u\n " ,
924+ allocateNewSlot ? " Allocated" : " Reused" ,
925+ (flags & GC_SLOT_UNTRACKED) ? " global " : " " ,
926+ (flags & GC_SLOT_INTERIOR) ? " interior " : " " ,
927+ *pSlot,
928+ offsetBytes
929+ );
930+ }
931+
932+ void ReportLiveRange (uint32_t offsetBytes, GcSlotFlags flags, int varIndex)
933+ {
934+ GcSlotId *pSlot = LocateGcSlotTableEntry (offsetBytes, flags);
935+ assert (varIndex < m_compiler->m_varsSize );
936+
937+ InterpVar *pVar = &m_compiler->m_pVars [varIndex];
938+ if (pVar->global )
939+ return ;
940+
941+ GcSlotId slot = *pSlot;
942+ assert (slot != ((GcSlotId)-1 ));
943+ assert (pVar->liveStart );
944+ assert (pVar->liveEnd );
945+ uint32_t startOffset = m_compiler->ConvertOffset (m_compiler->GetLiveStartOffset (varIndex)),
946+ endOffset = m_compiler->ConvertOffset (m_compiler->GetLiveEndOffset (varIndex));
947+ INTERP_DUMP (
948+ " Slot %u (%s var #%d offset %u) live [IR_%04x - IR_%04x] [%u - %u]\n " ,
949+ slot, pVar->global ? " global" : " local" ,
950+ varIndex, pVar->offset ,
951+ m_compiler->GetLiveStartOffset (varIndex), m_compiler->GetLiveEndOffset (varIndex),
952+ startOffset, endOffset
953+ );
954+ m_encoder->SetSlotState (startOffset, slot, GC_SLOT_LIVE);
955+ m_encoder->SetSlotState (endOffset, slot, GC_SLOT_DEAD);
956+ }
957+ };
958+ #endif
959+
865960void InterpCompiler::BuildGCInfo (InterpMethod *pInterpMethod)
866961{
867962#ifdef FEATURE_INTERPRETER
868963 InterpIAllocator* pAllocator = new (this ) InterpIAllocator (this );
869964 InterpreterGcInfoEncoder* gcInfoEncoder = new (this ) InterpreterGcInfoEncoder (m_compHnd, m_methodInfo, pAllocator, Interp_NOMEM);
870- assert (gcInfoEncoder);
965+ InterpGcSlotAllocator slotAllocator (this , gcInfoEncoder);
966+
967+ gcInfoEncoder->SetCodeLength (ConvertOffset (m_methodCodeSize));
871968
872- gcInfoEncoder->SetCodeLength (m_methodCodeSize);
969+ INTERP_DUMP (" Allocating gcinfo slots for %u vars\n " , m_varsSize);
970+
971+ for (int pass = 0 ; pass < 2 ; pass++)
972+ {
973+ for (int i = 0 ; i < m_varsSize; i++)
974+ {
975+ InterpVar *pVar = &m_pVars[i];
976+ GcSlotFlags flags = pVar->global
977+ ? (GcSlotFlags)GC_SLOT_UNTRACKED
978+ : (GcSlotFlags)0 ;
873979
874- // TODO: Request slot IDs for all our locals before finalizing
980+ switch (pVar->interpType ) {
981+ case InterpTypeO:
982+ break ;
983+ case InterpTypeByRef:
984+ flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
985+ break ;
986+ case InterpTypeVT:
987+ {
988+ InterpreterStackMap *stackMap = GetInterpreterStackMap (m_compHnd, pVar->clsHnd );
989+ for (unsigned j = 0 ; j < stackMap->m_slotCount ; j++)
990+ {
991+ InterpreterStackMapSlot slotInfo = stackMap->m_slots [j];
992+ unsigned fieldOffset = pVar->offset + slotInfo.m_offsetBytes ;
993+ GcSlotFlags fieldFlags = (GcSlotFlags)(flags | slotInfo.m_gcSlotFlags );
994+ if (pass == 0 )
995+ slotAllocator.AllocateOrReuseGcSlot (fieldOffset, fieldFlags);
996+ else
997+ slotAllocator.ReportLiveRange (fieldOffset, fieldFlags, i);
998+ }
875999
876- gcInfoEncoder->FinalizeSlotIds ();
1000+ // Don't perform the regular allocateGcSlot call
1001+ continue ;
1002+ }
1003+ default :
1004+ // Neither an object, interior pointer, or vt, so no slot needed
1005+ continue ;
1006+ }
8771007
878- // TODO: Use finalized slot IDs to declare live ranges
1008+ if (pass == 0 )
1009+ slotAllocator.AllocateOrReuseGcSlot (pVar->offset , flags);
1010+ else
1011+ slotAllocator.ReportLiveRange (pVar->offset , flags, i);
1012+ }
8791013
880- gcInfoEncoder->Build ();
1014+ if (pass == 0 )
1015+ gcInfoEncoder->FinalizeSlotIds ();
1016+ else
1017+ gcInfoEncoder->Build ();
1018+ }
8811019
8821020 // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
883- // let's save the values anyway for debugging purposes
8841021 gcInfoEncoder->Emit ();
8851022#endif
8861023}
@@ -908,6 +1045,8 @@ int32_t* InterpCompiler::GetCode(int32_t *pCodeSize)
9081045
9091046InterpCompiler::InterpCompiler (COMP_HANDLE compHnd,
9101047 CORINFO_METHOD_INFO* methodInfo)
1048+ : m_pInitLocalsIns(nullptr )
1049+ , m_globalVarsWithRefsStackTop(0 )
9111050{
9121051 // Fill in the thread-local used for assertions
9131052 t_InterpJitInfoTls = compHnd;
@@ -948,6 +1087,7 @@ InterpMethod* InterpCompiler::CompileMethod()
9481087#endif
9491088
9501089 AllocOffsets ();
1090+ PatchInitLocals (m_methodInfo);
9511091
9521092 EmitCode ();
9531093
@@ -965,6 +1105,33 @@ InterpMethod* InterpCompiler::CompileMethod()
9651105 return CreateInterpMethod ();
9661106}
9671107
1108+ void InterpCompiler::PatchInitLocals (CORINFO_METHOD_INFO* methodInfo)
1109+ {
1110+ // We may have global vars containing managed pointers or interior pointers, so we need
1111+ // to zero the region of the stack containing global vars, not just IL locals. Now that
1112+ // offset allocation has occurred we know where the global vars end, so we can expand
1113+ // the initlocals opcode that was originally generated to also zero them.
1114+ int32_t startOffset = m_pInitLocalsIns->data [0 ];
1115+ int32_t totalSize = m_globalVarsWithRefsStackTop - startOffset;
1116+ if (totalSize > m_pInitLocalsIns->data [1 ])
1117+ {
1118+ INTERP_DUMP (
1119+ " Expanding initlocals from [%d-%d] to [%d-%d]\n " ,
1120+ startOffset, startOffset + m_pInitLocalsIns->data [1 ],
1121+ startOffset, startOffset + totalSize
1122+ );
1123+ m_pInitLocalsIns->data [1 ] = totalSize;
1124+ }
1125+ else
1126+ {
1127+ INTERP_DUMP (
1128+ " Not expanding initlocals from [%d-%d] for global vars stack top of %d\n " ,
1129+ startOffset, startOffset + m_pInitLocalsIns->data [1 ],
1130+ m_globalVarsWithRefsStackTop
1131+ );
1132+ }
1133+ }
1134+
9681135// Adds a conversion instruction for the value pointed to by sp, also updating the stack information
9691136void InterpCompiler::EmitConv (StackInfo *sp, InterpInst *prevIns, StackType type, InterpOpcode convOp)
9701137{
@@ -1936,12 +2103,13 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
19362103 AddIns (INTOP_BREAKPOINT);
19372104#endif
19382105
1939- if ((methodInfo->options & CORINFO_OPT_INIT_LOCALS) && m_ILLocalsSize > 0 )
1940- {
1941- AddIns (INTOP_INITLOCALS);
1942- m_pLastNewIns->data [0 ] = m_ILLocalsOffset;
1943- m_pLastNewIns->data [1 ] = m_ILLocalsSize;
1944- }
2106+ // We need to always generate this opcode because even if we have no IL locals, we may have
2107+ // global vars which contain managed pointers or interior pointers
2108+ m_pInitLocalsIns = AddIns (INTOP_INITLOCALS);
2109+ // if (methodInfo->options & CORINFO_OPT_INIT_LOCALS)
2110+ // FIXME: We can't currently skip zeroing locals because we don't have accurate liveness for global refs and byrefs
2111+ m_pInitLocalsIns->data [0 ] = m_ILLocalsOffset;
2112+ m_pInitLocalsIns->data [1 ] = m_ILLocalsSize;
19452113
19462114 codeEnd = m_ip + m_ILCodeSize;
19472115
0 commit comments