Skip to content

Commit a82432f

Browse files
committed
Checkpoint support for byref and ref fields inside VT interpreter locals
1 parent ddf5949 commit a82432f

File tree

4 files changed

+188
-53
lines changed

4 files changed

+188
-53
lines changed

src/coreclr/interpreter/CMakeLists.txt

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@ set(INTERPRETER_SOURCES
44
compileropt.cpp
55
intops.cpp
66
interpconfig.cpp
7-
eeinterp.cpp)
7+
eeinterp.cpp
8+
stackmap.cpp
9+
../../native/containers/dn-simdhash.c
10+
../../native/containers/dn-simdhash-ptr-ptr.c)
11+
12+
set_source_files_properties(
13+
../../native/containers/dn-simdhash.c
14+
../../native/containers/dn-simdhash-ptr-ptr.c
15+
PROPERTIES COMPILE_FLAGS
16+
/DNO_CONFIG_H
17+
)
818

919
set(INTERPRETER_LINK_LIBRARIES
1020
gcinfo

src/coreclr/interpreter/compiler.cpp

+78-52
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
#include "gcinfoencoder.h"
44
#include "interpreter.h"
5+
#include "stackmap.h"
56

67
#include <inttypes.h>
78

@@ -868,7 +869,7 @@ void InterpCompiler::BuildGCInfo(InterpMethod *pInterpMethod)
868869

869870
gcInfoEncoder->SetCodeLength(ConvertOffset(m_methodCodeSize));
870871

871-
uint32_t stackSlotCount = m_totalVarsStackSize / INTERP_STACK_SLOT_SIZE,
872+
uint32_t stackSlotCount = m_totalVarsStackSize / sizeof(void *),
872873
slotTableSize = stackSlotCount * sizeof(GcSlotId);
873874
// [pObjects, pByrefs]
874875
GcSlotId *slotTables[2] = {
@@ -879,86 +880,111 @@ void InterpCompiler::BuildGCInfo(InterpMethod *pInterpMethod)
879880
for (int i = 0; i < 2; i++)
880881
memset(slotTables[i], 0xFF, slotTableSize);
881882

882-
INTERP_DUMP("Allocating gcinfo slots for %u vars\n", m_varsSize);
883-
884-
// Request slot IDs for all our vars, not just IL vars
885-
for (int i = 0; i < m_varsSize; i++)
883+
auto allocateGcSlot = [&] (uint32_t offsetBytes, GcSlotFlags flags)
886884
{
887-
GcSlotId *slotTable;
888-
InterpVar *pVar = &m_pVars[i];
889-
GcSlotFlags flags;
890-
switch (pVar->interpType) {
891-
case InterpTypeO:
892-
slotTable = slotTables[0];
893-
flags = (GcSlotFlags)0;
894-
break;
895-
case InterpTypeByRef:
896-
slotTable = slotTables[1];
897-
flags = (GcSlotFlags)GC_SLOT_INTERIOR;
898-
break;
899-
default:
900-
continue;
901-
}
885+
// Select the appropriate slot table based on the flags
886+
GcSlotId *slotTable = slotTables[(flags & GC_SLOT_INTERIOR) == GC_SLOT_INTERIOR];
887+
uint32_t slotIndex = offsetBytes / sizeof(void *);
888+
bool allocateNewSlot = slotTable[slotIndex] == ((GcSlotId)-1);
902889

903-
if (pVar->global)
904-
flags = (GcSlotFlags)(flags | GC_SLOT_UNTRACKED);
905-
906-
uint32_t slotIndex = pVar->offset / INTERP_STACK_SLOT_SIZE;
907-
uint8_t allocateNewSlot = slotTable[slotIndex] == ((GcSlotId)-1);
908890
if (allocateNewSlot)
909891
{
910892
// Important to pass GC_FRAMEREG_REL, the default is broken due to GET_CALLER_SP being unimplemented
911-
slotTable[slotIndex] = gcInfoEncoder->GetStackSlotId(pVar->offset, flags, GC_FRAMEREG_REL);
893+
slotTable[slotIndex] = gcInfoEncoder->GetStackSlotId(offsetBytes, flags, GC_FRAMEREG_REL);
912894
}
913895
else
914896
{
915-
assert(!pVar->global);
897+
assert((flags & GC_SLOT_UNTRACKED) == 0);
916898
}
917899

918900
INTERP_DUMP(
919-
"%s gcinfo slot %u for %s%svar #%d at offset %d\n",
901+
"%s %s%sgcslot %u at %u\n",
920902
allocateNewSlot ? "Allocated" : "Reused",
903+
(flags & GC_SLOT_UNTRACKED) ? "global " : "",
904+
(flags & GC_SLOT_INTERIOR) ? "interior " : "",
921905
slotTable[slotIndex],
922-
pVar->global ? "global " : "",
923-
pVar->interpType == InterpTypeByRef ? "byref " : "",
924-
i, pVar->offset
906+
offsetBytes
925907
);
926-
}
927908

928-
gcInfoEncoder->FinalizeSlotIds();
909+
return slotTable[slotIndex];
910+
};
929911

930-
// Now that slot IDs are finalized we want to record live ranges for every var that isn't global
931-
for (int i = 0; i < m_varsSize; i++)
912+
auto recordLiveRange = [&] (uint32_t offsetBytes, GcSlotFlags flags, int varIndex)
932913
{
933-
InterpVar *pVar = &m_pVars[i];
934-
// Even if we have a gc slot for this offset, this var might not be an object reference
935-
if ((pVar->interpType != InterpTypeO) && (pVar->interpType != InterpTypeByRef))
936-
continue;
937-
// We don't need to report liveness ranges for untracked vars, the gc info decoder will report them
938-
// unconditionally.
914+
// Select the appropriate slot table based on the flags
915+
GcSlotId *slotTable = slotTables[(flags & GC_SLOT_INTERIOR) == GC_SLOT_INTERIOR];
916+
uint32_t slotIndex = offsetBytes / sizeof(void *);
917+
918+
InterpVar *pVar = &m_pVars[varIndex];
939919
if (pVar->global)
940-
continue;
920+
return;
941921

942-
GcSlotId *slotTable = pVar->interpType == InterpTypeByRef
943-
? slotTables[1] : slotTables[0];
944-
uint32_t slotIndex = pVar->offset / INTERP_STACK_SLOT_SIZE;
945922
GcSlotId slot = slotTable[slotIndex];
946923
assert(slot != ((GcSlotId)-1));
947924
assert(pVar->liveStart);
948925
assert(pVar->liveEnd);
949-
uint32_t startOffset = ConvertOffset(GetLiveStartOffset(i)),
950-
endOffset = ConvertOffset(GetLiveEndOffset(i));
926+
uint32_t startOffset = ConvertOffset(GetLiveStartOffset(varIndex)),
927+
endOffset = ConvertOffset(GetLiveEndOffset(varIndex));
951928
INTERP_DUMP(
952-
"Recording gcinfo slot %u live range for var #%d at offset %u: [IR_%04x - IR_%04x] [%u - %u]\n",
953-
slotTable[slotIndex], i, pVar->offset,
954-
GetLiveStartOffset(i), GetLiveEndOffset(i),
929+
"Recording slot %u (var #%d offset %u) live range [IR_%04x - IR_%04x] [%u - %u]\n",
930+
slotTable[slotIndex], varIndex, pVar->offset,
931+
GetLiveStartOffset(varIndex), GetLiveEndOffset(varIndex),
955932
startOffset, endOffset
956933
);
957934
gcInfoEncoder->SetSlotState(startOffset, slot, GC_SLOT_LIVE);
958935
gcInfoEncoder->SetSlotState(endOffset, slot, GC_SLOT_DEAD);
959-
}
936+
};
937+
938+
INTERP_DUMP("Allocating gcinfo slots for %u vars\n", m_varsSize);
960939

961-
gcInfoEncoder->Build();
940+
for (int pass = 0; pass < 2; pass++)
941+
{
942+
for (int i = 0; i < m_varsSize; i++)
943+
{
944+
InterpVar *pVar = &m_pVars[i];
945+
GcSlotFlags flags = pVar->global
946+
? (GcSlotFlags)GC_SLOT_UNTRACKED
947+
: (GcSlotFlags)0;
948+
949+
switch (pVar->interpType) {
950+
case InterpTypeO:
951+
break;
952+
case InterpTypeByRef:
953+
flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
954+
break;
955+
case InterpTypeVT:
956+
{
957+
InterpreterStackMap *stackMap = getInterpreterStackMap(m_compHnd, pVar->clsHnd);
958+
for (unsigned j = 0; j < stackMap->m_slotCount; j++)
959+
{
960+
InterpreterStackMapSlot slotInfo = stackMap->m_slots[j];
961+
unsigned fieldOffset = pVar->offset + slotInfo.m_offsetBytes;
962+
GcSlotFlags fieldFlags = (GcSlotFlags)(flags | slotInfo.m_gcSlotFlags);
963+
if (pass == 0)
964+
allocateGcSlot(fieldOffset, fieldFlags);
965+
else
966+
recordLiveRange(fieldOffset, fieldFlags, i);
967+
}
968+
969+
// Don't perform the regular allocateGcSlot call
970+
continue;
971+
}
972+
default:
973+
// Neither an object, interior pointer, or vt, so no slot needed
974+
continue;
975+
}
976+
977+
if (pass == 0)
978+
allocateGcSlot(pVar->offset, flags);
979+
else
980+
recordLiveRange(pVar->offset, flags, i);
981+
}
982+
983+
if (pass == 0)
984+
gcInfoEncoder->FinalizeSlotIds();
985+
else
986+
gcInfoEncoder->Build();
987+
}
962988

963989
// GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
964990
gcInfoEncoder->Emit();

src/coreclr/interpreter/stackmap.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include "gcinfoencoder.h" // for GcSlotFlags
5+
#include "interpreter.h"
6+
#include "stackmap.h"
7+
8+
extern "C" {
9+
#include "../../native/containers/dn-simdhash.h"
10+
#include "../../native/containers/dn-simdhash-specializations.h"
11+
12+
void assertAbort(const char* why, const char* file, unsigned line);
13+
14+
void
15+
dn_simdhash_assert_fail (const char *file, int line, const char *condition);
16+
17+
void
18+
dn_simdhash_assert_fail (const char *file, int line, const char *condition) {
19+
assertAbort(condition, file, line);
20+
}
21+
}
22+
23+
thread_local dn_simdhash_ptr_ptr_t *gSharedStackMapLookup = nullptr;
24+
25+
InterpreterStackMap* getInterpreterStackMap(ICorJitInfo *jitInfo, CORINFO_CLASS_HANDLE classHandle)
26+
{
27+
InterpreterStackMap* result = nullptr;
28+
if (!gSharedStackMapLookup)
29+
gSharedStackMapLookup = dn_simdhash_ptr_ptr_new(0, nullptr);
30+
if (!dn_simdhash_ptr_ptr_try_get_value(gSharedStackMapLookup, classHandle, (void **)&result))
31+
{
32+
result = new InterpreterStackMap(jitInfo, classHandle);
33+
dn_simdhash_ptr_ptr_try_add(gSharedStackMapLookup, classHandle, result);
34+
}
35+
return result;
36+
}
37+
38+
void InterpreterStackMap::PopulateStackMap(ICorJitInfo *jitInfo, CORINFO_CLASS_HANDLE classHandle, uint32_t offset)
39+
{
40+
unsigned size = jitInfo->getClassSize(classHandle);
41+
unsigned maxGcPtrs = size / sizeof(void *);
42+
if (maxGcPtrs < 1)
43+
return;
44+
45+
uint8_t *gcPtrs = (uint8_t *)alloca(maxGcPtrs);
46+
unsigned numGcPtrs = jitInfo->getClassGClayout(classHandle, gcPtrs),
47+
newCapacity = m_slotCount + numGcPtrs;
48+
49+
m_slots = (InterpreterStackMapSlot *)realloc(m_slots, sizeof(InterpreterStackMapSlot) * newCapacity);
50+
51+
for (unsigned i = 0; i < numGcPtrs; i++) {
52+
GcSlotFlags flags;
53+
54+
switch (gcPtrs[i]) {
55+
case TYPE_GC_NONE:
56+
case TYPE_GC_OTHER:
57+
continue;
58+
case TYPE_GC_BYREF:
59+
flags = GC_SLOT_INTERIOR;
60+
break;
61+
case TYPE_GC_REF:
62+
flags = GC_SLOT_BASE;
63+
break;
64+
default:
65+
assert(false);
66+
continue;
67+
}
68+
69+
unsigned slotOffset = (sizeof(void *) * i) + offset;
70+
m_slots[m_slotCount++] = { slotOffset, (unsigned)flags };
71+
}
72+
}

src/coreclr/interpreter/stackmap.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
struct InterpreterStackMapSlot
5+
{
6+
unsigned m_offsetBytes;
7+
unsigned m_gcSlotFlags;
8+
};
9+
10+
class InterpreterStackMap
11+
{
12+
private:
13+
void PopulateStackMap (ICorJitInfo *jitInfo, CORINFO_CLASS_HANDLE classHandle, uint32_t offset);
14+
15+
public:
16+
unsigned m_slotCount;
17+
InterpreterStackMapSlot* m_slots;
18+
19+
InterpreterStackMap (ICorJitInfo *jitInfo, CORINFO_CLASS_HANDLE classHandle)
20+
: m_slotCount(0)
21+
, m_slots(nullptr)
22+
{
23+
PopulateStackMap(jitInfo, classHandle, 0);
24+
}
25+
};
26+
27+
InterpreterStackMap* getInterpreterStackMap(ICorJitInfo *jitInfo, CORINFO_CLASS_HANDLE classHandle);

0 commit comments

Comments
 (0)