diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index b5ee60a32fd153..f7d791d80155e9 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2882,8 +2882,8 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree) ValueNumPair vnPair = tree->gtVNPair; ValueNum vnCns = vnStore->VNConservativeNormalValue(vnPair); - // Check if node evaluates to a constant. - if (!vnStore->IsVNConstant(vnCns)) + // Check if node evaluates to a constant or Vector.Zero. + if (!vnStore->IsVNConstant(vnCns) && !vnStore->IsVNVectorZero(vnCns)) { return nullptr; } @@ -3042,6 +3042,24 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree) } break; +#if FEATURE_HW_INTRINSICS + case TYP_SIMD8: + case TYP_SIMD12: + case TYP_SIMD16: + case TYP_SIMD32: + { + assert(vnStore->IsVNVectorZero(vnCns)); + VNSimdTypeInfo vnInfo = vnStore->GetVectorZeroSimdTypeOfVN(vnCns); + + assert(vnInfo.m_simdBaseJitType != CORINFO_TYPE_UNDEF); + assert(vnInfo.m_simdSize != 0); + assert(getSIMDTypeForSize(vnInfo.m_simdSize) == vnStore->TypeOfVN(vnCns)); + + conValTree = gtNewSimdZeroNode(tree->TypeGet(), vnInfo.m_simdBaseJitType, vnInfo.m_simdSize, true); + } + break; +#endif + case TYP_BYREF: // Do not support const byref optimization. break; @@ -5449,7 +5467,8 @@ struct VNAssertionPropVisitorInfo // GenTree* Compiler::optExtractSideEffListFromConst(GenTree* tree) { - assert(vnStore->IsVNConstant(vnStore->VNConservativeNormalValue(tree->gtVNPair))); + assert(vnStore->IsVNConstant(vnStore->VNConservativeNormalValue(tree->gtVNPair)) || + vnStore->IsVNVectorZero(vnStore->VNConservativeNormalValue(tree->gtVNPair))); GenTree* sideEffList = nullptr; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index c6277882f52758..698c326132ffdc 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -21499,7 +21499,7 @@ GenTree* Compiler::gtNewSimdZeroNode(var_types type, #if defined(TARGET_XARCH) intrinsic = (simdSize == 32) ? NI_Vector256_get_Zero : NI_Vector128_get_Zero; #elif defined(TARGET_ARM64) - intrinsic = (simdSize == 16) ? NI_Vector128_get_Zero : NI_Vector64_get_Zero; + intrinsic = (simdSize > 8) ? NI_Vector128_get_Zero : NI_Vector64_get_Zero; #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index a9dcd48c71ef3b..fb340cd530e0ab 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5323,6 +5323,34 @@ struct GenTreeJitIntrinsic : public GenTreeMultiOp return (CorInfoType)gtSimdBaseJitType; } + CorInfoType GetNormalizedSimdBaseJitType() const + { + CorInfoType simdBaseJitType = GetSimdBaseJitType(); + switch (simdBaseJitType) + { + case CORINFO_TYPE_NATIVEINT: + { +#ifdef TARGET_64BIT + return CORINFO_TYPE_LONG; +#else + return CORINFO_TYPE_INT; +#endif + } + + case CORINFO_TYPE_NATIVEUINT: + { +#ifdef TARGET_64BIT + return CORINFO_TYPE_ULONG; +#else + return CORINFO_TYPE_UINT; +#endif + } + + default: + return simdBaseJitType; + } + } + void SetSimdBaseJitType(CorInfoType simdBaseJitType) { gtSimdBaseJitType = (unsigned char)simdBaseJitType; diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index d9592683db7981..b6dc9acbe76425 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -2066,9 +2066,6 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_PUTARG_STK: case GT_IL_OFFSET: case GT_KEEPALIVE: -#ifdef FEATURE_HW_INTRINSICS - case GT_HWINTRINSIC: -#endif // FEATURE_HW_INTRINSICS // Never remove these nodes, as they are always side-effecting. // // NOTE: the only side-effect of some of these nodes (GT_CMP, GT_SUB_HI) is a write to the flags @@ -2076,6 +2073,16 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR // Properly modeling this would allow these nodes to be removed. break; +#ifdef FEATURE_HW_INTRINSICS + case GT_HWINTRINSIC: + // Conservative: This only removes Vector.Zero nodes, but could be expanded. + if (node->IsVectorZero()) + { + fgTryRemoveNonLocal(node, &blockRange); + } + break; +#endif // FEATURE_HW_INTRINSICS + case GT_NOP: { // NOTE: we need to keep some NOPs around because they are referenced by calls. See the dead store diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 8d0290cd4f36df..03053f3ebdb53b 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -1888,7 +1888,7 @@ ValueNum ValueNumStore::VNZeroForType(var_types typ) // "fully zeroed" vectors, and here we may be loading one from memory, leaving upper // bits undefined. So using "SIMD_Init" is "the next best thing", so to speak, and // TYP_FLOAT is one of the more popular base types, so that's why we use it here. - return VNForFunc(typ, VNF_SIMD_Init, VNForFloatCon(0), VNForSimdType(genTypeSize(typ), TYP_FLOAT)); + return VNForFunc(typ, VNF_SIMD_Init, VNForFloatCon(0), VNForSimdType(genTypeSize(typ), CORINFO_TYPE_FLOAT)); #endif // FEATURE_SIMD // These should be unreached. @@ -1935,9 +1935,9 @@ ValueNum ValueNumStore::VNOneForType(var_types typ) } #ifdef FEATURE_SIMD -ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, var_types simdBaseType) +ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, CorInfoType simdBaseJitType) { - ValueNum baseTypeVN = VNForIntCon(INT32(simdBaseType)); + ValueNum baseTypeVN = VNForIntCon(INT32(simdBaseJitType)); ValueNum sizeVN = VNForIntCon(simdSize); ValueNum simdTypeVN = VNForFunc(TYP_REF, VNF_SimdType, sizeVN, baseTypeVN); @@ -4594,6 +4594,114 @@ bool ValueNumStore::IsVNConstant(ValueNum vn) } } +//------------------------------------------------------------------------ +// IsVNVectorZero: Checks if the value number is a Vector*_get_Zero. +// +// Arguments: +// vn - The value number. +// +// Return Value: +// true - The value number is a Vector*_get_Zero. +// false - The value number is not a Vector*_get_Zero. +bool ValueNumStore::IsVNVectorZero(ValueNum vn) +{ +#ifdef FEATURE_SIMD + VNSimdTypeInfo vnInfo = GetVectorZeroSimdTypeOfVN(vn); + // Check the size to see if we got a valid SIMD type. + // '0' means it is not valid. + if (vnInfo.m_simdSize != 0) + { + return true; + } +#endif + return false; +} + +#ifdef FEATURE_SIMD +//------------------------------------------------------------------------ +// GetSimdTypeOfVN: Returns the SIMD type information based on the given value number. +// +// Arguments: +// vn - The value number. +// +// Return Value: +// Returns VNSimdTypeInfo(0, CORINFO_TYPE_UNDEF) if the given value number has not been given a SIMD type. +VNSimdTypeInfo ValueNumStore::GetSimdTypeOfVN(ValueNum vn) +{ + VNSimdTypeInfo vnInfo; + + // The SIMD type is encoded as a function, + // even though it is not actually a function. + VNFuncApp simdType; + if (GetVNFunc(vn, &simdType) && simdType.m_func == VNF_SimdType) + { + assert(simdType.m_arity == 2); + vnInfo.m_simdSize = GetConstantInt32(simdType.m_args[0]); + vnInfo.m_simdBaseJitType = (CorInfoType)GetConstantInt32(simdType.m_args[1]); + return vnInfo; + } + + vnInfo.m_simdSize = 0; + vnInfo.m_simdBaseJitType = CORINFO_TYPE_UNDEF; + return vnInfo; +} + +//------------------------------------------------------------------------ +// GetVectorZeroSimdTypeOfVN: Returns the SIMD type information based on the given value number +// if it's Vector*_get_Zero. +// +// Arguments: +// vn - The value number. +// +// Return Value: +// Returns VNSimdTypeInfo(0, CORINFO_TYPE_UNDEF) if the given value number has not been given a SIMD type +// for a Vector*_get_Zero value number. +// +// REVIEW: Vector*_get_Zero nodes in VN currently encode their SIMD type for +// conservative reasons. In the future, it might be possible not do this +// on most platforms since Vector*_get_Zero's base type does not matter. +VNSimdTypeInfo ValueNumStore::GetVectorZeroSimdTypeOfVN(ValueNum vn) +{ +#ifdef FEATURE_HW_INTRINSICS + // REVIEW: This will only return true if Vector*_get_Zero encodes + // its base type as an argument. On XARCH there may be + // scenarios where Vector*_get_Zero will not encode its base type; + // therefore, returning false here. + // Vector*_get_Zero does not have any arguments, + // but its SIMD type is encoded as an argument. + VNFuncApp funcApp; + if (GetVNFunc(vn, &funcApp) && funcApp.m_arity == 1) + { + switch (funcApp.m_func) + { + case VNF_HWI_Vector128_get_Zero: +#if defined(TARGET_XARCH) + case VNF_HWI_Vector256_get_Zero: +#elif defined(TARGET_ARM64) + case VNF_HWI_Vector64_get_Zero: +#endif + { + return GetSimdTypeOfVN(funcApp.m_args[0]); + } + + default: + { + VNSimdTypeInfo vnInfo; + vnInfo.m_simdSize = 0; + vnInfo.m_simdBaseJitType = CORINFO_TYPE_UNDEF; + return vnInfo; + } + } + } +#endif + + VNSimdTypeInfo vnInfo; + vnInfo.m_simdSize = 0; + vnInfo.m_simdBaseJitType = CORINFO_TYPE_UNDEF; + return vnInfo; +} +#endif // FEATURE_SIMD + bool ValueNumStore::IsVNInt32Constant(ValueNum vn) { if (!IsVNConstant(vn)) @@ -6170,10 +6278,10 @@ void ValueNumStore::vnDumpSimdType(Compiler* comp, VNFuncApp* simdType) assert(IsVNConstant(simdType->m_args[0])); assert(IsVNConstant(simdType->m_args[1])); - int simdSize = ConstantValue(simdType->m_args[0]); - var_types baseType = (var_types)ConstantValue(simdType->m_args[1]); + int simdSize = ConstantValue(simdType->m_args[0]); + CorInfoType baseJitType = (CorInfoType)ConstantValue(simdType->m_args[1]); - printf("%s(simd%d, %s)", VNFuncName(simdType->m_func), simdSize, varTypeName(baseType)); + printf("%s(simd%d, %s)", VNFuncName(simdType->m_func), simdSize, varTypeName(JitType2PreciseVarType(baseJitType))); } #endif // FEATURE_SIMD @@ -9404,7 +9512,7 @@ void Compiler::fgValueNumberSimd(GenTreeSIMD* tree) if (encodeResultType) { - ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType()); + ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType()); resvnp.SetBoth(simdTypeVN); #ifdef DEBUG @@ -9519,7 +9627,7 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree) if (encodeResultType) { - ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType()); + ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType()); resvnp.SetBoth(simdTypeVN); #ifdef DEBUG diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 6c19327c800375..235017a78cbec7 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -184,6 +184,13 @@ struct VNFuncApp } }; +// An instance of this struct represents the decoded information of a SIMD type from a value number. +struct VNSimdTypeInfo +{ + unsigned int m_simdSize; + CorInfoType m_simdBaseJitType; +}; + // We use a unique prefix character when printing value numbers in dumps: i.e. $1c0 // This define is used with string concatenation to put this in printf format strings #define FMT_VN "$%x" @@ -463,7 +470,7 @@ class ValueNumStore #ifdef FEATURE_SIMD // A helper function for constructing VNF_SimdType VNs. - ValueNum VNForSimdType(unsigned simdSize, var_types simdBaseType); + ValueNum VNForSimdType(unsigned simdSize, CorInfoType simdBaseJitType); #endif // FEATURE_SIMD // Create or return the existimg value number representing a singleton exception set @@ -714,6 +721,14 @@ class ValueNumStore // Returns true iff the VN represents a (non-handle) constant. bool IsVNConstant(ValueNum vn); + bool IsVNVectorZero(ValueNum vn); + +#ifdef FEATURE_SIMD + VNSimdTypeInfo GetSimdTypeOfVN(ValueNum vn); + + VNSimdTypeInfo GetVectorZeroSimdTypeOfVN(ValueNum vn); +#endif + // Returns true iff the VN represents an integer constant. bool IsVNInt32Constant(ValueNum vn); diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_33972/Runtime_33972.cs b/src/tests/JIT/Regression/JitBlue/Runtime_33972/Runtime_33972.cs index 9524e8d666add4..d57bd99e680e4e 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_33972/Runtime_33972.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_33972/Runtime_33972.cs @@ -149,6 +149,46 @@ static Vector128 AdvSimd_CompareEqual_Vector128_Single_CreateZeroZeroZero return AdvSimd.CompareEqual(left, asVar); } + [MethodImpl(MethodImplOptions.NoInlining)] + static Vector128 AdvSimd_CompareEqual_Vector128_Single_CreateZeroZeroZeroZero_AsVariableLoop(Vector128 left) + { + Vector128 result = default; + var asVar = Vector128.Create(0f, 0f, 0f, 0f); + for (var i = 0; i < 4; i++) + { + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + for (var j = 0; j < 4; j++) + { + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + result = AdvSimd.CompareEqual(left, asVar); + } + } + return result; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe Vector128 AdvSimd_Arm64_CompareEqual_Vector128_Long_AsVariableLoop(Vector128 left) + { + Vector128 result = default; + Vector128 asVar = Vector128.Create((long)0); + Vector128 asVar2 = Vector128.Create((nint)0); + Vector128 asVar3 = asVar2.AsInt64(); + for (var i = 0; i < 4; i++) + { + result = AdvSimd.Arm64.CompareEqual(left, asVar); + for (var j = 0; j < 4; j++) + { + result = AdvSimd.Arm64.CompareEqual(left, asVar3); + } + } + return result; + } + [MethodImpl(MethodImplOptions.NoInlining)] static Vector128 AdvSimd_Arm64_CompareEqual_Vector128_Double_Zero(Vector128 left) { @@ -564,6 +604,9 @@ static int Tests_AdvSimd() if (!ValidateResult_Vector128(AdvSimd_CompareEqual_Vector128_Single_CreateZeroZeroZeroZero_AsVariable(Vector128.Zero), Single.NaN)) result = -1; + if (!ValidateResult_Vector128(AdvSimd_CompareEqual_Vector128_Single_CreateZeroZeroZeroZero_AsVariableLoop(Vector128.Zero), Single.NaN)) + result = -1; + // End CompareEqual Tests // Begin CompareGreaterThan Tests @@ -747,6 +790,9 @@ static int Tests_AdvSimd_Arm64() if (!ValidateResult_Vector128(AdvSimd_Arm64_CompareEqual_Vector128_Int64_Zero(Vector128.Zero), -1)) result = -1; + if (!ValidateResult_Vector128(AdvSimd_Arm64_CompareEqual_Vector128_Long_AsVariableLoop(Vector128.Zero), -1)) + result = -1; + // Vector64 if (!ValidateResult_Vector64(AdvSimd_Arm64_CompareEqualScalar_Vector64_Single_Zero(Vector64.Zero), Vector64.CreateScalar(Single.NaN)))