Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
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