Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Replace 1-field structs with the field for returns.
  • Loading branch information
Sergey Andreenko committed Jun 29, 2020
commit 3965e9d3b404048c6a55f15f4102158e541a1fe1
29 changes: 24 additions & 5 deletions src/coreclr/src/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3057,12 +3057,12 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore)
assert(varDsc->lvFieldCnt == 1);
unsigned fldNum = varDsc->lvFieldLclStart;
LclVarDsc* fldDsc = comp->lvaGetDesc(fldNum);
lclStore->SetLclNum(fldNum);
lclStore->ChangeType(fldDsc->TypeGet());

JITDUMP("Replacing an independently promoted local var V%02u with its only field V%02u for the store "
"from a call [%06u]\n",
lclStore->GetLclNum(), fldNum, comp->dspTreeID(lclStore));
lclStore->SetLclNum(fldNum);
lclStore->ChangeType(fldDsc->TypeGet());
varDsc = fldDsc;
}
}
Expand Down Expand Up @@ -3238,10 +3238,13 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret)
break;

case GT_CNS_INT:
assert(retVal->TypeIs(TYP_INT));
assert(retVal->AsIntCon()->IconValue() == 0);
// When we promote LCL_VAR single fields into return
// we could have all type of constans here.
if (varTypeUsesFloatReg(nativeReturnType))
{
// Do not expect `initblock` for SIMD* types,
// only 'initobj'.
assert(retVal->AsIntCon()->IconValue() == 0);
retVal->ChangeOperConst(GT_CNS_DBL);
retVal->ChangeType(TYP_FLOAT);
retVal->AsDblCon()->gtDconVal = 0;
Expand Down Expand Up @@ -3292,8 +3295,24 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret)
}
break;

default:
#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM)
case GT_CNS_DBL:
// Currently we are not promoting structs with a single float field.
// TODO: can improve `GT_CNS_DBL` handling for supported platforms, but
// because it is only x86 nowadays it is not worth it.
unreached();
#endif

default:
assert(varTypeIsEnregisterable(retVal));
if (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(retVal))
{
GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), retVal);
ret->gtOp1 = bitcast;
BlockRange().InsertBefore(ret, bitcast);
ContainCheckBitCast(bitcast);
}
break;
}
}

Expand Down
37 changes: 33 additions & 4 deletions src/coreclr/src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11982,9 +11982,39 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)

return tree;
}
if (!compDoOldStructRetyping() && !tree->TypeIs(TYP_VOID) && op1->OperIs(GT_OBJ, GT_BLK, GT_IND))
if (!compDoOldStructRetyping() && !tree->TypeIs(TYP_VOID))
{
op1 = fgMorphRetInd(tree->AsUnOp());
if (op1->OperIs(GT_OBJ, GT_BLK, GT_IND))
{
op1 = fgMorphRetInd(tree->AsUnOp());
}
if (op1->OperIs(GT_LCL_VAR) && (genReturnBB == nullptr))
{
// With a `genReturnBB` it will be done via field by field asignment.
GenTreeLclVar* lclVar = op1->AsLclVar();
LclVarDsc* varDsc = lvaGetDesc(lclVar);
if (varDsc->CanBeReplacedWithItsField(this))
{
// We can replace the struct with its only field and allow copy propogation to replace
// return value that was written as a field.
unsigned fieldLclNum = varDsc->lvFieldLclStart;
LclVarDsc* fieldDsc = lvaGetDesc(fieldLclNum);

if (!varTypeIsSmallInt(fieldDsc->lvType))
{
// TODO: support that substitution for small types without creating `CAST` node.
// When a small struct is returned in a register higher bits could be left in undefined
// state.
JITDUMP(
"Replacing an independently promoted local var V%02u with its only field V%02u for "
"the return [%06u]\n",
lclVar->GetLclNum(), fieldLclNum, dspTreeID(tree));
lclVar->SetLclNum(fieldLclNum);
var_types fieldType = fieldDsc->lvType;
lclVar->ChangeType(fieldDsc->lvType);
}
}
}
}
break;

Expand Down Expand Up @@ -14280,13 +14310,12 @@ GenTree* Compiler::fgMorphRetInd(GenTreeUnOp* ret)
// and enregister it.
DEBUG_DESTROY_NODE(ind);
DEBUG_DESTROY_NODE(addr);

ret->gtOp1 = lclVar;
return ret->gtGetOp1();
}
else if (!varDsc->lvDoNotEnregister)
{
lvaSetVarDoNotEnregister(lclVar->GetLclNum() DEBUGARG(Compiler::DNER_VMNeedsStackAddr));
lvaSetVarDoNotEnregister(lclVar->GetLclNum() DEBUGARG(Compiler::DNER_BlockOp));
}
}
}
Expand Down