Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Pull struct type info out of GenTreeObj (#21705)
Browse files Browse the repository at this point in the history
* Introduce ClassLayout

* Delete unused getStructGcPtrsFromOp

* Use ClassLayout in GenTreeObj/Blk

* Use ClassLayout in LclVarDsc

* Remove layout info from GenTreePutArgStk

* Always initialze ClassLayout GC layout

* Make ClassLayout::GetGCPtrs private

* Restore genAdjustStackForPutArgStk asserts

* Comments

* Put layout related code in new files

* More comments and small tweaks
  • Loading branch information
mikedn authored and CarolEidt committed Sep 5, 2019
1 parent 21d651b commit 9479f67
Show file tree
Hide file tree
Showing 29 changed files with 997 additions and 809 deletions.
2 changes: 2 additions & 0 deletions src/jit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set( JIT_SOURCES
assertionprop.cpp
bitset.cpp
block.cpp
layout.cpp
codegencommon.cpp
codegenlinear.cpp
compiler.cpp
Expand Down Expand Up @@ -97,6 +98,7 @@ if (WIN32)
alloc.h
arraystack.h
bitset.h
layout.h
bitsetasshortlong.h
bitsetasuint64.h
bitsetasuint64inclass.h
Expand Down
49 changes: 16 additions & 33 deletions src/jit/codegenarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)

// This GenTree node has data about GC pointers, this means we're dealing
// with CpObj.
assert(cpObjNode->gtGcPtrCount > 0);
assert(cpObjNode->GetLayout()->HasGCPtr());
#endif // DEBUG

// Consume the operands and get them into the right registers.
Expand All @@ -839,29 +839,16 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
instGen_MemoryBarrier();
}

unsigned slots = cpObjNode->gtSlots;
emitter* emit = getEmitter();

BYTE* gcPtrs = cpObjNode->gtGcPtrs;
emitter* emit = getEmitter();
ClassLayout* layout = cpObjNode->GetLayout();
unsigned slots = layout->GetSlotCount();

// If we can prove it's on the stack we don't need to use the write barrier.
emitAttr attr = EA_PTRSIZE;
if (dstOnStack)
{
for (unsigned i = 0; i < slots; ++i)
{
if (gcPtrs[i] == GCT_GCREF)
{
attr = EA_GCREF;
}
else if (gcPtrs[i] == GCT_BYREF)
{
attr = EA_BYREF;
}
else
{
attr = EA_PTRSIZE;
}
emitAttr attr = emitTypeSize(layout->GetGCPtrType(i));

emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
Expand All @@ -871,26 +858,22 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
}
else
{
unsigned gcPtrCount = cpObjNode->gtGcPtrCount;
unsigned gcPtrCount = layout->GetGCPtrCount();

unsigned i = 0;
while (i < slots)
{
switch (gcPtrs[i])
if (!layout->IsGCPtr(i))
{
emit->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
emit->emitIns_R_R_I(INS_str, EA_PTRSIZE, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
}
else
{
case TYPE_GC_NONE:
emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
emit->emitIns_R_R_I(INS_str, attr, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
break;

default:
// In the case of a GC-Pointer we'll call the ByRef write barrier helper
genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);

gcPtrCount--;
break;
genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);
gcPtrCount--;
}
++i;
}
Expand Down
64 changes: 30 additions & 34 deletions src/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2647,7 +2647,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)

// This GenTree node has data about GC pointers, this means we're dealing
// with CpObj.
assert(cpObjNode->gtGcPtrCount > 0);
assert(cpObjNode->GetLayout()->HasGCPtr());
#endif // DEBUG

// Consume the operands and get them into the right registers.
Expand All @@ -2656,7 +2656,8 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_SRC_BYREF, srcAddrType);
gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_DST_BYREF, dstAddr->TypeGet());

unsigned slots = cpObjNode->gtSlots;
ClassLayout* layout = cpObjNode->GetLayout();
unsigned slots = layout->GetSlotCount();

// Temp register(s) used to perform the sequence of loads and stores.
regNumber tmpReg = cpObjNode->ExtractTempReg();
Expand All @@ -2683,17 +2684,15 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)

emitter* emit = getEmitter();

BYTE* gcPtrs = cpObjNode->gtGcPtrs;

// If we can prove it's on the stack we don't need to use the write barrier.
if (dstOnStack)
{
unsigned i = 0;
// Check if two or more remaining slots and use a ldp/stp sequence
while (i < slots - 1)
{
emitAttr attr0 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 0]));
emitAttr attr1 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 1]));
emitAttr attr0 = emitTypeSize(layout->GetGCPtrType(i + 0));
emitAttr attr1 = emitTypeSize(layout->GetGCPtrType(i + 1));

emit->emitIns_R_R_R_I(INS_ldp, attr0, tmpReg, tmpReg2, REG_WRITE_BARRIER_SRC_BYREF, 2 * TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX, attr1);
Expand All @@ -2705,7 +2704,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
// Use a ldr/str sequence for the last remainder
if (i < slots)
{
emitAttr attr0 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 0]));
emitAttr attr0 = emitTypeSize(layout->GetGCPtrType(i + 0));

emit->emitIns_R_R_I(INS_ldr, attr0, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
Expand All @@ -2715,38 +2714,35 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
}
else
{
unsigned gcPtrCount = cpObjNode->gtGcPtrCount;
unsigned gcPtrCount = cpObjNode->GetLayout()->GetGCPtrCount();

unsigned i = 0;
while (i < slots)
{
switch (gcPtrs[i])
if (!layout->IsGCPtr(i))
{
case TYPE_GC_NONE:
// Check if the next slot's type is also TYP_GC_NONE and use ldp/stp
if ((i + 1 < slots) && (gcPtrs[i + 1] == TYPE_GC_NONE))
{
emit->emitIns_R_R_R_I(INS_ldp, EA_8BYTE, tmpReg, tmpReg2, REG_WRITE_BARRIER_SRC_BYREF,
2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX);
emit->emitIns_R_R_R_I(INS_stp, EA_8BYTE, tmpReg, tmpReg2, REG_WRITE_BARRIER_DST_BYREF,
2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX);
++i; // extra increment of i, since we are copying two items
}
else
{
emit->emitIns_R_R_I(INS_ldr, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
emit->emitIns_R_R_I(INS_str, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
}
break;

default:
// In the case of a GC-Pointer we'll call the ByRef write barrier helper
genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);

gcPtrCount--;
break;
// Check if the next slot's type is also TYP_GC_NONE and use ldp/stp
if ((i + 1 < slots) && !layout->IsGCPtr(i + 1))
{
emit->emitIns_R_R_R_I(INS_ldp, EA_8BYTE, tmpReg, tmpReg2, REG_WRITE_BARRIER_SRC_BYREF,
2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX);
emit->emitIns_R_R_R_I(INS_stp, EA_8BYTE, tmpReg, tmpReg2, REG_WRITE_BARRIER_DST_BYREF,
2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX);
++i; // extra increment of i, since we are copying two items
}
else
{
emit->emitIns_R_R_I(INS_ldr, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
emit->emitIns_R_R_I(INS_str, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
}
}
else
{
// In the case of a GC-Pointer we'll call the ByRef write barrier helper
genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);
gcPtrCount--;
}
++i;
}
Expand Down
68 changes: 23 additions & 45 deletions src/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,18 +849,10 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// the xor ensures that only one of the two is setup, not both
assert((varNode != nullptr) ^ (addrNode != nullptr));

BYTE gcPtrArray[MAX_ARG_REG_COUNT] = {}; // TYPE_GC_NONE = 0
BYTE* gcPtrs = gcPtrArray;
ClassLayout* layout;
unsigned structSize;
bool isHfa;

unsigned gcPtrCount; // The count of GC pointers in the struct
unsigned structSize;
bool isHfa;

#ifdef _TARGET_ARM_
// On ARM32, size of reference map can be larger than MAX_ARG_REG_COUNT
gcPtrs = treeNode->gtGcPtrs;
gcPtrCount = treeNode->gtNumberReferenceSlots;
#endif
// Setup the structSize, isHFa, and gcPtrCount
if (source->OperGet() == GT_LCL_VAR)
{
Expand All @@ -874,12 +866,8 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)

structSize = varDsc->lvSize(); // This yields the roundUp size, but that is fine
// as that is how much stack is allocated for this LclVar
isHfa = varDsc->lvIsHfa();
#ifdef _TARGET_ARM64_
gcPtrCount = varDsc->lvStructGcCount;
for (unsigned i = 0; i < gcPtrCount; ++i)
gcPtrs[i] = varDsc->lvGcLayout[i];
#endif // _TARGET_ARM_
isHfa = varDsc->lvIsHfa();
layout = varDsc->GetLayout();
}
else // we must have a GT_OBJ
{
Expand All @@ -889,9 +877,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// it provides (size and GC layout) even if the node wraps a lclvar. Due
// to struct reinterpretation (e.g. Unsafe.As<X, Y>) it is possible that
// the OBJ node has a different type than the lclvar.
CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass;
layout = source->AsObj()->GetLayout();

structSize = compiler->info.compCompHnd->getClassSize(objClass);
structSize = layout->GetSize();

// The codegen code below doesn't have proper support for struct sizes
// that are not multiple of the slot size. Call arg morphing handles this
Expand All @@ -908,22 +896,14 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
assert((structSize % TARGET_POINTER_SIZE) == 0);
}

isHfa = compiler->IsHfa(objClass);

#ifdef _TARGET_ARM64_
// On ARM32, Lowering places the correct GC layout information in the
// GenTreePutArgStk node and the code above already use that. On ARM64,
// this information is not available (in order to keep GenTreePutArgStk
// nodes small) and we need to retrieve it from the VM here.
gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
#endif
isHfa = compiler->IsHfa(layout->GetClassHandle());
}

// If we have an HFA we can't have any GC pointers,
// if not then the max size for the the struct is 16 bytes
if (isHfa)
{
noway_assert(gcPtrCount == 0);
noway_assert(!layout->HasGCPtr());
}
#ifdef _TARGET_ARM64_
else
Expand All @@ -945,8 +925,8 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)

while (remainingSize >= 2 * TARGET_POINTER_SIZE)
{
var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]);
var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]);
var_types type0 = layout->GetGCPtrType(nextIndex + 0);
var_types type1 = layout->GetGCPtrType(nextIndex + 1);

if (varNode != nullptr)
{
Expand Down Expand Up @@ -981,7 +961,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// str r2, [sp, #16]
while (remainingSize >= TARGET_POINTER_SIZE)
{
var_types type = compiler->getJitGCType(gcPtrs[nextIndex]);
var_types type = layout->GetGCPtrType(nextIndex);

if (varNode != nullptr)
{
Expand Down Expand Up @@ -1018,7 +998,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
{
if (remainingSize >= TARGET_POINTER_SIZE)
{
var_types nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
var_types nextType = layout->GetGCPtrType(nextIndex);
emitAttr nextAttr = emitTypeSize(nextType);
remainingSize -= TARGET_POINTER_SIZE;

Expand Down Expand Up @@ -1051,7 +1031,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
assert(varNode == nullptr);

// the left over size is smaller than a pointer and thus can never be a GC type
assert(varTypeIsGC(compiler->getJitGCType(gcPtrs[nextIndex])) == false);
assert(!layout->IsGCPtr(nextIndex));

var_types loadType = TYP_UINT;
if (loadSize == 1)
Expand Down Expand Up @@ -1224,11 +1204,6 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
// the xor ensures that only one of the two is setup, not both
assert((varNode != nullptr) ^ (addrNode != nullptr));

// Setup the structSize, isHFa, and gcPtrCount
BYTE* gcPtrs = treeNode->gtGcPtrs;
unsigned gcPtrCount = treeNode->gtNumberReferenceSlots; // The count of GC pointers in the struct
int structSize = treeNode->getArgSize();

// This is the varNum for our load operations,
// only used when we have a struct with a LclVar source
unsigned srcVarNum = BAD_VAR_NUM;
Expand Down Expand Up @@ -1263,9 +1238,12 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
assert(baseReg != addrReg);

// We don't split HFA struct
assert(!compiler->IsHfa(source->gtObj.gtClass));
assert(!compiler->IsHfa(source->AsObj()->GetLayout()->GetClassHandle()));
}

int structSize = treeNode->getArgSize();
ClassLayout* layout = source->AsObj()->GetLayout();

// Put on stack first
unsigned nextIndex = treeNode->gtNumRegs;
unsigned structOffset = nextIndex * TARGET_POINTER_SIZE;
Expand All @@ -1275,7 +1253,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
assert(remainingSize % TARGET_POINTER_SIZE == 0);
while (remainingSize > 0)
{
var_types type = compiler->getJitGCType(gcPtrs[nextIndex]);
var_types type = layout->GetGCPtrType(nextIndex);

if (varNode != nullptr)
{
Expand Down Expand Up @@ -2804,7 +2782,7 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// Must be <= 16 bytes or else it wouldn't be passed in registers, except for HFA,
// which can be bigger (and is handled above).
noway_assert(EA_SIZE_IN_BYTES(varDsc->lvSize()) <= 16);
loadType = compiler->getJitGCType(varDsc->lvGcLayout[0]);
loadType = varDsc->GetLayout()->GetGCPtrType(0);
}
else
{
Expand All @@ -2825,7 +2803,7 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// Restore the second register.
argRegNext = genRegArgNext(argReg);

loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]);
loadType = varDsc->GetLayout()->GetGCPtrType(1);
loadSize = emitActualTypeSize(loadType);
getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE);

Expand Down Expand Up @@ -2918,7 +2896,7 @@ void CodeGen::genJmpMethod(GenTree* jmp)
for (unsigned ofs = 0; ofs < maxSize; ofs += REGSIZE_BYTES)
{
unsigned idx = ofs / REGSIZE_BYTES;
loadType = compiler->getJitGCType(varDsc->lvGcLayout[idx]);
loadType = varDsc->GetLayout()->GetGCPtrType(idx);

if (varDsc->lvRegNum != argReg)
{
Expand Down Expand Up @@ -3389,7 +3367,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)

if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
{
assert(blkOp->AsObj()->gtGcPtrCount != 0);
assert(blkOp->AsObj()->GetLayout()->HasGCPtr());
genCodeForCpObj(blkOp->AsObj());
return;
}
Expand Down
Loading

0 comments on commit 9479f67

Please sign in to comment.