summaryrefslogtreecommitdiff
path: root/src/jit/morph.cpp
diff options
context:
space:
mode:
authorCarol Eidt <carol.eidt@microsoft.com>2019-03-28 11:23:10 -0700
committerGitHub <noreply@github.com>2019-03-28 11:23:10 -0700
commit3d4a1d5cea0ae71eed1482990ce6e575049829d8 (patch)
treefd3ea00fbd67bec2b6f6ec0966ef2bd34ea0c01d /src/jit/morph.cpp
parenta32f7e6b176fc18973581d48d919112d66e321aa (diff)
downloadcoreclr-3d4a1d5cea0ae71eed1482990ce6e575049829d8.tar.gz
coreclr-3d4a1d5cea0ae71eed1482990ce6e575049829d8.tar.bz2
coreclr-3d4a1d5cea0ae71eed1482990ce6e575049829d8.zip
Struct & SIMD improvements (#22255)
* [WIP] Struct & SIMD improvements - Enable CSE of struct values when handle is available (and add code to get the handle of HW SIMD types) - Don't require block nodes for SIMD assignments - Don't set `GTF_GLOB_REF` on `GT_OBJ` if it is local - Set `lvRegStruct` on promoted SIMD fields - Add tests for #19910 (fixed with this PR) and #3539 & #19438 (fixed with #21314) - Additional cleanup Fix #19910
Diffstat (limited to 'src/jit/morph.cpp')
-rw-r--r--src/jit/morph.cpp147
1 files changed, 56 insertions, 91 deletions
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index fb6b3f4417..78f43eb101 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -2153,16 +2153,10 @@ GenTree* Compiler::fgMakeTmpArgNode(fgArgTabEntry* curArgTabEntry)
// Create an Obj of the temp to use it as a call argument.
arg = gtNewObjNode(lvaGetStruct(tmpVarNum), arg);
-
- // TODO-1stClassStructs: We should not need to set the GTF_DONT_CSE flag here;
- // this is only to preserve former behavior (though some CSE'ing of struct
- // values can be pessimizing, so enabling this may require some additional tuning).
- arg->gtFlags |= GTF_DONT_CSE;
}
#else
// Always create an Obj of the temp to use it as a call argument.
arg = gtNewObjNode(lvaGetStruct(tmpVarNum), arg);
- arg->gtFlags |= GTF_DONT_CSE;
#endif // !_TARGET_ARM64_
#endif // FEATURE_MULTIREG_ARGS
}
@@ -6615,14 +6609,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
GenTree* res = fgMorphSmpOp(tree);
- // If we have a struct type, this node would previously have been under a GT_ADDR,
- // and therefore would have been marked GTF_DONT_CSE.
- // TODO-1stClassStructs: revisit this.
- if ((res->TypeGet() == TYP_STRUCT) && !objIsLocal)
- {
- res->gtFlags |= GTF_DONT_CSE;
- }
-
if (fldOffset == 0 && res->OperGet() == GT_IND)
{
GenTree* addr = res->gtOp.gtOp1;
@@ -9000,24 +8986,8 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
bool isCopyBlock = asg->OperIsCopyBlkOp();
bool isInitBlock = !isCopyBlock;
- unsigned size;
+ unsigned size = 0;
CORINFO_CLASS_HANDLE clsHnd = NO_CLASS_HANDLE;
-#ifdef FEATURE_SIMD
- // importer introduces cpblk nodes with src = GT_ADDR(GT_SIMD/GT_HWIntrinsic)
- // The SIMD type in question could be Vector2f which is 8-bytes in size.
- // The below check is to make sure that we don't turn that copyblk
- // into a assignment, since rationalizer logic will transform the
- // copyblk appropriately. Otherwise, the transformation made in this
- // routine will prevent rationalizer logic and we might end up with
- // GT_ADDR(GT_SIMD/GT_HWIntrinsic) node post rationalization, leading to a noway assert
- // in codegen.
- // TODO-1stClassStructs: This is here to preserve old behavior.
- // It should be eliminated.
- if (src->OperIsSimdOrHWintrinsic())
- {
- return nullptr;
- }
-#endif
if (dest->gtEffectiveVal()->OperIsBlk())
{
@@ -9037,24 +9007,43 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
{
// Is this an enregisterable struct that is already a simple assignment?
// This can happen if we are re-morphing.
- if ((dest->OperGet() == GT_IND) && (dest->TypeGet() != TYP_STRUCT) && isCopyBlock)
+ // Note that we won't do this straightaway if this is a SIMD type, since it
+ // may be a promoted lclVar (sometimes we promote the individual float fields of
+ // fixed-size SIMD).
+ if (dest->OperGet() == GT_IND)
{
- return tree;
+ noway_assert(asgType != TYP_STRUCT);
+ if (varTypeIsStruct(asgType))
+ {
+ destLclVarTree = fgIsIndirOfAddrOfLocal(dest);
+ }
+ if (isCopyBlock && destLclVarTree == nullptr && !src->OperIs(GT_LCL_VAR))
+ {
+ fgMorphBlockOperand(src, asgType, genTypeSize(asgType), false /*isDest*/);
+ return tree;
+ }
}
- noway_assert(dest->OperIsLocal());
- destLclVarTree = dest;
- destVarNum = destLclVarTree->AsLclVarCommon()->gtLclNum;
- destVarDsc = &(lvaTable[destVarNum]);
- if (isCopyBlock)
+ else
{
- clsHnd = destVarDsc->lvVerTypeInfo.GetClassHandle();
- size = info.compCompHnd->getClassSize(clsHnd);
+ noway_assert(dest->OperIsLocal());
+ destLclVarTree = dest;
}
- else
+ if (destLclVarTree != nullptr)
+ {
+ destVarNum = destLclVarTree->AsLclVarCommon()->gtLclNum;
+ destVarDsc = &(lvaTable[destVarNum]);
+ if (asgType == TYP_STRUCT)
+ {
+ clsHnd = destVarDsc->lvVerTypeInfo.GetClassHandle();
+ size = destVarDsc->lvExactSize;
+ }
+ }
+ if (asgType != TYP_STRUCT)
{
- size = destVarDsc->lvExactSize;
+ size = genTypeSize(asgType);
}
}
+ noway_assert((size != 0) || dest->OperIs(GT_DYN_BLK));
//
// See if we can do a simple transformation:
@@ -9161,7 +9150,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
// holes, whose contents could be meaningful in unsafe code. If we decide that's a valid
// concern, then we could compromise, and say that address-exposed + fields do not completely cover the
// memory of the struct prevent field-wise assignments. Same situation exists for the "src" decision.
- if (varTypeIsStruct(destLclVarTree) && (destVarDsc->lvPromoted || destVarDsc->lvIsSIMDType()))
+ if (varTypeIsStruct(destLclVarTree) && destVarDsc->lvPromoted)
{
// Let fgMorphInitBlock handle it. (Since we'll need to do field-var-wise assignments.)
return nullptr;
@@ -9256,11 +9245,9 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
else
{
// The source argument of the copyblk can potentially be accessed only through indir(addr(lclVar))
- // or indir(lclVarAddr) in rational form and liveness won't account for these uses. That said,
- // we have to mark this local as address exposed so we don't delete it as a dead store later on.
- unsigned lclVarNum = srcLclVarTree->gtLclVarCommon.gtLclNum;
- lvaTable[lclVarNum].lvAddrExposed = true;
- lvaSetVarDoNotEnregister(lclVarNum DEBUGARG(DNER_AddrExposed));
+ // or indir(lclVarAddr) so it must be on the stack.
+ unsigned lclVarNum = srcLclVarTree->gtLclVarCommon.gtLclNum;
+ lvaSetVarDoNotEnregister(lclVarNum DEBUGARG(DNER_BlockOp));
GenTree* srcAddr;
if (src == srcLclVarTree)
{
@@ -9289,9 +9276,8 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
src->SetIndirExceptionFlags(this);
}
}
- else
+ else // InitBlk
{
-// InitBlk
#if FEATURE_SIMD
if (varTypeIsSIMD(asgType))
{
@@ -10022,11 +10008,17 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
{
if (indirTree != nullptr)
{
- // We should never find a struct indirection on the lhs of an assignment.
- assert(!isDest || indirTree->OperIsBlk());
- if (!isDest && indirTree->OperIsBlk())
+ if (indirTree->OperIsBlk())
+ {
+ if (!isDest || (asgType != TYP_STRUCT))
+ {
+ (void)fgMorphBlkToInd(effectiveVal->AsBlk(), asgType);
+ }
+ }
+ else
{
- (void)fgMorphBlkToInd(effectiveVal->AsBlk(), asgType);
+ // We should never find a TYP_STRUCT GT_IND on the lhs of an assignment.
+ assert(!isDest || (asgType != TYP_STRUCT));
}
}
else
@@ -10047,14 +10039,6 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
{
gtSetObjGcInfo(newTree->AsObj());
}
- if (effectiveVal->IsLocal() && ((effectiveVal->gtFlags & GTF_GLOB_EFFECT) == 0))
- {
- // This is not necessarily a global reference, though gtNewObjNode always assumes it is.
- // TODO-1stClassStructs: This check should be done in the GenTreeObj constructor,
- // where it currently sets GTF_GLOB_EFFECT unconditionally, but it is handled
- // separately now to avoid excess diffs.
- newTree->gtFlags &= ~(GTF_GLOB_EFFECT);
- }
}
}
else
@@ -10530,11 +10514,11 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
}
}
+ var_types asgType = dest->TypeGet();
if (requiresCopyBlock)
{
- var_types asgType = dest->TypeGet();
- dest = fgMorphBlockOperand(dest, asgType, blockWidth, true /*isDest*/);
- asg->gtOp.gtOp1 = dest;
+ dest = fgMorphBlockOperand(dest, asgType, blockWidth, true /*isDest*/);
+ asg->gtOp.gtOp1 = dest;
asg->gtFlags |= (dest->gtFlags & GTF_ALL_EFFECT);
// Note that the unrolling of CopyBlk is only implemented on some platforms.
@@ -10557,21 +10541,6 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
rhs = fgMorphBlockOperand(rhs, asgType, blockWidth, false /*!isDest*/);
asg->gtOp.gtOp2 = rhs;
- // Formerly, liveness did not consider copyblk arguments of simple types as being
- // a use or def, so these variables were marked as address-exposed.
- // TODO-1stClassStructs: This should no longer be needed.
- if (srcLclNum != BAD_VAR_NUM && !varTypeIsStruct(srcLclVar))
- {
- JITDUMP("Non-struct copyBlk src V%02d is addr exposed\n", srcLclNum);
- lvaTable[srcLclNum].lvAddrExposed = true;
- }
-
- if (destLclNum != BAD_VAR_NUM && !varTypeIsStruct(destLclVar))
- {
- JITDUMP("Non-struct copyBlk dest V%02d is addr exposed\n", destLclNum);
- lvaTable[destLclNum].lvAddrExposed = true;
- }
-
goto _Done;
}
@@ -10601,7 +10570,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
else if (destDoFldAsg)
{
fieldCnt = destLclVar->lvFieldCnt;
- rhs = fgMorphBlockOperand(rhs, TYP_STRUCT, blockWidth, false /*isDest*/);
+ rhs = fgMorphBlockOperand(rhs, asgType, blockWidth, false /*isDest*/);
if (srcAddr == nullptr)
{
srcAddr = fgMorphGetStructAddr(&rhs, destLclVar->lvVerTypeInfo.GetClassHandle(), true /* rValue */);
@@ -10611,7 +10580,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
{
assert(srcDoFldAsg);
fieldCnt = srcLclVar->lvFieldCnt;
- dest = fgMorphBlockOperand(dest, TYP_STRUCT, blockWidth, true /*isDest*/);
+ dest = fgMorphBlockOperand(dest, asgType, blockWidth, true /*isDest*/);
if (dest->OperIsBlk())
{
(void)fgMorphBlkToInd(dest->AsBlk(), TYP_STRUCT);
@@ -10692,6 +10661,9 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
if (addrSpill != nullptr)
{
+ // Simplify the address if possible, and mark as DONT_CSE as needed..
+ addrSpill = fgMorphTree(addrSpill);
+
// Spill the (complex) address to a BYREF temp.
// Note, at most one address may need to be spilled.
addrSpillTemp = lvaGrabTemp(true DEBUGARG("BlockOp address local"));
@@ -10816,9 +10788,6 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree)
noway_assert(srcLclVarTree != nullptr);
src->gtFlags |= srcLclVarTree->gtFlags & ~GTF_NODE_MASK;
- // TODO-1stClassStructs: These should not need to be marked GTF_DONT_CSE,
- // but they are when they are under a GT_ADDR.
- src->gtFlags |= GTF_DONT_CSE;
}
else
{
@@ -18936,12 +18905,8 @@ bool Compiler::fgMorphCombineSIMDFieldAssignments(BasicBlock* block, GenTree* st
}
#endif
- // TODO-1stClassStructs: we should be able to simply use a GT_IND here.
- GenTree* blkNode = gtNewBlockVal(copyBlkDst, simdSize);
- blkNode->gtType = simdType;
- tree = gtNewBlkOpNode(blkNode, simdStructNode, simdSize,
- false, // not volatile
- true); // copyBlock
+ GenTree* dstNode = gtNewOperNode(GT_IND, simdType, copyBlkDst);
+ tree = gtNewAssignNode(dstNode, simdStructNode);
stmt->gtStmt.gtStmtExpr = tree;