-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Change fgMorphMultiOp to fgMorphHWIntrinsic #103732
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
Changes from all commits
951a90f
b7c3bcc
f9e91d1
60ee682
67edbcf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20350,6 +20350,12 @@ bool GenTree::isCommutativeHWIntrinsic() const | |
{ | ||
return !varTypeIsFloating(node->GetSimdBaseType()); | ||
} | ||
|
||
case NI_AVX512F_Add: | ||
case NI_AVX512F_Multiply: | ||
{ | ||
return node->GetOperandCount() == 2; | ||
} | ||
#endif // TARGET_XARCH | ||
|
||
default: | ||
|
@@ -28536,6 +28542,110 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const | |
} | ||
} | ||
|
||
//--------------------------------------------------------------------------------------- | ||
// GenTreeHWIntrinsic::ShouldConstantProp: | ||
// Determines if a given operand should be constant propagated | ||
// | ||
// Arguments | ||
// operand - The operand to check | ||
// vecCon - The vector constant to check | ||
// | ||
// Return Value | ||
// true if operand should be constant propagated, otherwise false | ||
// | ||
// Remarks | ||
// This method takes the operand and vector constant given that assertion prop | ||
// may be checking if the underlying constant for a lcl_var should be propagated | ||
// | ||
bool GenTreeHWIntrinsic::ShouldConstantProp(GenTree* operand, GenTreeVecCon* vecCon) | ||
{ | ||
assert(HWIntrinsicInfo::CanBenefitFromConstantProp(gtHWIntrinsicId)); | ||
|
||
var_types simdBaseType = GetSimdBaseType(); | ||
|
||
switch (gtHWIntrinsicId) | ||
{ | ||
#if defined(TARGET_ARM64) | ||
case NI_Vector64_op_Equality: | ||
case NI_Vector64_op_Inequality: | ||
#endif // TARGET_ARM64 | ||
case NI_Vector128_op_Equality: | ||
case NI_Vector128_op_Inequality: | ||
#if defined(TARGET_XARCH) | ||
case NI_Vector256_op_Equality: | ||
case NI_Vector256_op_Inequality: | ||
case NI_Vector512_op_Equality: | ||
case NI_Vector512_op_Inequality: | ||
#endif // TARGET_XARCH | ||
{ | ||
// We can optimize when the constant is zero, but only | ||
// for non floating-point since +0.0 == -0.0. | ||
return vecCon->IsZero() && !varTypeIsFloating(simdBaseType); | ||
} | ||
|
||
#if defined(TARGET_ARM64) | ||
case NI_AdvSimd_CompareEqual: | ||
case NI_AdvSimd_Arm64_CompareEqual: | ||
case NI_AdvSimd_Arm64_CompareEqualScalar: | ||
{ | ||
// We can optimize when the constant is zero due to a | ||
// specialized encoding for the instruction | ||
return vecCon->IsZero(); | ||
} | ||
|
||
case NI_AdvSimd_CompareGreaterThan: | ||
case NI_AdvSimd_CompareGreaterThanOrEqual: | ||
case NI_AdvSimd_Arm64_CompareGreaterThan: | ||
case NI_AdvSimd_Arm64_CompareGreaterThanOrEqual: | ||
case NI_AdvSimd_Arm64_CompareGreaterThanScalar: | ||
case NI_AdvSimd_Arm64_CompareGreaterThanOrEqualScalar: | ||
{ | ||
// We can optimize when the constant is zero, but only | ||
// for signed types, due to a specialized encoding for | ||
// the instruction | ||
return vecCon->IsZero() && !varTypeIsUnsigned(simdBaseType); | ||
} | ||
#endif // TARGET_ARM64 | ||
|
||
#if defined(TARGET_XARCH) | ||
case NI_SSE41_Insert: | ||
{ | ||
// We can optimize for float when the constant is zero | ||
// due to a specialized encoding for the instruction | ||
return (simdBaseType == TYP_FLOAT) && vecCon->IsZero(); | ||
} | ||
|
||
case NI_EVEX_CompareEqualMask: | ||
case NI_EVEX_CompareNotEqualMask: | ||
{ | ||
// We can optimize when the constant is zero, but only | ||
// for non floating-point since +0.0 == -0.0 | ||
return vecCon->IsZero() && !varTypeIsFloating(simdBaseType); | ||
} | ||
#endif // TARGET_XARCH | ||
|
||
case NI_Vector128_Shuffle: | ||
#if defined(TARGET_XARCH) | ||
case NI_Vector256_Shuffle: | ||
case NI_Vector512_Shuffle: | ||
#elif defined(TARGET_ARM64) | ||
case NI_Vector64_Shuffle: | ||
#endif | ||
{ | ||
// The shuffle indices need to be constant so we can preserve | ||
// the node as a hwintrinsic instead of rewriting as a user call. | ||
assert(GetOperandCount() == 2); | ||
return IsUserCall() && (operand == Op(2)); | ||
} | ||
|
||
default: | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
#endif // FEATURE_HW_INTRINSICS | ||
|
||
//--------------------------------------------------------------------------------------- | ||
|
@@ -29567,6 +29677,38 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) | |
return tree; | ||
} | ||
|
||
NamedIntrinsic ni = tree->GetHWIntrinsicId(); | ||
var_types retType = tree->TypeGet(); | ||
var_types simdBaseType = tree->GetSimdBaseType(); | ||
CorInfoType simdBaseJitType = tree->GetSimdBaseJitType(); | ||
unsigned int simdSize = tree->GetSimdSize(); | ||
|
||
simd_t simdVal = {}; | ||
|
||
if (GenTreeVecCon::IsHWIntrinsicCreateConstant<simd_t>(tree, simdVal)) | ||
{ | ||
GenTreeVecCon* vecCon = gtNewVconNode(retType); | ||
|
||
for (GenTree* arg : tree->Operands()) | ||
{ | ||
DEBUG_DESTROY_NODE(arg); | ||
} | ||
|
||
vecCon->gtSimdVal = simdVal; | ||
|
||
if (fgGlobalMorph) | ||
{ | ||
INDEBUG(vecCon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); | ||
; | ||
} | ||
|
||
if (vnStore != nullptr) | ||
{ | ||
fgValueNumberTreeConst(vecCon); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a few places doing this that can switch, I'll get them updated in a follow up PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened #103771 |
||
} | ||
return vecCon; | ||
} | ||
|
||
GenTree* op1 = nullptr; | ||
GenTree* op2 = nullptr; | ||
GenTree* op3 = nullptr; | ||
|
@@ -29598,6 +29740,86 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) | |
} | ||
} | ||
|
||
if (tree->OperIsConvertMaskToVector()) | ||
{ | ||
GenTree* op = op1; | ||
|
||
if (!op->OperIsHWIntrinsic()) | ||
{ | ||
return tree; | ||
} | ||
|
||
unsigned simdBaseTypeSize = genTypeSize(simdBaseType); | ||
GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); | ||
|
||
if (!cvtOp->OperIsConvertVectorToMask()) | ||
{ | ||
return tree; | ||
} | ||
|
||
if ((genTypeSize(cvtOp->GetSimdBaseType()) != simdBaseTypeSize)) | ||
{ | ||
// We need the operand to be the same kind of mask; otherwise | ||
// the bitwise operation can differ in how it performs | ||
return tree; | ||
} | ||
|
||
#if defined(TARGET_XARCH) | ||
GenTree* vectorNode = cvtOp->Op(1); | ||
#elif defined(TARGET_ARM64) | ||
GenTree* vectorNode = cvtOp->Op(2); | ||
#else | ||
#error Unsupported platform | ||
#endif // !TARGET_XARCH && !TARGET_ARM64 | ||
|
||
DEBUG_DESTROY_NODE(op, tree); | ||
INDEBUG(vectorNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); | ||
|
||
return vectorNode; | ||
} | ||
|
||
if (tree->OperIsConvertVectorToMask()) | ||
{ | ||
GenTree* op = op1; | ||
|
||
#if defined(TARGET_ARM64) | ||
if (!op->OperIsHWIntrinsic(NI_Sve_CreateTrueMaskAll)) | ||
{ | ||
return tree; | ||
} | ||
op = op2; | ||
#endif // TARGET_ARM64 | ||
|
||
if (!op->OperIsHWIntrinsic()) | ||
{ | ||
return tree; | ||
} | ||
|
||
unsigned simdBaseTypeSize = genTypeSize(simdBaseType); | ||
GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); | ||
|
||
if (!cvtOp->OperIsConvertMaskToVector()) | ||
{ | ||
return tree; | ||
} | ||
|
||
if ((genTypeSize(cvtOp->GetSimdBaseType()) != simdBaseTypeSize)) | ||
{ | ||
// We need the operand to be the same kind of mask; otherwise | ||
// the bitwise operation can differ in how it performs | ||
return tree; | ||
} | ||
|
||
GenTree* maskNode = cvtOp->Op(1); | ||
|
||
#if defined(TARGET_ARM64) | ||
DEBUG_DESTROY_NODE(op1); | ||
#endif // TARGET_ARM64 | ||
|
||
DEBUG_DESTROY_NODE(op, tree); | ||
return maskNode; | ||
} | ||
|
||
bool isScalar = false; | ||
genTreeOps oper = tree->HWOperGet(&isScalar); | ||
|
||
|
@@ -29630,12 +29852,6 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) | |
|
||
GenTree* resultNode = tree; | ||
|
||
NamedIntrinsic ni = tree->GetHWIntrinsicId(); | ||
var_types retType = tree->TypeGet(); | ||
var_types simdBaseType = tree->GetSimdBaseType(); | ||
CorInfoType simdBaseJitType = tree->GetSimdBaseJitType(); | ||
unsigned int simdSize = tree->GetSimdSize(); | ||
|
||
if (otherNode == nullptr) | ||
{ | ||
assert(op2 == nullptr); | ||
|
Uh oh!
There was an error while loading. Please reload this page.