Skip to content

Commit

Permalink
Replace the last two SIMDIntrinsic in LIR with NamedIntrinsic and del…
Browse files Browse the repository at this point in the history
…ete GT_SIMD (#80027)

* Replace the last two SIMDIntrinsic in LIR with NamedIntrinsic and delete GT_SIMD

* Applying formatting patch

* Ensure SIMD_UpperRestore/Save is handled in gtSetEvalOrder

* Handle some asserts on Arm64
  • Loading branch information
tannergooding committed Jan 5, 2023
1 parent aea2eb7 commit 83261a9
Show file tree
Hide file tree
Showing 27 changed files with 198 additions and 691 deletions.
5 changes: 2 additions & 3 deletions docs/design/coreclr/jit/first-class-structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ Structs only appear as rvalues in the following contexts:
* As a call argument
* In this context, it must be one of: `GT_OBJ`, `GT_LCL_VAR`, `GT_LCL_FLD` or `GT_FIELD_LIST`.

* As an operand to a hardware or SIMD intrinsic (for `TYP_SIMD*` only)
* As an operand to a hardware intrinsic (for `TYP_SIMD*` only)
* In this case the struct handle is generally assumed to be unneeded, as it is captured (directly or
indirectly) in the `GT_SIMD` or `GT_HWINTRINSIC` node.
indirectly) in the `GT_HWINTRINSIC` node.
* It would simplify both the recognition and optimization of these nodes if they carried a `ClassLayout`.

After morph, a struct-typed value on the RHS of assignment is one of:
Expand All @@ -124,7 +124,6 @@ After morph, a struct-typed value on the RHS of assignment is one of:
* `GT_CALL`
* `GT_LCL_VAR`
* `GT_LCL_FLD`
* `GT_SIMD`
* `GT_OBJ` nodes can also be used as rvalues when they are call arguments
* Proposed: `GT_OBJ` nodes can be used in any context where a struct rvalue or lvalue might occur,
except after morph when the struct is independently promoted.
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/jit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ set( JIT_HEADERS
sideeffects.h
simd.h
simdashwintrinsic.h
simdintrinsiclist.h
sm.h
smallhash.h
smcommon.h
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/jit/clrjit.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
<Type Name="GenTreeOp">
<DisplayString Condition="this->gtOper==GT_ASG">{gtTreeID, d}: [[{this-&gt;gtOp1,na}={this-&gt;gtOp2,na}]</DisplayString>
<DisplayString Condition="this->gtOper==GT_CAST">{gtTreeID, d}: [[{((GenTreeCast*)this)-&gt;gtCastType,en} &lt;- {((GenTreeUnOp*)this)-&gt;gtOp1-&gt;gtType,en}]</DisplayString>
<DisplayString Condition="this->gtOper==GT_SIMD">{gtTreeID, d}: [[{((GenTreeSIMD*)this)-&gt;gtSIMDIntrinsicID,en}, {gtType,en}]</DisplayString>
<DisplayString Condition="this->gtOper==GT_HWINTRINSIC">{gtTreeID, d}: [[{((GenTreeHWIntrinsic*)this)-&gt;gtHWIntrinsicId,en}, {gtType,en}]</DisplayString>
<DisplayString>{gtTreeID, d}: [[{gtOper,en}, {gtType,en}]</DisplayString>
</Type>
Expand Down
7 changes: 3 additions & 4 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCodeForContainedCompareChain(GenTree* tree, bool* inchain, GenCondition* prevCond);
#endif
void genCodeForSelect(GenTreeOp* select);
void genIntrinsic(GenTree* treeNode);
void genIntrinsic(GenTreeIntrinsic* treeNode);
void genPutArgStk(GenTreePutArgStk* treeNode);
void genPutArgReg(GenTreeOp* tree);
#if FEATURE_ARG_SPLIT
Expand All @@ -1078,9 +1078,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#ifdef TARGET_ARM64
insOpts genGetSimdInsOpt(emitAttr size, var_types elementType);
#endif
void genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode);
void genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode);
void genSIMDIntrinsic(GenTreeSIMD* simdNode);
void genSimdUpperSave(GenTreeIntrinsic* node);
void genSimdUpperRestore(GenTreeIntrinsic* node);

// TYP_SIMD12 (i.e Vector3 of size 12 bytes) is not a hardware supported size and requires
// two reads/writes on 64-bit targets. These routines abstract reading/writing of Vector3
Expand Down
106 changes: 39 additions & 67 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5018,46 +5018,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
}

#ifdef FEATURE_SIMD

//------------------------------------------------------------------------
// genSIMDIntrinsic: Generate code for a SIMD Intrinsic. This is the main
// routine which in turn calls appropriate genSIMDIntrinsicXXX() routine.
//
// Arguments:
// simdNode - The GT_SIMD node
//
// Return Value:
// None.
//
// Notes:
// Currently, we only recognize SIMDVector<float> and SIMDVector<int>, and
// a limited set of methods.
//
// TODO-CLEANUP Merge all versions of this function and move to new file simdcodegencommon.cpp.
void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode)
{
// NYI for unsupported base types
if (!varTypeIsArithmetic(simdNode->GetSimdBaseType()))
{
noway_assert(!"SIMD intrinsic with unsupported base type.");
}

switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicUpperSave:
genSIMDIntrinsicUpperSave(simdNode);
break;

case SIMDIntrinsicUpperRestore:
genSIMDIntrinsicUpperRestore(simdNode);
break;

default:
noway_assert(!"Unimplemented SIMD intrinsic.");
unreached();
}
}

insOpts CodeGen::genGetSimdInsOpt(emitAttr size, var_types elementType)
{
assert((size == EA_16BYTE) || (size == EA_8BYTE));
Expand Down Expand Up @@ -5092,11 +5052,11 @@ insOpts CodeGen::genGetSimdInsOpt(emitAttr size, var_types elementType)
}

//-----------------------------------------------------------------------------
// genSIMDIntrinsicUpperSave: save the upper half of a TYP_SIMD16 vector to
// the given register, if any, or to memory.
// genSimdUpperSave: save the upper half of a TYP_SIMD16 vector to
// the given register, if any, or to memory.
//
// Arguments:
// simdNode - The GT_SIMD node
// node - The GT_INTRINSIC node
//
// Return Value:
// None.
Expand All @@ -5109,81 +5069,93 @@ insOpts CodeGen::genGetSimdInsOpt(emitAttr size, var_types elementType)
// In that case, this node will be marked GTF_SPILL, which will cause this method to save
// the upper half to the lclVar's home location.
//
void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode)
void CodeGen::genSimdUpperSave(GenTreeIntrinsic* node)
{
assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperSave);
assert(node->gtIntrinsicName == NI_SIMD_UpperSave);

GenTree* op1 = node->gtGetOp1();
assert(op1->IsLocal());

GenTree* op1 = simdNode->Op(1);
GenTreeLclVar* lclNode = op1->AsLclVar();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
assert(emitTypeSize(varDsc->GetRegisterType(lclNode)) == 16);

regNumber targetReg = simdNode->GetRegNum();
regNumber op1Reg = genConsumeReg(op1);
regNumber tgtReg = node->GetRegNum();
assert(tgtReg != REG_NA);

regNumber op1Reg = genConsumeReg(op1);
assert(op1Reg != REG_NA);
assert(targetReg != REG_NA);
GetEmitter()->emitIns_R_R_I_I(INS_mov, EA_8BYTE, targetReg, op1Reg, 0, 1);

if ((simdNode->gtFlags & GTF_SPILL) != 0)
GetEmitter()->emitIns_R_R_I_I(INS_mov, EA_8BYTE, tgtReg, op1Reg, 0, 1);

if ((node->gtFlags & GTF_SPILL) != 0)
{
// This is not a normal spill; we'll spill it to the lclVar location.
// The localVar must have a stack home.

unsigned varNum = lclNode->GetLclNum();
assert(varDsc->lvOnFrame);

// We want to store this to the upper 8 bytes of this localVar's home.
int offset = 8;

emitAttr attr = emitTypeSize(TYP_SIMD8);
GetEmitter()->emitIns_S_R(INS_str, attr, targetReg, varNum, offset);
GetEmitter()->emitIns_S_R(INS_str, attr, tgtReg, varNum, offset);
}
else
{
genProduceReg(simdNode);
genProduceReg(node);
}
}

//-----------------------------------------------------------------------------
// genSIMDIntrinsicUpperRestore: Restore the upper half of a TYP_SIMD16 vector to
// the given register, if any, or to memory.
// genSimdUpperRestore: Restore the upper half of a TYP_SIMD16 vector to
// the given register, if any, or to memory.
//
// Arguments:
// simdNode - The GT_SIMD node
// node - The GT_INTRINSIC node
//
// Return Value:
// None.
//
// Notes:
// For consistency with genSIMDIntrinsicUpperSave, and to ensure that lclVar nodes always
// have their home register, this node has its targetReg on the lclVar child, and its source
// on the simdNode.
// Regarding spill, please see the note above on genSIMDIntrinsicUpperSave. If we have spilled
// For consistency with genSimdUpperSave, and to ensure that lclVar nodes always
// have their home register, this node has its tgtReg on the lclVar child, and its source
// on the node.
// Regarding spill, please see the note above on genSimdUpperSave. If we have spilled
// an upper-half to the lclVar's home location, this node will be marked GTF_SPILLED.
//
void CodeGen::genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode)
void CodeGen::genSimdUpperRestore(GenTreeIntrinsic* node)
{
assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperRestore);
assert(node->gtIntrinsicName == NI_SIMD_UpperRestore);

GenTree* op1 = simdNode->Op(1);
GenTree* op1 = node->gtGetOp1();
assert(op1->IsLocal());

GenTreeLclVar* lclNode = op1->AsLclVar();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
assert(emitTypeSize(varDsc->GetRegisterType(lclNode)) == 16);

regNumber srcReg = simdNode->GetRegNum();
regNumber srcReg = node->GetRegNum();
assert(srcReg != REG_NA);

regNumber lclVarReg = genConsumeReg(lclNode);
unsigned varNum = lclNode->GetLclNum();
assert(lclVarReg != REG_NA);
assert(srcReg != REG_NA);
if (simdNode->gtFlags & GTF_SPILLED)

unsigned varNum = lclNode->GetLclNum();

if (node->gtFlags & GTF_SPILLED)
{
// The localVar must have a stack home.
assert(varDsc->lvOnFrame);

// We will load this from the upper 8 bytes of this localVar's home.
int offset = 8;

emitAttr attr = emitTypeSize(TYP_SIMD8);
GetEmitter()->emitIns_R_S(INS_ldr, attr, srcReg, varNum, offset);
}

GetEmitter()->emitIns_R_R_I_I(INS_mov, EA_8BYTE, lclVarReg, srcReg, 1, 0);
}

Expand Down
50 changes: 32 additions & 18 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
break;

case GT_INTRINSIC:
genIntrinsic(treeNode);
genIntrinsic(treeNode->AsIntrinsic());
break;

#ifdef FEATURE_SIMD
case GT_SIMD:
genSIMDIntrinsic(treeNode->AsSIMD());
break;
#endif // FEATURE_SIMD

#ifdef FEATURE_HW_INTRINSICS
case GT_HWINTRINSIC:
genHWIntrinsic(treeNode->AsHWIntrinsic());
Expand Down Expand Up @@ -662,18 +656,21 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
// Return value:
// None
//
void CodeGen::genIntrinsic(GenTree* treeNode)
void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode)
{
assert(treeNode->OperIs(GT_INTRINSIC));

// Both operand and its result must be of the same floating point type.
GenTree* srcNode = treeNode->AsOp()->gtOp1;
assert(varTypeIsFloating(srcNode));
assert(srcNode->TypeGet() == treeNode->TypeGet());
GenTree* srcNode = treeNode->gtGetOp1();

// Only a subset of functions are treated as math intrinsics.
//
switch (treeNode->AsIntrinsic()->gtIntrinsicName)
#ifdef DEBUG
if ((treeNode->gtIntrinsicName > NI_SYSTEM_MATH_START) && (treeNode->gtIntrinsicName < NI_SYSTEM_MATH_END))
{
assert(varTypeIsFloating(srcNode));
assert(srcNode->TypeGet() == treeNode->TypeGet());
}
#endif // DEBUG

// Handle intrinsics that can be implemented by target-specific instructions
switch (treeNode->gtIntrinsicName)
{
case NI_System_Math_Abs:
genConsumeOperands(treeNode->AsOp());
Expand Down Expand Up @@ -704,13 +701,13 @@ void CodeGen::genIntrinsic(GenTree* treeNode)
case NI_System_Math_Max:
genConsumeOperands(treeNode->AsOp());
GetEmitter()->emitIns_R_R_R(INS_fmax, emitActualTypeSize(treeNode), treeNode->GetRegNum(),
treeNode->gtGetOp1()->GetRegNum(), treeNode->gtGetOp2()->GetRegNum());
srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum());
break;

case NI_System_Math_Min:
genConsumeOperands(treeNode->AsOp());
GetEmitter()->emitIns_R_R_R(INS_fmin, emitActualTypeSize(treeNode), treeNode->GetRegNum(),
treeNode->gtGetOp1()->GetRegNum(), treeNode->gtGetOp2()->GetRegNum());
srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum());
break;
#endif // TARGET_ARM64

Expand All @@ -719,6 +716,23 @@ void CodeGen::genIntrinsic(GenTree* treeNode)
GetEmitter()->emitInsBinary(INS_SQRT, emitActualTypeSize(treeNode), treeNode, srcNode);
break;

#if defined(FEATURE_SIMD)
// The handling is a bit more complex so genSimdUpperSave/Restore
// handles genConsumeOperands and genProduceReg

case NI_SIMD_UpperRestore:
{
genSimdUpperRestore(treeNode);
return;
}

case NI_SIMD_UpperSave:
{
genSimdUpperSave(treeNode);
return;
}
#endif // FEATURE_SIMD

default:
assert(!"genIntrinsic: Unsupported intrinsic");
unreached();
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1717,7 +1717,7 @@ void CodeGen::genConsumeOperands(GenTreeOp* tree)
#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
//------------------------------------------------------------------------
// genConsumeOperands: Do liveness update for the operands of a multi-operand node,
// currently GT_SIMD or GT_HWINTRINSIC
// currently GT_HWINTRINSIC
//
// Arguments:
// tree - the GenTreeMultiOp whose operands will have their liveness updated.
Expand Down
Loading

0 comments on commit 83261a9

Please sign in to comment.