@@ -10938,6 +10938,20 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree, bool* optAssertionPropD
10938
10938
//
10939
10939
GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree)
10940
10940
{
10941
+ // It is important that this follows the general flow of fgMorphSmpOp
10942
+ // * Perform required preorder processing
10943
+ // * Process the operands, in order, if any
10944
+ // * Perform required postorder morphing
10945
+ // * Perform optional postorder morphing if optimizing
10946
+ //
10947
+ // It is also important that similar checks be done where relevant, so
10948
+ // if fgMorphSmpOp does a check for fgGlobalMorph or OptimizationEnabled
10949
+ // so should this method.
10950
+
10951
+ // ------------------------------------------------------------------------
10952
+ // First do any PRE-ORDER processing
10953
+ //
10954
+
10941
10955
bool allArgsAreConst = true;
10942
10956
bool canBenefitFromConstantProp = false;
10943
10957
bool hasImmediateOperand = false;
@@ -10955,6 +10969,18 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree)
10955
10969
hasImmediateOperand = true;
10956
10970
}
10957
10971
10972
+ #ifdef TARGET_XARCH
10973
+ if (intrinsicId == NI_Vector128_op_Division || intrinsicId == NI_Vector256_op_Division)
10974
+ {
10975
+ fgAddCodeRef(compCurBB, SCK_DIV_BY_ZERO);
10976
+ fgAddCodeRef(compCurBB, SCK_OVERFLOW);
10977
+ }
10978
+ #endif // TARGET_XARCH
10979
+
10980
+ // ------------------------------------------------------------------------
10981
+ // Process the operands, if any
10982
+ //
10983
+
10958
10984
for (GenTree** use : tree->UseEdges())
10959
10985
{
10960
10986
*use = fgMorphTree(*use);
@@ -11004,102 +11030,143 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree)
11004
11030
tree->AddAllEffectsFlags(operand);
11005
11031
}
11006
11032
11007
- #ifdef TARGET_XARCH
11008
- if (intrinsicId == NI_Vector128_op_Division || intrinsicId == NI_Vector256_op_Division)
11033
+ // ------------------------------------------------------------------------
11034
+ // Now do POST-ORDER processing
11035
+ //
11036
+
11037
+ var_types retType = tree->TypeGet();
11038
+ CorInfoType simdBaseJitType = tree->GetSimdBaseJitType();
11039
+ var_types simdBaseType = tree->GetSimdBaseType();
11040
+ unsigned simdSize = tree->GetSimdSize();
11041
+
11042
+ // Try to fold it, maybe we get lucky,
11043
+ GenTree* morphedTree = gtFoldExpr(tree);
11044
+
11045
+ if ((morphedTree != tree) || !morphedTree->OperIsHWIntrinsic())
11009
11046
{
11010
- fgAddCodeRef(compCurBB, SCK_DIV_BY_ZERO );
11011
- fgAddCodeRef(compCurBB, SCK_OVERFLOW );
11047
+ assert(!fgIsCommaThrow(morphedTree) );
11048
+ morphedTree->SetMorphed(this );
11012
11049
}
11013
- #endif // TARGET_XARCH
11014
-
11015
- if (opts.OptimizationEnabled())
11050
+ else
11016
11051
{
11017
- var_types retType = tree->TypeGet();
11018
- CorInfoType simdBaseJitType = tree->GetSimdBaseJitType();
11019
- var_types simdBaseType = tree->GetSimdBaseType();
11020
- unsigned simdSize = tree->GetSimdSize();
11021
-
11022
- if (tree->isCommutativeHWIntrinsic())
11052
+ if (allArgsAreConst && tree->IsVectorCreate())
11023
11053
{
11024
- assert(tree->GetOperandCount() == 2);
11025
- GenTree*& op1 = tree->Op(1);
11054
+ // Avoid unexpected CSE for constant arguments for Vector_.Create
11055
+ // but only if all arguments are constants.
11026
11056
11027
- if (op1->IsCnsVec ())
11057
+ for (GenTree* arg : tree->Operands ())
11028
11058
{
11029
- // Move constant vectors from op1 to op2 for commutative operations
11030
- std::swap(op1, tree->Op(2));
11059
+ arg->SetDoNotCSE();
11031
11060
}
11032
11061
}
11033
- else
11034
- {
11035
- bool isScalar = false;
11036
- genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar);
11037
11062
11038
- // We can't handle scalar operations since they can copy upper bits from op1
11039
- if (GenTree::OperIsCompare(oper) && !isScalar)
11040
- {
11041
- assert(tree->GetOperandCount() == 2);
11063
+ // ------------------------------------------------------------------------
11064
+ // Perform the required oper-specific postorder morphing
11065
+ //
11042
11066
11043
- GenTree* op1 = tree->Op(1);
11044
- GenTree* op2 = tree->Op(2);
11067
+ morphedTree = fgMorphHWIntrinsicRequired(tree);
11045
11068
11046
- if (op1->IsCnsVec())
11047
- {
11048
- // Move constant vectors from op1 to op2 for comparison operations
11069
+ // ------------------------------------------------------------------------
11070
+ // Optional morphing is done if tree transformations is permitted
11071
+ //
11049
11072
11050
- genTreeOps newOper = GenTree::SwapRelop(oper);
11051
- var_types lookupType =
11052
- GenTreeHWIntrinsic::GetLookupTypeForCmpOp(this, newOper, retType, simdBaseType, simdSize);
11053
- NamedIntrinsic newId =
11054
- GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(this, newOper, retType, op2, op1, simdBaseType,
11055
- simdSize, false);
11073
+ if ((opts.compFlags & CLFLG_TREETRANS) != 0)
11074
+ {
11075
+ morphedTree = fgMorphHWIntrinsicOptional(morphedTree->AsHWIntrinsic());
11076
+ }
11077
+ }
11056
11078
11057
- if (newId != NI_Illegal)
11058
- {
11059
- tree->ResetHWIntrinsicId(newId, op2, op1);
11079
+ if (retType != morphedTree->TypeGet())
11080
+ {
11081
+ assert(varTypeIsMask(morphedTree));
11082
+ morphedTree = gtNewSimdCvtMaskToVectorNode(retType, morphedTree, simdBaseJitType, simdSize);
11083
+ morphedTree = gtFoldExpr(morphedTree);
11084
+ }
11085
+ return morphedTree;
11086
+ }
11060
11087
11061
- if (lookupType != retType)
11062
- {
11063
- assert(varTypeIsMask(lookupType));
11064
- tree->gtType = lookupType;
11065
- }
11066
- }
11067
- }
11068
- }
11069
- }
11088
+ //------------------------------------------------------------------------
11089
+ // fgMorphHWIntrinsicRequired: Perform required postorder morphing of a GenTreeHWIntrinsic tree.
11090
+ //
11091
+ // Arguments:
11092
+ // tree - The tree to morph
11093
+ //
11094
+ // Return Value:
11095
+ // The morphed tree.
11096
+ //
11097
+ GenTree* Compiler::fgMorphHWIntrinsicRequired(GenTreeHWIntrinsic* tree)
11098
+ {
11099
+ var_types retType = tree->TypeGet();
11100
+ CorInfoType simdBaseJitType = tree->GetSimdBaseJitType();
11101
+ var_types simdBaseType = tree->GetSimdBaseType();
11102
+ unsigned simdSize = tree->GetSimdSize();
11070
11103
11071
- // Try to fold it, maybe we get lucky,
11072
- GenTree* morphedTree = gtFoldExpr(tree);
11104
+ if (tree->isCommutativeHWIntrinsic())
11105
+ {
11106
+ assert(tree->GetOperandCount() == 2);
11107
+ GenTree*& op1 = tree->Op(1);
11073
11108
11074
- if ((morphedTree != tree) || !morphedTree->OperIsHWIntrinsic ())
11109
+ if (op1->OperIsConst ())
11075
11110
{
11076
- morphedTree->SetMorphed(this);
11111
+ // Move constants from op1 to op2 for commutative operations
11112
+ std::swap(op1, tree->Op(2));
11077
11113
}
11078
- else
11114
+ }
11115
+ else
11116
+ {
11117
+ bool isScalar = false;
11118
+ genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar);
11119
+
11120
+ // We can't handle scalar operations since they can copy upper bits from op1
11121
+ if (GenTree::OperIsCompare(oper) && !isScalar)
11079
11122
{
11080
- if (allArgsAreConst && tree->IsVectorCreate())
11123
+ assert(tree->GetOperandCount() == 2);
11124
+
11125
+ GenTree* op1 = tree->Op(1);
11126
+ GenTree* op2 = tree->Op(2);
11127
+
11128
+ if (op1->IsCnsVec())
11081
11129
{
11082
- // Avoid unexpected CSE for constant arguments for Vector_.Create
11083
- // but only if all arguments are constants.
11130
+ // Move constant vectors from op1 to op2 for comparison operations
11131
+
11132
+ genTreeOps newOper = GenTree::SwapRelop(oper);
11133
+ var_types lookupType =
11134
+ GenTreeHWIntrinsic::GetLookupTypeForCmpOp(this, newOper, retType, simdBaseType, simdSize);
11135
+ NamedIntrinsic newId = GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(this, newOper, retType, op2, op1,
11136
+ simdBaseType, simdSize, false);
11084
11137
11085
- for (GenTree* arg : tree->Operands() )
11138
+ if (newId != NI_Illegal )
11086
11139
{
11087
- arg->SetDoNotCSE();
11140
+ tree->ResetHWIntrinsicId(newId, op2, op1);
11141
+
11142
+ if (lookupType != retType)
11143
+ {
11144
+ assert(varTypeIsMask(lookupType));
11145
+ tree->gtType = lookupType;
11146
+ }
11088
11147
}
11089
11148
}
11090
-
11091
- morphedTree = fgOptimizeHWIntrinsic(tree);
11092
11149
}
11150
+ }
11093
11151
11094
- if (retType != morphedTree->TypeGet())
11095
- {
11096
- assert(varTypeIsMask(morphedTree));
11097
- morphedTree = gtNewSimdCvtMaskToVectorNode(retType, morphedTree, simdBaseJitType, simdSize);
11098
- morphedTree = gtFoldExpr(morphedTree);
11099
- }
11100
- return morphedTree;
11152
+ if (opts.OptimizationEnabled())
11153
+ {
11154
+ return fgOptimizeHWIntrinsic(tree);
11101
11155
}
11156
+ return tree;
11157
+ }
11102
11158
11159
+ //------------------------------------------------------------------------
11160
+ // fgMorphHWIntrinsicOptional: Perform optional postorder morphing of a GenTreeHWIntrinsic tree.
11161
+ //
11162
+ // Arguments:
11163
+ // tree - The tree to morph
11164
+ //
11165
+ // Return Value:
11166
+ // The morphed tree.
11167
+ //
11168
+ GenTree* Compiler::fgMorphHWIntrinsicOptional(GenTreeHWIntrinsic* tree)
11169
+ {
11103
11170
return tree;
11104
11171
}
11105
11172
#endif // FEATURE_HW_INTRINSICS
0 commit comments