diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 41e097df6a6713..4993bf556c1701 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -25383,14 +25383,11 @@ GenTree* Compiler::gtNewSimdNarrowNode( else { // var tmp1 = op1.ToVector128Unsafe(); - // var tmp2 = AdvSimd.InsertScalar(tmp1.AsUInt64(), 1, op2.AsUInt64()).As(); - signed integer use int64, - // unsigned integer use uint64 + // var tmp2 = tmp1.WithUpper(op2); // return AdvSimd.ExtractNarrowingLower(tmp2); - CorInfoType tmp2BaseJitType = varTypeIsSigned(simdBaseType) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG; - tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, NI_Vector64_ToVector128Unsafe, simdBaseJitType, simdSize); - tmp2 = gtNewSimdWithUpperNode(TYP_SIMD16, tmp1, op2, tmp2BaseJitType, 16); + tmp2 = gtNewSimdWithUpperNode(TYP_SIMD16, tmp1, op2, simdBaseJitType, 16); return gtNewSimdHWIntrinsicNode(type, tmp2, NI_AdvSimd_ExtractNarrowingLower, simdBaseJitType, simdSize); } diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 44525287c99b59..af6c1bb935ae81 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -717,6 +717,32 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector64_AddSaturate: + case NI_Vector128_AddSaturate: + { + assert(sig->numArgs == 2); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + } + else + { + intrinsic = NI_AdvSimd_AddSaturate; + + if ((simdSize == 8) && varTypeIsLong(simdBaseType)) + { + intrinsic = NI_AdvSimd_AddSaturateScalar; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + break; + } + case NI_AdvSimd_BitwiseClear: case NI_Vector64_AndNot: case NI_Vector128_AndNot: @@ -2122,6 +2148,39 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector64_NarrowWithSaturation: + case NI_Vector128_NarrowWithSaturation: + { + assert(sig->numArgs == 2); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize); + } + else if (simdSize == 16) + { + intrinsic = NI_AdvSimd_ExtractNarrowingSaturateLower; + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD8, op1, intrinsic, simdBaseJitType, 8); + + intrinsic = NI_AdvSimd_ExtractNarrowingSaturateUpper; + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + else + { + intrinsic = NI_Vector64_ToVector128Unsafe; + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseJitType, simdSize); + + op1 = gtNewSimdWithUpperNode(TYP_SIMD16, op1, op2, simdBaseJitType, 16); + + intrinsic = NI_AdvSimd_ExtractNarrowingSaturateLower; + retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); + } + break; + } + case NI_Vector64_op_UnaryNegation: case NI_Vector128_op_UnaryNegation: { @@ -2598,6 +2657,32 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector64_SubtractSaturate: + case NI_Vector128_SubtractSaturate: + { + assert(sig->numArgs == 2); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + } + else + { + intrinsic = NI_AdvSimd_SubtractSaturate; + + if ((simdSize == 8) && varTypeIsLong(simdBaseType)) + { + intrinsic = NI_AdvSimd_SubtractSaturateScalar; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + break; + } + case NI_Vector64_Sum: case NI_Vector128_Sum: { diff --git a/src/coreclr/jit/hwintrinsiclistarm64.h b/src/coreclr/jit/hwintrinsiclistarm64.h index bcd837f48ec750..e54f77c13c64bf 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64.h +++ b/src/coreclr/jit/hwintrinsiclistarm64.h @@ -17,6 +17,7 @@ // Vector64 Intrinsics #define FIRST_NI_Vector64 NI_Vector64_Abs HARDWARE_INTRINSIC(Vector64, Abs, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector64, AddSaturate, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, AndNot, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, As, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, AsByte, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -88,6 +89,7 @@ HARDWARE_INTRINSIC(Vector64, Min, HARDWARE_INTRINSIC(Vector64, MinNative, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, MultiplyAddEstimate, 8, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Narrow, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector64, NarrowWithSaturation, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Round, 8, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, ShiftLeft, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Shuffle, 8, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_CanBenefitFromConstantProp) @@ -97,6 +99,7 @@ HARDWARE_INTRINSIC(Vector64, Sqrt, HARDWARE_INTRINSIC(Vector64, StoreAligned, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, StoreAlignedNonTemporal, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, StoreUnsafe, 8, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector64, SubtractSaturate, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Sum, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, ToScalar, 8, 1, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector64, ToVector128, 8, 1, {INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov}, HW_Category_SIMD, HW_Flag_SpecialCodeGen) @@ -133,6 +136,7 @@ HARDWARE_INTRINSIC(Vector64, op_UnsignedRightShift, // Vector128 Intrinsics #define FIRST_NI_Vector128 NI_Vector128_Abs HARDWARE_INTRINSIC(Vector128, Abs, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, AddSaturate, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AndNot, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, As, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsByte, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -212,6 +216,7 @@ HARDWARE_INTRINSIC(Vector128, Min, HARDWARE_INTRINSIC(Vector128, MinNative, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, MultiplyAddEstimate, 16, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Narrow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, NarrowWithSaturation, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Round, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ShiftLeft, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_CanBenefitFromConstantProp) @@ -221,6 +226,7 @@ HARDWARE_INTRINSIC(Vector128, Sqrt, HARDWARE_INTRINSIC(Vector128, StoreAligned, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAlignedNonTemporal, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreUnsafe, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector128, SubtractSaturate, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Sum, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, Truncate, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index f8c72b30277d49..330cd59229518f 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -29,6 +29,7 @@ // Vector128 Intrinsics #define FIRST_NI_Vector128 NI_Vector128_Abs HARDWARE_INTRINSIC(Vector128, Abs, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, AddSaturate, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AndNot, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, As, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsByte, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -106,6 +107,7 @@ HARDWARE_INTRINSIC(Vector128, Min, HARDWARE_INTRINSIC(Vector128, MinNative, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, MultiplyAddEstimate, 16, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Narrow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, NarrowWithSaturation, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, Round, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ShiftLeft, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_CanBenefitFromConstantProp) @@ -115,6 +117,7 @@ HARDWARE_INTRINSIC(Vector128, Sqrt, HARDWARE_INTRINSIC(Vector128, StoreAligned, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAlignedNonTemporal, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreUnsafe, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector128, SubtractSaturate, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Sum, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(Vector128, ToVector256, 16, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) @@ -152,6 +155,7 @@ HARDWARE_INTRINSIC(Vector128, op_UnsignedRightShift, // Vector256 Intrinsics #define FIRST_NI_Vector256 NI_Vector256_Abs HARDWARE_INTRINSIC(Vector256, Abs, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector256, AddSaturate, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, AndNot, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, As, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, AsByte, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) @@ -227,6 +231,7 @@ HARDWARE_INTRINSIC(Vector256, Min, HARDWARE_INTRINSIC(Vector256, MinNative, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, MultiplyAddEstimate, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Narrow, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector256, NarrowWithSaturation, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, Round, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, ShiftLeft, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Shuffle, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_CanBenefitFromConstantProp) @@ -236,6 +241,7 @@ HARDWARE_INTRINSIC(Vector256, Sqrt, HARDWARE_INTRINSIC(Vector256, StoreAligned, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, StoreAlignedNonTemporal, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, StoreUnsafe, 32, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) +HARDWARE_INTRINSIC(Vector256, SubtractSaturate, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Sum, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, ToScalar, 32, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ToVector512, 32, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) @@ -274,6 +280,7 @@ HARDWARE_INTRINSIC(Vector256, op_UnsignedRightShift, // Vector512 Intrinsics #define FIRST_NI_Vector512 NI_Vector512_Abs HARDWARE_INTRINSIC(Vector512, Abs, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector512, AddSaturate, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, AndNot, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, As, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, AsByte, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -350,6 +357,7 @@ HARDWARE_INTRINSIC(Vector512, Min, HARDWARE_INTRINSIC(Vector512, MinNative, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, MultiplyAddEstimate, 64, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Narrow, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector512, NarrowWithSaturation, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, Round, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, ShiftLeft, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Shuffle, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_CanBenefitFromConstantProp) @@ -359,6 +367,7 @@ HARDWARE_INTRINSIC(Vector512, Sqrt, HARDWARE_INTRINSIC(Vector512, StoreAligned, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, StoreAlignedNonTemporal, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, StoreUnsafe, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector512, SubtractSaturate, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Sum, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, ToScalar, 64, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, Truncate, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 106a0b952fe139..29fe70b74b62a6 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -1512,6 +1512,133 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector128_AddSaturate: + case NI_Vector256_AddSaturate: + case NI_Vector512_AddSaturate: + { + assert(sig->numArgs == 2); + + if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || + compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + } + else if (varTypeIsSmall(simdBaseType)) + { + if (simdSize == 64) + { + intrinsic = NI_AVX512BW_AddSaturate; + } + else if (simdSize == 32) + { + intrinsic = NI_AVX2_AddSaturate; + } + else + { + assert(simdSize == 16); + intrinsic = NI_SSE2_AddSaturate; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + else if (varTypeIsUnsigned(simdBaseType)) + { + // For unsigned we simply have to detect `(x + y) < x` + // and in that scenario return MaxValue (AllBitsSet) + + GenTree* cns = gtNewAllBitsSetConNode(retType); + GenTree* op1Dup1 = fgMakeMultiUse(&op1); + + GenTree* tmp = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + GenTree* tmpDup1 = fgMakeMultiUse(&tmp); + GenTree* msk = gtNewSimdCmpOpNode(GT_LT, retType, tmp, op1Dup1, simdBaseJitType, simdSize); + + retNode = gtNewSimdCndSelNode(retType, msk, cns, tmpDup1, simdBaseJitType, simdSize); + } + else + { + // For signed the logic is a bit more complex, but is + // explained on the managed side as part of Scalar.AddSaturate + + GenTreeVecCon* minCns = gtNewVconNode(retType); + GenTreeVecCon* maxCns = gtNewVconNode(retType); + + switch (simdBaseType) + { + case TYP_SHORT: + { + minCns->EvaluateBroadcastInPlace(INT16_MIN); + maxCns->EvaluateBroadcastInPlace(INT16_MAX); + break; + } + + case TYP_INT: + { + minCns->EvaluateBroadcastInPlace(INT32_MIN); + maxCns->EvaluateBroadcastInPlace(INT32_MAX); + break; + } + + case TYP_LONG: + { + minCns->EvaluateBroadcastInPlace(INT64_MIN); + maxCns->EvaluateBroadcastInPlace(INT64_MAX); + break; + } + + default: + { + unreached(); + } + } + + GenTree* op1Dup1 = fgMakeMultiUse(&op1); + GenTree* op2Dup1 = fgMakeMultiUse(&op2); + + GenTree* tmp = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + + GenTree* tmpDup1 = fgMakeMultiUse(&tmp); + GenTree* tmpDup2 = gtCloneExpr(tmpDup1); + + GenTree* msk = gtNewSimdIsNegativeNode(retType, tmpDup1, simdBaseJitType, simdSize); + GenTree* ovf = gtNewSimdCndSelNode(retType, msk, maxCns, minCns, simdBaseJitType, simdSize); + + // The mask we need is ((a ^ b) & ~(b ^ c)) < 0 + + if (compOpportunisticallyDependsOn(InstructionSet_AVX512F)) + { + // tmpDup1 = a: 0xF0 + // op1Dup1 = b: 0xCC + // op2Dup2 = c: 0xAA + // + // 0x18 = A ? norBC : andBC + // a ? ~(b | c) : (b & c) + msk = gtNewSimdTernaryLogicNode(retType, tmp, op1Dup1, op2Dup1, gtNewIconNode(0x18), + simdBaseJitType, simdSize); + } + else + { + GenTree* op1Dup2 = gtCloneExpr(op1Dup1); + + GenTree* msk2 = gtNewSimdBinOpNode(GT_XOR, retType, tmp, op1Dup1, simdBaseJitType, simdSize); + GenTree* msk3 = + gtNewSimdBinOpNode(GT_XOR, retType, op1Dup2, op2Dup1, simdBaseJitType, simdSize); + + msk = gtNewSimdBinOpNode(GT_AND_NOT, retType, msk2, msk3, simdBaseJitType, simdSize); + } + + msk = gtNewSimdIsNegativeNode(retType, msk, simdBaseJitType, simdSize); + retNode = gtNewSimdCndSelNode(retType, msk, ovf, tmpDup2, simdBaseJitType, simdSize); + } + } + break; + } + case NI_Vector128_AndNot: case NI_Vector256_AndNot: case NI_Vector512_AndNot: @@ -3340,6 +3467,219 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector128_NarrowWithSaturation: + case NI_Vector256_NarrowWithSaturation: + case NI_Vector512_NarrowWithSaturation: + { + assert(sig->numArgs == 2); + + if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || + compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (simdBaseType == TYP_DOUBLE) + { + // gtNewSimdNarrowNode uses the base type of the return for the simdBaseType + retNode = gtNewSimdNarrowNode(retType, op1, op2, CORINFO_TYPE_FLOAT, simdSize); + } + else if ((simdSize == 16) && ((simdBaseType == TYP_SHORT) || (simdBaseType == TYP_INT))) + { + // PackSignedSaturate uses the base type of the return for the simdBaseType + simdBaseJitType = (simdBaseType == TYP_SHORT) ? CORINFO_TYPE_BYTE : CORINFO_TYPE_SHORT; + + intrinsic = NI_SSE2_PackSignedSaturate; + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + else if (compOpportunisticallyDependsOn(InstructionSet_AVX512F)) + { + if ((simdSize == 32) || (simdSize == 64)) + { + if (simdSize == 32) + { + intrinsic = NI_Vector256_ToVector512Unsafe; + + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD64, op1, intrinsic, simdBaseJitType, simdSize); + op1 = gtNewSimdWithUpperNode(TYP_SIMD64, op1, op2, simdBaseJitType, simdSize * 2); + } + + switch (simdBaseType) + { + case TYP_SHORT: + { + intrinsic = NI_AVX512BW_ConvertToVector256SByteWithSaturation; + break; + } + + case TYP_USHORT: + { + intrinsic = NI_AVX512BW_ConvertToVector256ByteWithSaturation; + break; + } + + case TYP_INT: + { + intrinsic = NI_AVX512F_ConvertToVector256Int16WithSaturation; + break; + } + + case TYP_UINT: + { + intrinsic = NI_AVX512F_ConvertToVector256UInt16WithSaturation; + break; + } + + case TYP_LONG: + { + intrinsic = NI_AVX512F_ConvertToVector256Int32WithSaturation; + break; + } + + case TYP_ULONG: + { + intrinsic = NI_AVX512F_ConvertToVector256UInt32WithSaturation; + break; + } + + default: + { + unreached(); + } + } + } + else + { + assert(simdSize == 16); + intrinsic = NI_Vector128_ToVector256Unsafe; + + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, intrinsic, simdBaseJitType, simdSize); + op1 = gtNewSimdWithUpperNode(TYP_SIMD32, op1, op2, simdBaseJitType, simdSize * 2); + + switch (simdBaseType) + { + case TYP_USHORT: + { + intrinsic = NI_AVX512BW_VL_ConvertToVector128ByteWithSaturation; + break; + } + + case TYP_UINT: + { + intrinsic = NI_AVX512F_VL_ConvertToVector128UInt16WithSaturation; + break; + } + + case TYP_LONG: + { + intrinsic = NI_AVX512F_VL_ConvertToVector128Int32WithSaturation; + break; + } + + case TYP_ULONG: + { + intrinsic = NI_AVX512F_VL_ConvertToVector128UInt32WithSaturation; + break; + } + + default: + { + unreached(); + } + } + } + + if (simdSize == 64) + { + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, intrinsic, simdBaseJitType, simdSize); + op2 = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op2, intrinsic, simdBaseJitType, simdSize); + + retNode = gtNewSimdWithUpperNode(retType, op1, op2, simdBaseJitType, simdSize); + } + else + { + retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize * 2); + } + } + else + { + // gtNewSimdNarrowNode uses the base type of the return for the simdBaseType + CorInfoType narrowSimdBaseJitType; + + GenTreeVecCon* minCns = varTypeIsSigned(simdBaseType) ? gtNewVconNode(retType) : nullptr; + GenTreeVecCon* maxCns = gtNewVconNode(retType); + + switch (simdBaseType) + { + case TYP_SHORT: + { + minCns->EvaluateBroadcastInPlace(INT8_MIN); + maxCns->EvaluateBroadcastInPlace(INT8_MAX); + + narrowSimdBaseJitType = CORINFO_TYPE_BYTE; + break; + } + + case TYP_USHORT: + { + maxCns->EvaluateBroadcastInPlace(UINT8_MAX); + narrowSimdBaseJitType = CORINFO_TYPE_UBYTE; + break; + } + + case TYP_INT: + { + minCns->EvaluateBroadcastInPlace(INT16_MIN); + maxCns->EvaluateBroadcastInPlace(INT16_MAX); + + narrowSimdBaseJitType = CORINFO_TYPE_SHORT; + break; + } + + case TYP_UINT: + { + maxCns->EvaluateBroadcastInPlace(UINT16_MAX); + narrowSimdBaseJitType = CORINFO_TYPE_USHORT; + break; + } + + case TYP_LONG: + { + minCns->EvaluateBroadcastInPlace(INT32_MIN); + maxCns->EvaluateBroadcastInPlace(INT32_MAX); + + narrowSimdBaseJitType = CORINFO_TYPE_INT; + break; + } + + case TYP_ULONG: + { + maxCns->EvaluateBroadcastInPlace(UINT32_MAX); + narrowSimdBaseJitType = CORINFO_TYPE_UINT; + break; + } + + default: + { + unreached(); + } + } + + if (minCns != nullptr) + { + op1 = gtNewSimdMaxNode(retType, op1, minCns, simdBaseJitType, simdSize); + op2 = gtNewSimdMaxNode(retType, op2, gtCloneExpr(minCns), simdBaseJitType, simdSize); + } + + op1 = gtNewSimdMinNode(retType, op1, maxCns, simdBaseJitType, simdSize); + op2 = gtNewSimdMinNode(retType, op2, gtCloneExpr(maxCns), simdBaseJitType, simdSize); + + retNode = gtNewSimdNarrowNode(retType, op1, op2, narrowSimdBaseJitType, simdSize); + } + } + break; + } + case NI_Vector128_op_UnaryNegation: case NI_Vector256_op_UnaryNegation: case NI_Vector512_op_UnaryNegation: @@ -3710,6 +4050,133 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector128_SubtractSaturate: + case NI_Vector256_SubtractSaturate: + case NI_Vector512_SubtractSaturate: + { + assert(sig->numArgs == 2); + + if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || + compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + } + else if (varTypeIsSmall(simdBaseType)) + { + if (simdSize == 64) + { + intrinsic = NI_AVX512BW_SubtractSaturate; + } + else if (simdSize == 32) + { + intrinsic = NI_AVX2_SubtractSaturate; + } + else + { + assert(simdSize == 16); + intrinsic = NI_SSE2_SubtractSaturate; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + else if (varTypeIsUnsigned(simdBaseType)) + { + // For unsigned we simply have to detect `(x - y) > x` + // and in that scenario return MinValue (Zero) + + GenTree* cns = gtNewZeroConNode(retType); + GenTree* op1Dup1 = fgMakeMultiUse(&op1); + + GenTree* tmp = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + GenTree* tmpDup1 = fgMakeMultiUse(&tmp); + GenTree* msk = gtNewSimdCmpOpNode(GT_GT, retType, tmp, op1Dup1, simdBaseJitType, simdSize); + + retNode = gtNewSimdCndSelNode(retType, msk, cns, tmpDup1, simdBaseJitType, simdSize); + } + else + { + // For signed the logic is a bit more complex, but is + // explained on the managed side as part of Scalar.SubtractSaturate + + GenTreeVecCon* minCns = gtNewVconNode(retType); + GenTreeVecCon* maxCns = gtNewVconNode(retType); + + switch (simdBaseType) + { + case TYP_SHORT: + { + minCns->EvaluateBroadcastInPlace(INT16_MIN); + maxCns->EvaluateBroadcastInPlace(INT16_MAX); + break; + } + + case TYP_INT: + { + minCns->EvaluateBroadcastInPlace(INT32_MIN); + maxCns->EvaluateBroadcastInPlace(INT32_MAX); + break; + } + + case TYP_LONG: + { + minCns->EvaluateBroadcastInPlace(INT64_MIN); + maxCns->EvaluateBroadcastInPlace(INT64_MAX); + break; + } + + default: + { + unreached(); + } + } + + GenTree* op1Dup1 = fgMakeMultiUse(&op1); + GenTree* op2Dup1 = fgMakeMultiUse(&op2); + + GenTree* tmp = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + + GenTree* tmpDup1 = fgMakeMultiUse(&tmp); + GenTree* tmpDup2 = gtCloneExpr(tmpDup1); + + GenTree* msk = gtNewSimdIsNegativeNode(retType, tmpDup1, simdBaseJitType, simdSize); + GenTree* ovf = gtNewSimdCndSelNode(retType, msk, maxCns, minCns, simdBaseJitType, simdSize); + + // The mask we need is ((a ^ b) & (b ^ c)) < 0 + + if (compOpportunisticallyDependsOn(InstructionSet_AVX512F)) + { + // tmpDup1 = a: 0xF0 + // op1Dup1 = b: 0xCC + // op2Dup2 = c: 0xAA + // + // 0x18 = B ? norAC : andAC + // b ? ~(a | c) : (a & c) + msk = gtNewSimdTernaryLogicNode(retType, tmp, op1Dup1, op2Dup1, gtNewIconNode(0x24), + simdBaseJitType, simdSize); + } + else + { + GenTree* op1Dup2 = gtCloneExpr(op1Dup1); + + GenTree* msk2 = gtNewSimdBinOpNode(GT_XOR, retType, tmp, op1Dup1, simdBaseJitType, simdSize); + GenTree* msk3 = + gtNewSimdBinOpNode(GT_XOR, retType, op1Dup2, op2Dup1, simdBaseJitType, simdSize); + + msk = gtNewSimdBinOpNode(GT_AND, retType, msk2, msk3, simdBaseJitType, simdSize); + } + + msk = gtNewSimdIsNegativeNode(retType, msk, simdBaseJitType, simdSize); + retNode = gtNewSimdCndSelNode(retType, msk, ovf, tmpDup2, simdBaseJitType, simdSize); + } + } + break; + } + case NI_Vector128_Sum: case NI_Vector256_Sum: case NI_Vector512_Sum: diff --git a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs index 33d5c9c4843948..f6c2e09e922794 100644 --- a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs +++ b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs @@ -207,6 +207,7 @@ public static partial class Vector public static bool IsHardwareAccelerated { get { throw null; } } public static System.Numerics.Vector Abs(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector Add(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AddSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static bool All(System.Numerics.Vector vector, T value) { throw null; } public static bool AllWhereAllBitsSet(System.Numerics.Vector vector) { throw null; } public static System.Numerics.Vector AndNot(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -391,6 +392,17 @@ public static partial class Vector public static System.Numerics.Vector Narrow(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Numerics.Vector Narrow(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Numerics.Vector NarrowWithSaturation(System.Numerics.Vector low, System.Numerics.Vector high) { throw null; } public static System.Numerics.Vector Negate(System.Numerics.Vector value) { throw null; } public static bool None(System.Numerics.Vector vector, T value) { throw null; } public static bool NoneWhereAllBitsSet(System.Numerics.Vector vector) { throw null; } @@ -479,6 +491,7 @@ public static partial class Vector [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Numerics.Vector4 source, ref float destination, nuint elementOffset) { throw null; } public static System.Numerics.Vector Subtract(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector SubtractSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static T Sum(System.Numerics.Vector value) { throw null; } public static T ToScalar(this System.Numerics.Vector vector) { throw null; } public static float ToScalar(this System.Numerics.Vector2 vector) { throw null; } diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 0ebac20f1b9194..b5f4568a628285 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; @@ -5816,5 +5817,267 @@ private void CountIndexOfLastIndexOfWhereAllBitsSetTest(T allBitsSet, T value [Fact] public void CountIndexOfLastIndexOfWhereAllBitsSetUInt64Test() => CountIndexOfLastIndexOfWhereAllBitsSetTest(ulong.MaxValue, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMaxTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector left = Vector.CreateSequence(start, T.One); + Vector right = Vector.Create(T.MaxValue - T.CreateTruncating(Vector.Count) + T.One); + + Vector result = Vector.AddSaturate(left, right); + + for (int i = 0; i < Vector.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMinTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector left = Vector.CreateSequence(start, T.NegativeOne); + Vector right = Vector.Create(T.MinValue + T.CreateTruncating(Vector.Count) - T.One); + + Vector result = Vector.AddSaturate(left, right); + + for (int i = 0; i < Vector.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector.Count - 1]); + } + + [Fact] + public void AddSaturateByteTest() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateInt16Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt32Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt64Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateIntPtrTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateSByteTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateUInt16Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt32Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt64Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUIntPtrTest() => AddSaturateToMaxTest(1); + + private (Vector lower, Vector upper) GetNarrowWithSaturationInputs() + where TFrom : unmanaged, IMinMaxValue, INumber + where TTo : unmanaged, IMinMaxValue, INumber + { + Vector lower = Vector.Create(TFrom.CreateTruncating(TTo.MaxValue) - TFrom.CreateTruncating(Vector.Count) + TFrom.One) + + Vector.CreateSequence(TFrom.One, TFrom.One); + + Vector upper = Vector.Create(TFrom.CreateTruncating(TTo.MinValue) + TFrom.CreateTruncating(Vector.Count) - TFrom.One) + - Vector.CreateSequence(TFrom.One, TFrom.One); + + return (lower, upper); + } + + private void NarrowWithSaturationTest(Vector lower, Vector upper, Vector result) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + for (int i = 0; i < Vector.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(lower[i]); + Assert.Equal(expectedResult, result[i]); + } + + for (int i = 0; i < Vector.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(upper[i]); + Assert.Equal(expectedResult, result[Vector.Count + i]); + } + } + + [Fact] + public void NarrowWithSaturationInt16Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt32Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt64Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt16Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt32Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt64Test() + { + (Vector lower, Vector upper) = GetNarrowWithSaturationInputs(); + Vector result = Vector.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMaxTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector left = Vector.Create(T.MaxValue - T.CreateTruncating(Vector.Count) + T.One); + Vector right = Vector.CreateSequence(start, T.NegativeOne); + + Vector result = Vector.SubtractSaturate(left, right); + + for (int i = 0; i < Vector.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMinTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector left = Vector.Create(T.MinValue + T.CreateTruncating(Vector.Count) - T.One); + Vector right = Vector.CreateSequence(start, T.One); + + Vector result = Vector.SubtractSaturate(left, right); + + for (int i = 0; i < Vector.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector.Count - 1]); + } + + [Fact] + public void SubtractSaturateByteTest() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateInt16Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt32Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt64Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateIntPtrTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateSByteTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateUInt16Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt32Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt64Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUIntPtrTest() => SubtractSaturateToMinTest(1); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index a2a4d792115346..535fd4321661c3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -681,18 +681,14 @@ public static implicit operator Int128(sbyte value) { // For signed addition, we can detect overflow by checking if the sign of // both inputs are the same and then if that differs from the sign of the - // output. + // output. The logic for how this works is explained in the + // System.Runtime.Intrinsics.Scalar.AddSaturate method Int128 result = left + right; - uint sign = (uint)(left._upper >> 63); - - if (sign == (uint)(right._upper >> 63)) + if ((long)((result._upper ^ left._upper) & ~(left._upper ^ right._upper)) < 0) { - if (sign != (uint)(result._upper >> 63)) - { - ThrowHelper.ThrowOverflowException(); - } + ThrowHelper.ThrowOverflowException(); } return result; } @@ -2077,18 +2073,14 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma { // For signed subtraction, we can detect overflow by checking if the sign of // both inputs are different and then if that differs from the sign of the - // output. + // output. The logic for how this works is explained in the + // System.Runtime.Intrinsics.Scalar.SubtractSaturate method Int128 result = left - right; - uint sign = (uint)(left._upper >> 63); - - if (sign != (uint)(right._upper >> 63)) + if ((long)((result._upper ^ left._upper) & (left._upper ^ right._upper)) < 0) { - if (sign != (uint)(result._upper >> 63)) - { - ThrowHelper.ThrowOverflowException(); - } + ThrowHelper.ThrowOverflowException(); } return result; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index 744efd825eecce..de97800070383f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -5,15 +5,14 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; namespace System.Numerics { /// Provides a collection of static methods for creating, manipulating, and otherwise operating on generic vectors. [Intrinsic] - public static unsafe partial class Vector + public static partial class Vector { - internal static int Alignment => sizeof(Vector); + internal static int Alignment => Vector.Count; /// Gets a value that indicates whether vector operations are subject to hardware acceleration through JIT intrinsic support. /// if vector operations are subject to hardware acceleration; otherwise, . @@ -54,14 +53,33 @@ public static Vector Abs(Vector value) } } - /// Adds two vectors to compute their sum. - /// The vector to add with . - /// The vector to add with . - /// The type of the elements in the vector. - /// The sum of and . + /// [Intrinsic] public static Vector Add(Vector left, Vector right) => left + right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector AddSaturate(Vector left, Vector right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left + right; + } + else + { + Unsafe.SkipInit(out Vector result); + + for (int index = 0; index < Vector.Count; index++) + { + T value = Scalar.AddSaturate(left.GetElementUnsafe(index), right.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -379,17 +397,17 @@ public static Vector ClampNative(Vector value, Vector min, Vector [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ConvertToDouble(Vector value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.ConvertToDouble(value.AsVector512()).AsVector(); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.ConvertToDouble(value.AsVector256()).AsVector(); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.ConvertToDouble(value.AsVector128()).AsVector(); } } @@ -402,17 +420,17 @@ public static Vector ConvertToDouble(Vector value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ConvertToDouble(Vector value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.ConvertToDouble(value.AsVector512()).AsVector(); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.ConvertToDouble(value.AsVector256()).AsVector(); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.ConvertToDouble(value.AsVector128()).AsVector(); } } @@ -510,17 +528,17 @@ public static Vector ConvertToSingle(Vector value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ConvertToSingle(Vector value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.ConvertToSingle(value.AsVector512()).AsVector(); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.ConvertToSingle(value.AsVector256()).AsVector(); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.ConvertToSingle(value.AsVector128()).AsVector(); } } @@ -677,17 +695,17 @@ public static Vector CopySign(Vector value, Vector sign) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Count(Vector vector, T value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.Count(vector.AsVector512(), value); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.Count(vector.AsVector256(), value); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.Count(vector.AsVector128(), value); } } @@ -1334,17 +1352,17 @@ public static Vector Hypot(Vector x, Vector y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(Vector vector, T value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.IndexOf(vector.AsVector512(), value); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.IndexOf(vector.AsVector256(), value); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.IndexOf(vector.AsVector128(), value); } } @@ -1580,17 +1598,17 @@ public static Vector IsSubnormal(Vector vector) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(Vector vector, T value) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.LastIndexOf(vector.AsVector512(), value); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.LastIndexOf(vector.AsVector256(), value); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.LastIndexOf(vector.AsVector128(), value); } } @@ -1849,7 +1867,7 @@ public static bool LessThanOrEqualAny(Vector left, Vector right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static Vector Load(T* source) => LoadUnsafe(ref *source); + public static unsafe Vector Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. /// The type of the elements in the vector. @@ -1859,7 +1877,7 @@ public static bool LessThanOrEqualAny(Vector left, Vector right) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector LoadAligned(T* source) + public static unsafe Vector LoadAligned(T* source) { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -1879,7 +1897,7 @@ public static Vector LoadAligned(T* source) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static Vector LoadAlignedNonTemporal(T* source) => LoadAligned(source); + public static unsafe Vector LoadAlignedNonTemporal(T* source) => LoadAligned(source); /// Loads a vector from the given source. /// The type of the elements in the vector. @@ -2296,184 +2314,143 @@ public static Vector MultiplyAddEstimate(Vector left, VectorNarrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector Narrow(Vector low, Vector high) + internal static Vector Narrow(Vector low, Vector high) + where TSource : INumber + where TResult : INumber { - Unsafe.SkipInit(out Vector result); + Unsafe.SkipInit(out Vector result); - for (int i = 0; i < Vector.Count; i++) + for (int i = 0; i < Vector.Count; i++) { - float value = (float)low.GetElementUnsafe(i); + TResult value = TResult.CreateTruncating(low.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } - for (int i = Vector.Count; i < Vector.Count; i++) + for (int i = Vector.Count; i < Vector.Count; i++) { - float value = (float)high.GetElementUnsafe(i - Vector.Count); + TResult value = TResult.CreateTruncating(high.GetElementUnsafe(i - Vector.Count)); result.SetElementUnsafe(i, value); } return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector Narrow(Vector low, Vector high) + => Narrow(low, high); + + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Narrow(Vector low, Vector high) - { - Unsafe.SkipInit(out Vector result); - - for (int i = 0; i < Vector.Count; i++) - { - sbyte value = (sbyte)low.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } - - for (int i = Vector.Count; i < Vector.Count; i++) - { - sbyte value = (sbyte)high.GetElementUnsafe(i - Vector.Count); - result.SetElementUnsafe(i, value); - } + => Narrow(low, high); - return result; - } - - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Narrow(Vector low, Vector high) - { - Unsafe.SkipInit(out Vector result); - - for (int i = 0; i < Vector.Count; i++) - { - short value = (short)low.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } - - for (int i = Vector.Count; i < Vector.Count; i++) - { - short value = (short)high.GetElementUnsafe(i - Vector.Count); - result.SetElementUnsafe(i, value); - } - - return result; - } + => Narrow(low, high); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Narrow(Vector low, Vector high) - { - Unsafe.SkipInit(out Vector result); - - for (int i = 0; i < Vector.Count; i++) - { - int value = (int)low.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } + => Narrow(low, high); - for (int i = Vector.Count; i < Vector.Count; i++) - { - int value = (int)high.GetElementUnsafe(i - Vector.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector Narrow(Vector low, Vector high) + => Narrow(low, high); - return result; - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector Narrow(Vector low, Vector high) + => Narrow(low, high); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector Narrow(Vector low, Vector high) + public static Vector Narrow(Vector low, Vector high) + => Narrow(low, high); + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector NarrowWithSaturation(Vector low, Vector high) + where TSource : INumber + where TResult : INumber { - Unsafe.SkipInit(out Vector result); + Unsafe.SkipInit(out Vector result); - for (int i = 0; i < Vector.Count; i++) + for (int i = 0; i < Vector.Count; i++) { - byte value = (byte)low.GetElementUnsafe(i); + TResult value = TResult.CreateSaturating(low.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } - for (int i = Vector.Count; i < Vector.Count; i++) + for (int i = Vector.Count; i < Vector.Count; i++) { - byte value = (byte)high.GetElementUnsafe(i - Vector.Count); + TResult value = TResult.CreateSaturating(high.GetElementUnsafe(i - Vector.Count)); result.SetElementUnsafe(i, value); } return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] - [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector Narrow(Vector low, Vector high) - { - Unsafe.SkipInit(out Vector result); + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - for (int i = 0; i < Vector.Count; i++) - { - ushort value = (ushort)low.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - for (int i = Vector.Count; i < Vector.Count; i++) - { - ushort value = (ushort)high.GetElementUnsafe(i - Vector.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - return result; - } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector Narrow(Vector low, Vector high) - { - Unsafe.SkipInit(out Vector result); - - for (int i = 0; i < Vector.Count; i++) - { - uint value = (uint)low.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - for (int i = Vector.Count; i < Vector.Count; i++) - { - uint value = (uint)high.GetElementUnsafe(i - Vector.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); - return result; - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector NarrowWithSaturation(Vector low, Vector high) + => NarrowWithSaturation(low, high); /// Computes the unary negation of a vector. /// The vector to negate. @@ -2672,17 +2649,17 @@ internal static Vector Round(Vector vector) [Intrinsic] internal static Vector ShiftLeft(Vector vector, Vector shiftCount) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.ShiftLeft(vector.AsVector512(), shiftCount.AsVector512()).AsVector(); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.ShiftLeft(vector.AsVector256(), shiftCount.AsVector256()).AsVector(); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.ShiftLeft(vector.AsVector128(), shiftCount.AsVector128()).AsVector(); } } @@ -2698,17 +2675,17 @@ internal static Vector ShiftLeft(Vector vector, Vector shiftCo [Intrinsic] internal static Vector ShiftLeft(Vector vector, Vector shiftCount) { - if (sizeof(Vector) == sizeof(Vector512)) + if (Vector.Count == Vector512.Count) { return Vector512.ShiftLeft(vector.AsVector512(), shiftCount.AsVector512()).AsVector(); } - else if (sizeof(Vector) == sizeof(Vector256)) + else if (Vector.Count == Vector256.Count) { return Vector256.ShiftLeft(vector.AsVector256(), shiftCount.AsVector256()).AsVector(); } else { - Debug.Assert(sizeof(Vector) == sizeof(Vector128)); + Debug.Assert(Vector.Count == Vector128.Count); return Vector128.ShiftLeft(vector.AsVector128(), shiftCount.AsVector128()).AsVector(); } } @@ -2936,7 +2913,7 @@ public static Vector SquareRoot(Vector value) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static void Store(this Vector source, T* destination) => source.StoreUnsafe(ref *destination); + public static unsafe void Store(this Vector source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. /// The type of the elements in the vector. @@ -2946,7 +2923,7 @@ public static Vector SquareRoot(Vector value) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreAligned(this Vector source, T* destination) + public static unsafe void StoreAligned(this Vector source, T* destination) { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -2966,7 +2943,7 @@ public static void StoreAligned(this Vector source, T* destination) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static void StoreAlignedNonTemporal(this Vector source, T* destination) => source.StoreAligned(destination); + public static unsafe void StoreAlignedNonTemporal(this Vector source, T* destination) => source.StoreAligned(destination); /// Stores a vector at the given destination. /// The type of the elements in the vector. @@ -2998,14 +2975,33 @@ public static void StoreUnsafe(this Vector source, ref T destination, nuin Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); } - /// Subtracts two vectors to compute their difference. - /// The vector from which will be subtracted. - /// The vector to subtract from . - /// The type of the elements in the vector. - /// The difference of and . + /// [Intrinsic] public static Vector Subtract(Vector left, Vector right) => left - right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector SubtractSaturate(Vector left, Vector right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left - right; + } + else + { + Unsafe.SkipInit(out Vector result); + + for (int index = 0; index < Vector.Count; index++) + { + T value = Scalar.SubtractSaturate(left.GetElementUnsafe(index), right.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + } + /// /// Returns the sum of all elements inside the vector. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Scalar.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Scalar.cs index deb6ef6af4d3d7..6ad17964989bfa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Scalar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Scalar.cs @@ -258,6 +258,197 @@ public static T Add(T left, T right) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T AddSaturate(T left, T right) + { + // For unsigned types, addition can only overflow up + // so we need to check if the result is less than one + // of the inputs and return MaxValue if it is. + // + // For signed types, addition can overflow in either + // direction. However, this can only occur if the signs + // of the inputs are the same and the sign of the result + // then differs. This simplifies to, given a = b + c: + // ((a ^ b) & ~(b ^ c)) < 0 + // + // This simplification works because all negative values + // have their most significant bit set, while all positive + // values have it clear. Consider the below: + // + // p = p + p: (0 ^ 0) & ~(0 ^ 0) -> 0 & ~0 -> 0 & 1 -> 0 can overflow and didnt + // p = p + n: (0 ^ 0) & ~(0 ^ 1) -> 0 & ~1 -> 0 & 0 -> 0 cant overflow + // p = n + p: (0 ^ 1) & ~(1 ^ 0) -> 1 & ~1 -> 1 & 0 -> 0 cant overflow + // p = n + n: (0 ^ 1) & ~(1 ^ 1) -> 1 & ~0 -> 1 & 1 -> 1 can overflow and did + // n = p + p: (1 ^ 0) & ~(0 ^ 0) -> 1 & ~0 -> 1 & 1 -> 1 can overflow and did + // n = p + n: (1 ^ 0) & ~(0 ^ 1) -> 1 & ~1 -> 1 & 0 -> 0 cant overflow + // n = n + p: (1 ^ 1) & ~(1 ^ 0) -> 0 & ~1 -> 0 & 0 -> 0 cant overflow + // n = n + n: (1 ^ 1) & ~(1 ^ 1) -> 0 & ~0 -> 0 & 1 -> 0 can overflow and didnt + // |_______| |________| |_| |__| |_| |_| |_| + // | \_________|____\______|___\______|_______ produces 1 if the signs are the same, meaning overflow could occur + // | | | | + // \____________________\___________\__________|_______ produces 1 if the output signs differs from the first input, meaning overflow could occur + // | + // \_______ produces 1 if the input signs are the same and the output sign differs from that, meaning overflow did occur + // + // If we did overflow then a negative result needs to + // become MaxValue, while a positive result needs to become + // MinValue. + + if (typeof(T) == typeof(byte)) + { + byte actualLeft = (byte)(object)left; + byte actualRight = (byte)(object)right; + + byte result = (byte)(actualLeft + actualRight); + + if (result < actualLeft) + { + result = byte.MaxValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(double)) + { + return (T)(object)(double)((double)(object)left + (double)(object)right); + } + else if (typeof(T) == typeof(short)) + { + short actualLeft = (short)(object)left; + short actualRight = (short)(object)right; + + short result = (short)(actualLeft + actualRight); + + if (((result ^ actualLeft) & ~(actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? short.MaxValue : short.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(int)) + { + int actualLeft = (int)(object)left; + int actualRight = (int)(object)right; + + int result = (int)(actualLeft + actualRight); + + if (((result ^ actualLeft) & ~(actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? int.MaxValue : int.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(long)) + { + long actualLeft = (long)(object)left; + long actualRight = (long)(object)right; + + long result = (long)(actualLeft + actualRight); + + if (((result ^ actualLeft) & ~(actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? long.MaxValue : long.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(nint)) + { + nint actualLeft = (nint)(object)left; + nint actualRight = (nint)(object)right; + + nint result = (nint)(actualLeft + actualRight); + + if (((result ^ actualLeft) & ~(actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? nint.MaxValue : nint.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(nuint)) + { + nuint actualLeft = (nuint)(object)left; + nuint actualRight = (nuint)(object)right; + + nuint result = (nuint)(actualLeft + actualRight); + + if (result < actualLeft) + { + result = nuint.MaxValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(sbyte)) + { + sbyte actualLeft = (sbyte)(object)left; + sbyte actualRight = (sbyte)(object)right; + + sbyte result = (sbyte)(actualLeft + actualRight); + + if (((result ^ actualLeft) & ~(actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? sbyte.MaxValue : sbyte.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(float)) + { + return (T)(object)(float)((float)(object)left + (float)(object)right); + } + else if (typeof(T) == typeof(ushort)) + { + ushort actualLeft = (ushort)(object)left; + ushort actualRight = (ushort)(object)right; + + ushort result = (ushort)(actualLeft + actualRight); + + if (result < actualLeft) + { + result = ushort.MaxValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(uint)) + { + uint actualLeft = (uint)(object)left; + uint actualRight = (uint)(object)right; + + uint result = (uint)(actualLeft + actualRight); + + if (result < actualLeft) + { + result = uint.MaxValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(ulong)) + { + ulong actualLeft = (ulong)(object)left; + ulong actualRight = (ulong)(object)right; + + ulong result = (ulong)(actualLeft + actualRight); + + if (result < actualLeft) + { + result = ulong.MaxValue; + } + + return (T)(object)result; + } + else + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported); + return default!; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Ceiling(T value) { @@ -1579,6 +1770,196 @@ public static T Subtract(T left, T right) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SubtractSaturate(T left, T right) + { + // For unsigned types, subtraction can only overflow up + // so we need to check if the result is greater than the + // first input and return MinValue if it is. + // + // For signed types, subtraction can overflow in either + // direction. However, this can only occur if the signs + // of the inputs differ and the sign of the result + // then differs from the first input. This simplifies to: ((r ^ l) & (l ^ r)) < 0 + // + // This simplification works because all negative values + // have their most significant bit set, while all positive + // values have it clear. Consider the below: + // + // p = p - p: (0 ^ 0) & (0 ^ 0) -> 0 & 0 -> 0 cant overflow + // p = p - n: (0 ^ 0) & (0 ^ 1) -> 0 & 1 -> 0 can overflow and didn't + // p = n - p: (0 ^ 1) & (1 ^ 0) -> 1 & 1 -> 1 can overflow and did + // p = n - n: (0 ^ 1) & (1 ^ 1) -> 1 & 0 -> 0 cant overflow + // n = p - p: (1 ^ 0) & (0 ^ 0) -> 1 & 0 -> 0 cant overflow + // n = p - n: (1 ^ 0) & (0 ^ 1) -> 1 & 1 -> 1 can overflow and did + // n = n - p: (1 ^ 1) & (1 ^ 0) -> 0 & 1 -> 0 can overflow and didn't + // n = n - n: (1 ^ 1) & (1 ^ 1) -> 0 & 0 -> 0 cant overflow + // |_______| |_______| |_| |_| |_| + // | \________|___\______|_______ produces 1 if the signs are the differ, meaning overflow could occur + // | | | + // \___________________\__________|_______ produces 1 if the output signs differs from the first input, meaning overflow could occur + // | + // \_______ produces 1 if the input signs are the same and the output sign differs from that, meaning overflow did occur + // + // If we did overflow then a negative result needs to + // become MaxValue, while a positive result needs to become + // MinValue. + + if (typeof(T) == typeof(byte)) + { + byte actualLeft = (byte)(object)left; + byte actualRight = (byte)(object)right; + + byte result = (byte)(actualLeft - actualRight); + + if (result > actualLeft) + { + result = byte.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(double)) + { + return (T)(object)(double)((double)(object)left - (double)(object)right); + } + else if (typeof(T) == typeof(short)) + { + short actualLeft = (short)(object)left; + short actualRight = (short)(object)right; + + short result = (short)(actualLeft - actualRight); + + if (((result ^ actualLeft) & (actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? short.MaxValue : short.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(int)) + { + int actualLeft = (int)(object)left; + int actualRight = (int)(object)right; + + int result = (int)(actualLeft - actualRight); + + if (((result ^ actualLeft) & (actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? int.MaxValue : int.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(long)) + { + long actualLeft = (long)(object)left; + long actualRight = (long)(object)right; + + long result = (long)(actualLeft - actualRight); + + if (((result ^ actualLeft) & (actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? long.MaxValue : long.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(nint)) + { + nint actualLeft = (nint)(object)left; + nint actualRight = (nint)(object)right; + + nint result = (nint)(actualLeft - actualRight); + + if (((result ^ actualLeft) & (actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? nint.MaxValue : nint.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(nuint)) + { + nuint actualLeft = (nuint)(object)left; + nuint actualRight = (nuint)(object)right; + + nuint result = (nuint)(actualLeft - actualRight); + + if (result > actualLeft) + { + result = nuint.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(sbyte)) + { + sbyte actualLeft = (sbyte)(object)left; + sbyte actualRight = (sbyte)(object)right; + + sbyte result = (sbyte)(actualLeft - actualRight); + + if (((result ^ actualLeft) & (actualLeft ^ actualRight)) < 0) + { + result = (result < 0) ? sbyte.MaxValue : sbyte.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(float)) + { + return (T)(object)(float)((float)(object)left - (float)(object)right); + } + else if (typeof(T) == typeof(ushort)) + { + ushort actualLeft = (ushort)(object)left; + ushort actualRight = (ushort)(object)right; + + ushort result = (ushort)(actualLeft - actualRight); + + if (result > actualLeft) + { + result = ushort.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(uint)) + { + uint actualLeft = (uint)(object)left; + uint actualRight = (uint)(object)right; + + uint result = (uint)(actualLeft - actualRight); + + if (result > actualLeft) + { + result = uint.MinValue; + } + + return (T)(object)result; + } + else if (typeof(T) == typeof(ulong)) + { + ulong actualLeft = (ulong)(object)left; + ulong actualRight = (ulong)(object)right; + + ulong result = (ulong)(actualLeft - actualRight); + + if (result > actualLeft) + { + result = ulong.MinValue; + } + + return (T)(object)result; + } + else + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported); + return default!; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Truncate(T value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 47f20592da76ca..e8a8abe86edbbf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -75,15 +75,38 @@ public static Vector128 Abs(Vector128 vector) } } - /// Adds two vectors to compute their sum. + /// Adds two vectors to compute their element-wise sum. /// The type of the elements in the vector. /// The vector to add with . /// The vector to add with . - /// The sum of and . + /// The element-wise sum of and . /// The type of and () is not supported. [Intrinsic] public static Vector128 Add(Vector128 left, Vector128 right) => left + right; + /// Adds two vectors to compute their element-wise saturated sum. + /// The type of the elements in the vector. + /// The vector to add with . + /// The vector to add with . + /// The element-wise saturated sum of and . + /// The type of and () is not supported. + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 AddSaturate(Vector128 left, Vector128 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left + right; + } + else + { + return Create( + Vector64.AddSaturate(left._lower, right._lower), + Vector64.AddSaturate(left._upper, right._upper) + ); + } + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -356,7 +379,7 @@ public static Vector128 ClampNative(Vector128 value, Vector128 min, /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToDouble(Vector128 vector) + public static Vector128 ConvertToDouble(Vector128 vector) { if (Sse2.IsSupported) { @@ -397,7 +420,7 @@ public static unsafe Vector128 ConvertToDouble(Vector128 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToDouble(Vector128 vector) + public static Vector128 ConvertToDouble(Vector128 vector) { if (Sse2.IsSupported) { @@ -437,7 +460,7 @@ public static unsafe Vector128 ConvertToDouble(Vector128 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToInt32(Vector128 vector) + public static Vector128 ConvertToInt32(Vector128 vector) { return Create( Vector64.ConvertToInt32(vector._lower), @@ -450,7 +473,7 @@ public static unsafe Vector128 ConvertToInt32(Vector128 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToInt32Native(Vector128 vector) + public static Vector128 ConvertToInt32Native(Vector128 vector) { return Create( Vector64.ConvertToInt32Native(vector._lower), @@ -463,7 +486,7 @@ public static unsafe Vector128 ConvertToInt32Native(Vector128 vector /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToInt64(Vector128 vector) + public static Vector128 ConvertToInt64(Vector128 vector) { return Create( Vector64.ConvertToInt64(vector._lower), @@ -476,7 +499,7 @@ public static unsafe Vector128 ConvertToInt64(Vector128 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToInt64Native(Vector128 vector) + public static Vector128 ConvertToInt64Native(Vector128 vector) { return Create( Vector64.ConvertToInt64Native(vector._lower), @@ -489,7 +512,7 @@ public static unsafe Vector128 ConvertToInt64Native(Vector128 vect /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToSingle(Vector128 vector) + public static Vector128 ConvertToSingle(Vector128 vector) { return Create( Vector64.ConvertToSingle(vector._lower), @@ -503,7 +526,7 @@ public static unsafe Vector128 ConvertToSingle(Vector128 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToSingle(Vector128 vector) + public static Vector128 ConvertToSingle(Vector128 vector) { if (Sse2.IsSupported) { @@ -560,7 +583,7 @@ static Vector128 SoftwareFallback(Vector128 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToUInt32(Vector128 vector) + public static Vector128 ConvertToUInt32(Vector128 vector) { return Create( Vector64.ConvertToUInt32(vector._lower), @@ -574,7 +597,7 @@ public static unsafe Vector128 ConvertToUInt32(Vector128 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToUInt32Native(Vector128 vector) + public static Vector128 ConvertToUInt32Native(Vector128 vector) { return Create( Vector64.ConvertToUInt32Native(vector._lower), @@ -588,7 +611,7 @@ public static unsafe Vector128 ConvertToUInt32Native(Vector128 vect [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToUInt64(Vector128 vector) + public static Vector128 ConvertToUInt64(Vector128 vector) { return Create( Vector64.ConvertToUInt64(vector._lower), @@ -602,7 +625,7 @@ public static unsafe Vector128 ConvertToUInt64(Vector128 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ConvertToUInt64Native(Vector128 vector) + public static Vector128 ConvertToUInt64Native(Vector128 vector) { return Create( Vector64.ConvertToUInt64Native(vector._lower), @@ -666,7 +689,7 @@ public static void CopyTo(this Vector128 vector, T[] destination) /// The type of and () is not supported. /// is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(this Vector128 vector, T[] destination, int startIndex) + public static void CopyTo(this Vector128 vector, T[] destination, int startIndex) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons @@ -771,7 +794,7 @@ public static int CountWhereAllBitsSet(Vector128 vector) /// A new with all elements initialized to . /// The type of () is not supported. [Intrinsic] - public static unsafe Vector128 Create(T value) + public static Vector128 Create(T value) { Vector64 vector = Vector64.Create(value); return Create(vector, vector); @@ -782,48 +805,48 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi8 [Intrinsic] - public static unsafe Vector128 Create(byte value) => Create(value); + public static Vector128 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . /// On x86, this method corresponds to __m128d _mm_set1_pd [Intrinsic] - public static unsafe Vector128 Create(double value) => Create(value); + public static Vector128 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi16 [Intrinsic] - public static unsafe Vector128 Create(short value) => Create(value); + public static Vector128 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi32 [Intrinsic] - public static unsafe Vector128 Create(int value) => Create(value); + public static Vector128 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi64x [Intrinsic] - public static unsafe Vector128 Create(long value) => Create(value); + public static Vector128 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector128 Create(nint value) => Create(value); + public static Vector128 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 Create(nuint value) => Create(value); + public static Vector128 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -831,14 +854,14 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi8 [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 Create(sbyte value) => Create(value); + public static Vector128 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . /// On x86, this method corresponds to __m128 _mm_set1_ps [Intrinsic] - public static unsafe Vector128 Create(float value) => Create(value); + public static Vector128 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -846,7 +869,7 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi16 [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 Create(ushort value) => Create(value); + public static Vector128 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -854,7 +877,7 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi32 [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 Create(uint value) => Create(value); + public static Vector128 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -862,7 +885,7 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi64x [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 Create(ulong value) => Create(value); + public static Vector128 Create(ulong value) => Create(value); /// Creates a new from a given array. /// The type of the elements in the vector. @@ -943,7 +966,7 @@ public static Vector128 Create(ReadOnlySpan values) /// On x86, this method corresponds to __m128i _mm_setr_epi8 [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(byte e0, byte e1, byte e2, byte e3, byte e4, byte e5, byte e6, byte e7, byte e8, byte e9, byte e10, byte e11, byte e12, byte e13, byte e14, byte e15) + public static Vector128 Create(byte e0, byte e1, byte e2, byte e3, byte e4, byte e5, byte e6, byte e7, byte e8, byte e9, byte e10, byte e11, byte e12, byte e13, byte e14, byte e15) { return Create( Vector64.Create(e0, e1, e2, e3, e4, e5, e6, e7), @@ -958,7 +981,7 @@ public static unsafe Vector128 Create(byte e0, byte e1, byte e2, byte e3, /// On x86, this method corresponds to __m128d _mm_setr_pd [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(double e0, double e1) + public static Vector128 Create(double e0, double e1) { return Create( Vector64.Create(e0), @@ -979,7 +1002,7 @@ public static unsafe Vector128 Create(double e0, double e1) /// On x86, this method corresponds to __m128i _mm_setr_epi16 [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) + public static Vector128 Create(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) { return Create( Vector64.Create(e0, e1, e2, e3), @@ -996,7 +1019,7 @@ public static unsafe Vector128 Create(short e0, short e1, short e2, short /// On x86, this method corresponds to __m128i _mm_setr_epi32 [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(int e0, int e1, int e2, int e3) + public static Vector128 Create(int e0, int e1, int e2, int e3) { return Create( Vector64.Create(e0, e1), @@ -1011,7 +1034,7 @@ public static unsafe Vector128 Create(int e0, int e1, int e2, int e3) /// On x86, this method corresponds to __m128i _mm_setr_epi64x [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(long e0, long e1) + public static Vector128 Create(long e0, long e1) { return Create( Vector64.Create(e0), @@ -1041,7 +1064,7 @@ public static unsafe Vector128 Create(long e0, long e1) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(sbyte e0, sbyte e1, sbyte e2, sbyte e3, sbyte e4, sbyte e5, sbyte e6, sbyte e7, sbyte e8, sbyte e9, sbyte e10, sbyte e11, sbyte e12, sbyte e13, sbyte e14, sbyte e15) + public static Vector128 Create(sbyte e0, sbyte e1, sbyte e2, sbyte e3, sbyte e4, sbyte e5, sbyte e6, sbyte e7, sbyte e8, sbyte e9, sbyte e10, sbyte e11, sbyte e12, sbyte e13, sbyte e14, sbyte e15) { return Create( Vector64.Create(e0, e1, e2, e3, e4, e5, e6, e7), @@ -1058,7 +1081,7 @@ public static unsafe Vector128 Create(sbyte e0, sbyte e1, sbyte e2, sbyte /// On x86, this method corresponds to __m128 _mm_setr_ps [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(float e0, float e1, float e2, float e3) + public static Vector128 Create(float e0, float e1, float e2, float e3) { return Create( Vector64.Create(e0, e1), @@ -1080,7 +1103,7 @@ public static unsafe Vector128 Create(float e0, float e1, float e2, float [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(ushort e0, ushort e1, ushort e2, ushort e3, ushort e4, ushort e5, ushort e6, ushort e7) + public static Vector128 Create(ushort e0, ushort e1, ushort e2, ushort e3, ushort e4, ushort e5, ushort e6, ushort e7) { return Create( Vector64.Create(e0, e1, e2, e3), @@ -1098,7 +1121,7 @@ public static unsafe Vector128 Create(ushort e0, ushort e1, ushort e2, u [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(uint e0, uint e1, uint e2, uint e3) + public static Vector128 Create(uint e0, uint e1, uint e2, uint e3) { return Create( Vector64.Create(e0, e1), @@ -1114,7 +1137,7 @@ public static unsafe Vector128 Create(uint e0, uint e1, uint e2, uint e3) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Create(ulong e0, ulong e1) + public static Vector128 Create(ulong e0, ulong e1) { return Create( Vector64.Create(e0), @@ -1159,65 +1182,65 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// On x86, this method corresponds to __m128i _mm_setr_epi64 /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. @@ -1225,14 +1248,14 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// On x86, this method corresponds to __m128i _mm_setr_epi64 /// A new initialized from and . [CLSCompliant(false)] - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); + public static Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The type of the elements in the vector. @@ -1240,84 +1263,84 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. /// The type of () is not supported. [Intrinsic] - public static unsafe Vector128 CreateScalar(T value) => Vector64.CreateScalar(value).ToVector128(); + public static Vector128 CreateScalar(T value) => Vector64.CreateScalar(value).ToVector128(); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(byte value) => CreateScalar(value); + public static Vector128 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(double value) => CreateScalar(value); + public static Vector128 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(short value) => CreateScalar(value); + public static Vector128 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(int value) => CreateScalar(value); + public static Vector128 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(long value) => CreateScalar(value); + public static Vector128 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(nint value) => CreateScalar(value); + public static Vector128 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalar(nuint value) => CreateScalar(value); + public static Vector128 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalar(sbyte value) => CreateScalar(value); + public static Vector128 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector128 CreateScalar(float value) => CreateScalar(value); + public static Vector128 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalar(ushort value) => CreateScalar(value); + public static Vector128 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalar(uint value) => CreateScalar(value); + public static Vector128 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalar(ulong value) => CreateScalar(value); + public static Vector128 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The type of the elements in the vector. @@ -1342,78 +1365,78 @@ public static Vector128 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector128 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector128 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); + public static Vector128 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. /// The type of the elements in the vector. @@ -2591,108 +2614,193 @@ public static Vector128 MultiplyAddEstimate(Vector128 left, Vector ); } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) + internal static Vector128 Narrow(Vector128 lower, Vector128 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector128 result); + + for (int i = 0; i < Vector128.Count; i++) + { + TResult value = TResult.CreateTruncating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector128.Count; i < Vector128.Count; i++) + { + TResult value = TResult.CreateTruncating(upper.GetElementUnsafe(i - Vector128.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is saturation. + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); + + /// Narrows two vector of instances into one vector of . + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) - { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); - } + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) - { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); - } + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) - { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); - } + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) - { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); - } + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) - { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); - } + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . + /// Narrows two vector of instances into one vector of . /// The vector that will be narrowed to the lower half of the result vector. /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// A vector of containing elements narrowed from and . + /// This uses the default conversion behavior for to , which is truncation. [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 Narrow(Vector128 lower, Vector128 upper) + public static Vector128 Narrow(Vector128 lower, Vector128 upper) + => Narrow(lower, upper); + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector64.Narrow(lower._lower, lower._upper), - Vector64.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector128 result); + + for (int i = 0; i < Vector128.Count; i++) + { + TResult value = TResult.CreateSaturating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector128.Count; i < Vector128.Count; i++) + { + TResult value = TResult.CreateSaturating(upper.GetElementUnsafe(i - Vector128.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + + /// Narrows two vector of instances into one vector of using a saturating conversion. + /// The vector that will be narrowed to the lower half of the result vector. + /// The vector that will be narrowed to the upper half of the result vector. + /// A vector of containing elements narrowed with saturation from and . + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 NarrowWithSaturation(Vector128 lower, Vector128 upper) + => NarrowWithSaturation(lower, upper); + /// Negates a vector. /// The type of the elements in the vector. /// The vector to negate. @@ -2890,7 +2998,7 @@ internal static Vector128 ShiftLeft(Vector128 vector, Vector128 ShiftLeft(Vector128 vector, Vector128
    (this Vector128 source, ref T destination, n Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); } - /// Subtracts two vectors to compute their difference. + /// Subtracts two vectors to compute their element-wise difference. /// The type of the elements in the vector. /// The vector from which will be subtracted. /// The vector to subtract from . - /// The difference of and . + /// The element-wise difference of and . /// The type of and () is not supported. [Intrinsic] public static Vector128 Subtract(Vector128 left, Vector128 right) => left - right; + /// Subtracts two vectors to compute their element-wise saturated difference. + /// The type of the elements in the vector. + /// The vector to from which will be subtracted. + /// The vector to subtract from . + /// The element-wise saturated difference of and . + /// The type of and () is not supported. + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 SubtractSaturate(Vector128 left, Vector128 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left - right; + } + else + { + return Create( + Vector64.SubtractSaturate(left._lower, right._lower), + Vector64.SubtractSaturate(left._upper, right._upper) + ); + } + } + /// Computes the sum of all elements in a vector. /// The type of the elements in the vector. /// The vector whose elements will be summed. @@ -3844,7 +3975,7 @@ public static Vector256 ToVector256(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector256 ToVector256Unsafe(this Vector128 vector) + public static Vector256 ToVector256Unsafe(this Vector128 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -3913,46 +4044,46 @@ public static bool TryCopyTo(this Vector128 vector, Span destination) /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector128 Lower, Vector128 Upper) Widen(Vector128 source) => (WidenLower(source), WidenUpper(source)); /// Widens the lower half of a into a . /// The vector whose elements are to be widened. @@ -3975,7 +4106,7 @@ public static Vector128 WidenLower(Vector128 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -3990,7 +4121,7 @@ public static unsafe Vector128 WidenLower(Vector128 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -4006,7 +4137,7 @@ public static unsafe Vector128 WidenLower(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -4021,7 +4152,7 @@ public static unsafe Vector128 WidenLower(Vector128 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -4037,7 +4168,7 @@ public static unsafe Vector128 WidenLower(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -4053,7 +4184,7 @@ public static unsafe Vector128 WidenLower(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenLower(Vector128 source) + public static Vector128 WidenLower(Vector128 source) { Vector64 lower = source._lower; @@ -4084,7 +4215,7 @@ public static Vector128 WidenUpper(Vector128 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4099,7 +4230,7 @@ public static unsafe Vector128 WidenUpper(Vector128 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4115,7 +4246,7 @@ public static unsafe Vector128 WidenUpper(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4130,7 +4261,7 @@ public static unsafe Vector128 WidenUpper(Vector128 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4146,7 +4277,7 @@ public static unsafe Vector128 WidenUpper(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4162,7 +4293,7 @@ public static unsafe Vector128 WidenUpper(Vector128 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 WidenUpper(Vector128 source) + public static Vector128 WidenUpper(Vector128 source) { Vector64 upper = source._upper; @@ -4290,70 +4421,5 @@ internal static Vector128 UnpackHigh(Vector128 left, Vector128 } return AdvSimd.Arm64.ZipHigh(left, right); } - - // TODO: Make generic versions of these public, see https://github.com/dotnet/runtime/issues/82559 - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] - [CompExactlyDependsOn(typeof(Sse2))] - [CompExactlyDependsOn(typeof(PackedSimd))] - internal static Vector128 AddSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.AddSaturate(left, right); - } - else if (PackedSimd.IsSupported) - { - return PackedSimd.AddSaturate(left, right); - } - else if (!AdvSimd.Arm64.IsSupported) - { - ThrowHelper.ThrowNotSupportedException(); - } - return AdvSimd.AddSaturate(left, right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] - [CompExactlyDependsOn(typeof(Sse2))] - [CompExactlyDependsOn(typeof(PackedSimd))] - internal static Vector128 SubtractSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.SubtractSaturate(left, right); - } - else if (PackedSimd.IsSupported) - { - return PackedSimd.SubtractSaturate(left, right); - } - else if (!AdvSimd.Arm64.IsSupported) - { - ThrowHelper.ThrowNotSupportedException(); - } - return AdvSimd.SubtractSaturate(left, right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] - [CompExactlyDependsOn(typeof(Sse2))] - [CompExactlyDependsOn(typeof(PackedSimd))] - internal static Vector128 AddSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.AddSaturate(left, right); - } - else if (PackedSimd.IsSupported) - { - return PackedSimd.AddSaturate(left, right); - } - else if (!AdvSimd.Arm64.IsSupported) - { - ThrowHelper.ThrowNotSupportedException(); - } - return AdvSimd.AddSaturate(left, right); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index fd6e96bb18f2ba..5387d20943313f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace System.Runtime.Intrinsics @@ -29,7 +28,7 @@ namespace System.Runtime.Intrinsics // the internal inlining limits of the JIT. /// Provides a collection of static methods for creating, manipulating, and otherwise operating on 256-bit vectors. - public static unsafe class Vector256 + public static class Vector256 { internal const int Size = 32; @@ -79,15 +78,28 @@ public static Vector256 Abs(Vector256 vector) } } - /// Adds two vectors to compute their sum. - /// The type of the elements in the vector. - /// The vector to add with . - /// The vector to add with . - /// The sum of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector256 Add(Vector256 left, Vector256 right) => left + right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSaturate(Vector256 left, Vector256 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left + right; + } + else + { + return Create( + Vector128.AddSaturate(left._lower, right._lower), + Vector128.AddSaturate(left._upper, right._upper) + ); + } + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2315,7 +2327,7 @@ public static bool LessThanOrEqualAny(Vector256 left, Vector256 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static Vector256 Load(T* source) => LoadUnsafe(ref *source); + public static unsafe Vector256 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. /// The type of the elements in the vector. @@ -2325,7 +2337,7 @@ public static bool LessThanOrEqualAny(Vector256 left, Vector256 right) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 LoadAligned(T* source) + public static unsafe Vector256 LoadAligned(T* source) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -2345,7 +2357,7 @@ public static Vector256 LoadAligned(T* source) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - public static Vector256 LoadAlignedNonTemporal(T* source) => LoadAligned(source); + public static unsafe Vector256 LoadAlignedNonTemporal(T* source) => LoadAligned(source); /// Loads a vector from the given source. /// The type of the elements in the vector. @@ -2695,108 +2707,144 @@ public static Vector256 MultiplyAddEstimate(Vector256 left, Vector ); } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Narrow(Vector256 lower, Vector256 upper) + internal static Vector256 Narrow(Vector256 lower, Vector256 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector256 result); + + for (int i = 0; i < Vector256.Count; i++) + { + TResult value = TResult.CreateTruncating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector256.Count; i < Vector256.Count; i++) + { + TResult value = TResult.CreateTruncating(upper.GetElementUnsafe(i - Vector256.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Narrow(Vector256 lower, Vector256 upper) + => Narrow(lower, upper); + + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) - { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) - { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) - { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) - { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) - { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Narrow(Vector256 lower, Vector256 upper) + => Narrow(lower, upper); + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector128.Narrow(lower._lower, lower._upper), - Vector128.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector256 result); + + for (int i = 0; i < Vector256.Count; i++) + { + TResult value = TResult.CreateSaturating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector256.Count; i < Vector256.Count; i++) + { + TResult value = TResult.CreateSaturating(upper.GetElementUnsafe(i - Vector256.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NarrowWithSaturation(Vector256 lower, Vector256 upper) + => NarrowWithSaturation(lower, upper); + /// Negates a vector. /// The type of the elements in the vector. /// The vector to negate. @@ -2994,7 +3042,7 @@ internal static Vector256 ShiftLeft(Vector256 vector, Vector256 ShiftLeft(Vector256 vector, Vector256
      Sqrt(Vector256 vector) /// The type of and () is not supported. [Intrinsic] [CLSCompliant(false)] - public static void Store(this Vector256 source, T* destination) => source.StoreUnsafe(ref *destination); + public static unsafe void Store(this Vector256 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. /// The type of the elements in the vector. @@ -3766,7 +3814,7 @@ public static Vector256 Sqrt(Vector256 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreAligned(this Vector256 source, T* destination) + public static unsafe void StoreAligned(this Vector256 source, T* destination) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -3786,7 +3834,7 @@ public static void StoreAligned(this Vector256 source, T* destination) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - public static void StoreAlignedNonTemporal(this Vector256 source, T* destination) => source.StoreAligned(destination); + public static unsafe void StoreAlignedNonTemporal(this Vector256 source, T* destination) => source.StoreAligned(destination); /// Stores a vector at the given destination. /// The type of the elements in the vector. @@ -3818,15 +3866,28 @@ public static void StoreUnsafe(this Vector256 source, ref T destination, n Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); } - /// Subtracts two vectors to compute their difference. - /// The vector from which will be subtracted. - /// The vector to subtract from . - /// The type of the elements in the vector. - /// The difference of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector256 Subtract(Vector256 left, Vector256 right) => left - right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSaturate(Vector256 left, Vector256 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left - right; + } + else + { + return Create( + Vector128.SubtractSaturate(left._lower, right._lower), + Vector128.SubtractSaturate(left._upper, right._upper) + ); + } + } + /// Computes the sum of all elements in a vector. /// The vector whose elements will be summed. /// The type of the elements in the vector. @@ -3878,7 +3939,7 @@ public static Vector512 ToVector512(this Vector256 vector) /// A new with the lower 256-bits set to the value of and the upper 256-bits left uninitialized. /// The type of () is not supported. [Intrinsic] - public static unsafe Vector512 ToVector512Unsafe(this Vector256 vector) + public static Vector512 ToVector512Unsafe(this Vector256 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index c7bca739671e8d..c2811af824930d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -5,8 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; namespace System.Runtime.Intrinsics { @@ -29,7 +27,7 @@ namespace System.Runtime.Intrinsics // the internal inlining limits of the JIT. /// Provides a collection of static methods for creating, manipulating, and otherwise operting on 512-bit vectors. - public static unsafe class Vector512 + public static class Vector512 { internal const int Size = 64; @@ -79,15 +77,28 @@ public static Vector512 Abs(Vector512 vector) } } - /// Adds two vectors to compute their sum. - /// The type of the elements in the vector. - /// The vector to add with . - /// The vector to add with . - /// The sum of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector512 Add(Vector512 left, Vector512 right) => left + right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSaturate(Vector512 left, Vector512 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left + right; + } + else + { + return Create( + Vector256.AddSaturate(left._lower, right._lower), + Vector256.AddSaturate(left._upper, right._upper) + ); + } + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2342,7 +2353,7 @@ public static bool LessThanOrEqualAny(Vector512 left, Vector512 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - public static Vector512 Load(T* source) => LoadUnsafe(ref *source); + public static unsafe Vector512 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. /// The type of the elements in the vector. @@ -2352,7 +2363,7 @@ public static bool LessThanOrEqualAny(Vector512 left, Vector512 right) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 LoadAligned(T* source) + public static unsafe Vector512 LoadAligned(T* source) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -2372,7 +2383,7 @@ public static Vector512 LoadAligned(T* source) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - public static Vector512 LoadAlignedNonTemporal(T* source) => LoadAligned(source); + public static unsafe Vector512 LoadAlignedNonTemporal(T* source) => LoadAligned(source); /// Loads a vector from the given source. /// The type of the elements in the vector. @@ -2721,108 +2732,144 @@ public static Vector512 MultiplyAddEstimate(Vector512 left, Vector ); } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Narrow(Vector512 lower, Vector512 upper) + internal static Vector512 Narrow(Vector512 lower, Vector512 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector512 result); + + for (int i = 0; i < Vector512.Count; i++) + { + TResult value = TResult.CreateTruncating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector512.Count; i < Vector512.Count; i++) + { + TResult value = TResult.CreateTruncating(upper.GetElementUnsafe(i - Vector512.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Narrow(Vector512 lower, Vector512 upper) + => Narrow(lower, upper); + + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) - { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) - { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) - { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) - { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) - { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); - } + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Narrow(Vector512 lower, Vector512 upper) + => Narrow(lower, upper); + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + where TSource : INumber + where TResult : INumber { - return Create( - Vector256.Narrow(lower._lower, lower._upper), - Vector256.Narrow(upper._lower, upper._upper) - ); + Unsafe.SkipInit(out Vector512 result); + + for (int i = 0; i < Vector512.Count; i++) + { + TResult value = TResult.CreateSaturating(lower.GetElementUnsafe(i)); + result.SetElementUnsafe(i, value); + } + + for (int i = Vector512.Count; i < Vector512.Count; i++) + { + TResult value = TResult.CreateSaturating(upper.GetElementUnsafe(i - Vector512.Count)); + result.SetElementUnsafe(i, value); + } + + return result; } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NarrowWithSaturation(Vector512 lower, Vector512 upper) + => NarrowWithSaturation(lower, upper); + /// Negates a vector. /// The type of the elements in the vector. /// The vector to negate. @@ -3020,7 +3067,7 @@ internal static Vector512 ShiftLeft(Vector512 vector, Vector512 ShiftLeft(Vector512 vector, Vector512
        Sqrt(Vector512 vector) /// The type of and () is not supported. [Intrinsic] [CLSCompliant(false)] - public static void Store(this Vector512 source, T* destination) => source.StoreUnsafe(ref *destination); + public static unsafe void Store(this Vector512 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. /// The type of the elements in the vector. @@ -3778,7 +3825,7 @@ public static Vector512 Sqrt(Vector512 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreAligned(this Vector512 source, T* destination) + public static unsafe void StoreAligned(this Vector512 source, T* destination) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -3798,7 +3845,7 @@ public static void StoreAligned(this Vector512 source, T* destination) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - public static void StoreAlignedNonTemporal(this Vector512 source, T* destination) => source.StoreAligned(destination); + public static unsafe void StoreAlignedNonTemporal(this Vector512 source, T* destination) => source.StoreAligned(destination); /// Stores a vector at the given destination. /// The type of the elements in the vector. @@ -3830,15 +3877,28 @@ public static void StoreUnsafe(this Vector512 source, ref T destination, n Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); } - /// Subtracts two vectors to compute their difference. - /// The vector from which will be subtracted. - /// The vector to subtract from . - /// The type of the elements in the vector. - /// The difference of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector512 Subtract(Vector512 left, Vector512 right) => left - right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSaturate(Vector512 left, Vector512 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left - right; + } + else + { + return Create( + Vector256.SubtractSaturate(left._lower, right._lower), + Vector256.SubtractSaturate(left._upper, right._upper) + ); + } + } + /// Computes the sum of all elements in a vector. /// The vector whose elements will be summed. /// The type of the elements in the vector. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index e0ae9bc4cc35b0..5c49722a1fa36b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -5,9 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; -using System.Text; namespace System.Runtime.Intrinsics { @@ -58,15 +55,33 @@ public static Vector64 Abs(Vector64 vector) } } - /// Adds two vectors to compute their sum. - /// The type of the elements in the vector. - /// The vector to add with . - /// The vector to add with . - /// The sum of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector64 Add(Vector64 left, Vector64 right) => left + right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 AddSaturate(Vector64 left, Vector64 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left + right; + } + else + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = Scalar.AddSaturate(left.GetElementUnsafe(index), right.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + } + /// Determines if all elements of a vector are equal to a given value. /// The type of the elements in the vector. /// The vector whose elements are being checked. @@ -362,7 +377,7 @@ public static Vector64 ClampNative(Vector64 value, Vector64 min, Vec /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToDouble(Vector64 vector) + public static Vector64 ConvertToDouble(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -381,7 +396,7 @@ public static unsafe Vector64 ConvertToDouble(Vector64 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToDouble(Vector64 vector) + public static Vector64 ConvertToDouble(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -399,7 +414,7 @@ public static unsafe Vector64 ConvertToDouble(Vector64 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt32(Vector64 vector) + public static Vector64 ConvertToInt32(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -417,7 +432,7 @@ public static unsafe Vector64 ConvertToInt32(Vector64 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt32Native(Vector64 vector) + public static Vector64 ConvertToInt32Native(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -435,7 +450,7 @@ public static unsafe Vector64 ConvertToInt32Native(Vector64 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt64(Vector64 vector) + public static Vector64 ConvertToInt64(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -453,7 +468,7 @@ public static unsafe Vector64 ConvertToInt64(Vector64 vector) /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToInt64Native(Vector64 vector) + public static Vector64 ConvertToInt64Native(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -471,7 +486,7 @@ public static unsafe Vector64 ConvertToInt64Native(Vector64 vector /// The converted vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToSingle(Vector64 vector) + public static Vector64 ConvertToSingle(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -490,7 +505,7 @@ public static unsafe Vector64 ConvertToSingle(Vector64 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToSingle(Vector64 vector) + public static Vector64 ConvertToSingle(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -509,7 +524,7 @@ public static unsafe Vector64 ConvertToSingle(Vector64 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt32(Vector64 vector) + public static Vector64 ConvertToUInt32(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -528,7 +543,7 @@ public static unsafe Vector64 ConvertToUInt32(Vector64 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt32Native(Vector64 vector) + public static Vector64 ConvertToUInt32Native(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -547,7 +562,7 @@ public static unsafe Vector64 ConvertToUInt32Native(Vector64 vector [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt64(Vector64 vector) + public static Vector64 ConvertToUInt64(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -566,7 +581,7 @@ public static unsafe Vector64 ConvertToUInt64(Vector64 vector) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 ConvertToUInt64Native(Vector64 vector) + public static Vector64 ConvertToUInt64Native(Vector64 vector) { Unsafe.SkipInit(out Vector64 result); @@ -640,7 +655,7 @@ public static void CopyTo(this Vector64 vector, T[] destination) /// The type of and () is not supported. /// is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(this Vector64 vector, T[] destination, int startIndex) + public static void CopyTo(this Vector64 vector, T[] destination, int startIndex) { // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons @@ -766,7 +781,7 @@ public static int CountWhereAllBitsSet(Vector64 vector) /// A new with all elements initialized to . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(T value) + public static Vector64 Create(T value) { Unsafe.SkipInit(out Vector64 result); @@ -783,46 +798,46 @@ public static unsafe Vector64 Create(T value) /// On x86, this method corresponds to __m64 _mm_set1_pi8 /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(byte value) => Create(value); + public static Vector64 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(double value) => Create(value); + public static Vector64 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// On x86, this method corresponds to __m64 _mm_set1_pi16 /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(short value) => Create(value); + public static Vector64 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// On x86, this method corresponds to __m64 _mm_set1_pi32 /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(int value) => Create(value); + public static Vector64 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(long value) => Create(value); + public static Vector64 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(nint value) => Create(value); + public static Vector64 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 Create(nuint value) => Create(value); + public static Vector64 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -830,13 +845,13 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 Create(sbyte value) => Create(value); + public static Vector64 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - public static unsafe Vector64 Create(float value) => Create(value); + public static Vector64 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -844,7 +859,7 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 Create(ushort value) => Create(value); + public static Vector64 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. @@ -852,14 +867,14 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 Create(uint value) => Create(value); + public static Vector64 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 Create(ulong value) => Create(value); + public static Vector64 Create(ulong value) => Create(value); /// Creates a new from a given array. /// The type of the elements in the vector. @@ -932,7 +947,7 @@ public static Vector64 Create(ReadOnlySpan values) /// A new with each element initialized to corresponding specified value. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(byte e0, byte e1, byte e2, byte e3, byte e4, byte e5, byte e6, byte e7) + public static Vector64 Create(byte e0, byte e1, byte e2, byte e3, byte e4, byte e5, byte e6, byte e7) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -955,7 +970,7 @@ public static unsafe Vector64 Create(byte e0, byte e1, byte e2, byte e3, b /// A new with each element initialized to corresponding specified value. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(short e0, short e1, short e2, short e3) + public static Vector64 Create(short e0, short e1, short e2, short e3) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -972,7 +987,7 @@ public static unsafe Vector64 Create(short e0, short e1, short e2, short /// A new with each element initialized to corresponding specified value. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(int e0, int e1) + public static Vector64 Create(int e0, int e1) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -994,7 +1009,7 @@ public static unsafe Vector64 Create(int e0, int e1) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(sbyte e0, sbyte e1, sbyte e2, sbyte e3, sbyte e4, sbyte e5, sbyte e6, sbyte e7) + public static Vector64 Create(sbyte e0, sbyte e1, sbyte e2, sbyte e3, sbyte e4, sbyte e5, sbyte e6, sbyte e7) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -1014,7 +1029,7 @@ public static unsafe Vector64 Create(sbyte e0, sbyte e1, sbyte e2, sbyte /// A new with each element initialized to corresponding specified value. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(float e0, float e1) + public static Vector64 Create(float e0, float e1) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -1032,7 +1047,7 @@ public static unsafe Vector64 Create(float e0, float e1) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(ushort e0, ushort e1, ushort e2, ushort e3) + public static Vector64 Create(ushort e0, ushort e1, ushort e2, ushort e3) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -1050,7 +1065,7 @@ public static unsafe Vector64 Create(ushort e0, ushort e1, ushort e2, us [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Create(uint e0, uint e1) + public static Vector64 Create(uint e0, uint e1) { Unsafe.SkipInit(out Vector64 result); result.SetElementUnsafe(0, e0); @@ -1065,7 +1080,7 @@ public static unsafe Vector64 Create(uint e0, uint e1) /// The type of () is not supported. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 CreateScalar(T value) + public static Vector64 CreateScalar(T value) { Vector64 result = Vector64.Zero; result.SetElementUnsafe(0, value); @@ -1076,78 +1091,78 @@ public static unsafe Vector64 CreateScalar(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(byte value) => CreateScalar(value); + public static Vector64 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(double value) => CreateScalar(value); + public static Vector64 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(short value) => CreateScalar(value); + public static Vector64 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(int value) => CreateScalar(value); + public static Vector64 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(long value) => CreateScalar(value); + public static Vector64 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(nint value) => CreateScalar(value); + public static Vector64 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalar(nuint value) => CreateScalar(value); + public static Vector64 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalar(sbyte value) => CreateScalar(value); + public static Vector64 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - public static unsafe Vector64 CreateScalar(float value) => CreateScalar(value); + public static Vector64 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalar(ushort value) => CreateScalar(value); + public static Vector64 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalar(uint value) => CreateScalar(value); + public static Vector64 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalar(ulong value) => CreateScalar(value); + public static Vector64 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The type of the elements in the vector. @@ -1172,78 +1187,78 @@ public static Vector64 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - public static unsafe Vector64 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - public static unsafe Vector64 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); + public static Vector64 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. /// The type of the elements in the vector. @@ -2685,184 +2700,143 @@ public static Vector64 MultiplyAddEstimate(Vector64 left, Vector64 return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) + internal static Vector64 Narrow(Vector64 lower, Vector64 upper) + where TSource : INumber + where TResult : INumber { - Unsafe.SkipInit(out Vector64 result); + Unsafe.SkipInit(out Vector64 result); - for (int i = 0; i < Vector64.Count; i++) + for (int i = 0; i < Vector64.Count; i++) { - float value = (float)lower.GetElementUnsafe(i); + TResult value = TResult.CreateTruncating(lower.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } - for (int i = Vector64.Count; i < Vector64.Count; i++) + for (int i = Vector64.Count; i < Vector64.Count; i++) { - float value = (float)upper.GetElementUnsafe(i - Vector64.Count); + TResult value = TResult.CreateTruncating(upper.GetElementUnsafe(i - Vector64.Count)); result.SetElementUnsafe(i, value); } return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] - [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) - { - Unsafe.SkipInit(out Vector64 result); + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - for (int i = 0; i < Vector64.Count; i++) - { - sbyte value = (sbyte)lower.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } - - for (int i = Vector64.Count; i < Vector64.Count; i++) - { - sbyte value = (sbyte)upper.GetElementUnsafe(i - Vector64.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - return result; - } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) - { - Unsafe.SkipInit(out Vector64 result); + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - for (int i = 0; i < Vector64.Count; i++) - { - short value = (short)lower.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - for (int i = Vector64.Count; i < Vector64.Count; i++) - { - short value = (short)upper.GetElementUnsafe(i - Vector64.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - return result; - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Narrow(Vector64 lower, Vector64 upper) + => Narrow(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) + internal static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + where TSource : INumber + where TResult : INumber { - Unsafe.SkipInit(out Vector64 result); + Unsafe.SkipInit(out Vector64 result); - for (int i = 0; i < Vector64.Count; i++) + for (int i = 0; i < Vector64.Count; i++) { - int value = (int)lower.GetElementUnsafe(i); + TResult value = TResult.CreateSaturating(lower.GetElementUnsafe(i)); result.SetElementUnsafe(i, value); } - for (int i = Vector64.Count; i < Vector64.Count; i++) + for (int i = Vector64.Count; i < Vector64.Count; i++) { - int value = (int)upper.GetElementUnsafe(i - Vector64.Count); + TResult value = TResult.CreateSaturating(upper.GetElementUnsafe(i - Vector64.Count)); result.SetElementUnsafe(i, value); } return result; } - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] - [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) - { - Unsafe.SkipInit(out Vector64 result); + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - for (int i = 0; i < Vector64.Count; i++) - { - byte value = (byte)lower.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } - - for (int i = Vector64.Count; i < Vector64.Count; i++) - { - byte value = (byte)upper.GetElementUnsafe(i - Vector64.Count); - result.SetElementUnsafe(i, value); - } - - return result; - } - - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) - { - Unsafe.SkipInit(out Vector64 result); + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - for (int i = 0; i < Vector64.Count; i++) - { - ushort value = (ushort)lower.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } - - for (int i = Vector64.Count; i < Vector64.Count; i++) - { - ushort value = (ushort)upper.GetElementUnsafe(i - Vector64.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - return result; - } + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - /// Narrows two instances into one . - /// The vector that will be narrowed to the lower half of the result vector. - /// The vector that will be narrowed to the upper half of the result vector. - /// A containing elements narrowed from and . + /// [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 Narrow(Vector64 lower, Vector64 upper) - { - Unsafe.SkipInit(out Vector64 result); - - for (int i = 0; i < Vector64.Count; i++) - { - uint value = (uint)lower.GetElementUnsafe(i); - result.SetElementUnsafe(i, value); - } + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - for (int i = Vector64.Count; i < Vector64.Count; i++) - { - uint value = (uint)upper.GetElementUnsafe(i - Vector64.Count); - result.SetElementUnsafe(i, value); - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); - return result; - } + /// + [Intrinsic] + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 NarrowWithSaturation(Vector64 lower, Vector64 upper) + => NarrowWithSaturation(lower, upper); /// Negates a vector. /// The type of the elements in the vector. @@ -3803,15 +3777,33 @@ public static void StoreUnsafe(this Vector64 source, ref T destination, nu Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); } - /// Subtracts two vectors to compute their difference. - /// The type of the elements in the vector. - /// The vector from which will be subtracted. - /// The vector to subtract from . - /// The difference of and . - /// The type of and () is not supported. + /// [Intrinsic] public static Vector64 Subtract(Vector64 left, Vector64 right) => left - right; + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 SubtractSaturate(Vector64 left, Vector64 right) + { + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return left - right; + } + else + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = Scalar.SubtractSaturate(left.GetElementUnsafe(index), right.GetElementUnsafe(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + } + /// Computes the sum of all elements in a vector. /// The type of the elements in the vector. /// The vector whose elements will be summed. @@ -3866,7 +3858,7 @@ public static Vector128 ToVector128(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector128 ToVector128Unsafe(this Vector64 vector) + public static Vector128 ToVector128Unsafe(this Vector64 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); @@ -3940,46 +3932,46 @@ public static bool TryCopyTo(this Vector64 vector, Span destination) /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens a into two . /// The vector whose elements are to be widened. /// A pair of vectors that contain the widened lower and upper halves of . [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); + public static (Vector64 Lower, Vector64 Upper) Widen(Vector64 source) => (WidenLower(source), WidenUpper(source)); /// Widens the lower half of a into a . /// The vector whose elements are to be widened. @@ -4005,7 +3997,7 @@ public static Vector64 WidenLower(Vector64 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4023,7 +4015,7 @@ public static unsafe Vector64 WidenLower(Vector64 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4042,7 +4034,7 @@ public static unsafe Vector64 WidenLower(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4060,7 +4052,7 @@ public static unsafe Vector64 WidenLower(Vector64 source) /// A vector that contain the widened lower half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4079,7 +4071,7 @@ public static unsafe Vector64 WidenLower(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4098,7 +4090,7 @@ public static unsafe Vector64 WidenLower(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenLower(Vector64 source) + public static Vector64 WidenLower(Vector64 source) { Unsafe.SkipInit(out Vector64 lower); @@ -4135,7 +4127,7 @@ public static Vector64 WidenUpper(Vector64 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); @@ -4153,7 +4145,7 @@ public static unsafe Vector64 WidenUpper(Vector64 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); @@ -4172,7 +4164,7 @@ public static unsafe Vector64 WidenUpper(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); @@ -4190,7 +4182,7 @@ public static unsafe Vector64 WidenUpper(Vector64 source) /// A vector that contain the widened upper half of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); @@ -4209,7 +4201,7 @@ public static unsafe Vector64 WidenUpper(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); @@ -4228,7 +4220,7 @@ public static unsafe Vector64 WidenUpper(Vector64 source) [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Vector64 WidenUpper(Vector64 source) + public static Vector64 WidenUpper(Vector64 source) { Unsafe.SkipInit(out Vector64 upper); diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 02984d153ab9fd..2d92e5a405177b 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -13,6 +13,7 @@ public static partial class Vector128 public static bool IsHardwareAccelerated { get { throw null; } } public static System.Runtime.Intrinsics.Vector128 Abs(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Add(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 AddSaturate(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static bool All(System.Runtime.Intrinsics.Vector128 vector, T value) { throw null; } public static bool AllWhereAllBitsSet(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 AndNot(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } @@ -261,6 +262,17 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector128 vector, public static System.Runtime.Intrinsics.Vector128 Narrow(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector128 Narrow(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector128 NarrowWithSaturation(System.Runtime.Intrinsics.Vector128 lower, System.Runtime.Intrinsics.Vector128 upper) { throw null; } public static System.Runtime.Intrinsics.Vector128 Negate(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static bool None(System.Runtime.Intrinsics.Vector128 vector, T value) { throw null; } public static bool NoneWhereAllBitsSet(System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -350,6 +362,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector128 vector, [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector128 source, ref T destination, nuint elementOffset) { throw null; } public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 SubtractSaturate(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static T Sum(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static T ToScalar(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 ToVector256Unsafe(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -433,6 +446,7 @@ public static partial class Vector256 public static bool IsHardwareAccelerated { get { throw null; } } public static System.Runtime.Intrinsics.Vector256 Abs(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Add(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } + public static System.Runtime.Intrinsics.Vector256 AddSaturate(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static bool All(System.Runtime.Intrinsics.Vector256 vector, T value) { throw null; } public static bool AllWhereAllBitsSet(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 AndNot(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } @@ -674,6 +688,17 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector256 vector, public static System.Runtime.Intrinsics.Vector256 Narrow(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector256 Narrow(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector256 NarrowWithSaturation(System.Runtime.Intrinsics.Vector256 lower, System.Runtime.Intrinsics.Vector256 upper) { throw null; } public static System.Runtime.Intrinsics.Vector256 Negate(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static bool None(System.Runtime.Intrinsics.Vector256 vector, T value) { throw null; } public static bool NoneWhereAllBitsSet(System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -763,6 +788,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector256 vector, [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector256 source, ref T destination, nuint elementOffset) { throw null; } public static System.Runtime.Intrinsics.Vector256 Subtract(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } + public static System.Runtime.Intrinsics.Vector256 SubtractSaturate(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static T Sum(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static T ToScalar(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 ToVector512Unsafe(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -846,6 +872,7 @@ public static partial class Vector512 public static bool IsHardwareAccelerated { get { throw null; } } public static System.Runtime.Intrinsics.Vector512 Abs(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 AddSaturate(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static bool All(System.Runtime.Intrinsics.Vector512 vector, T value) { throw null; } public static bool AllWhereAllBitsSet(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 AndNot(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } @@ -1088,6 +1115,17 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector512 vector, public static System.Runtime.Intrinsics.Vector512 Narrow(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector512 Narrow(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector512 NarrowWithSaturation(System.Runtime.Intrinsics.Vector512 lower, System.Runtime.Intrinsics.Vector512 upper) { throw null; } public static System.Runtime.Intrinsics.Vector512 Negate(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static bool None(System.Runtime.Intrinsics.Vector512 vector, T value) { throw null; } public static bool NoneWhereAllBitsSet(System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1177,6 +1215,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector512 vector, [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector512 source, ref T destination, nuint elementOffset) { throw null; } public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 SubtractSaturate(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static T Sum(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static T ToScalar(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Truncate(System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1258,6 +1297,7 @@ public static partial class Vector64 public static bool IsHardwareAccelerated { get { throw null; } } public static System.Runtime.Intrinsics.Vector64 Abs(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Add(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 AddSaturate(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static bool All(System.Runtime.Intrinsics.Vector64 vector, T value) { throw null; } public static bool AllWhereAllBitsSet(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 AndNot(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } @@ -1471,6 +1511,17 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector64 vector, public static System.Runtime.Intrinsics.Vector64 Narrow(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector64 Narrow(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } + [System.CLSCompliantAttribute(false)] + public static System.Runtime.Intrinsics.Vector64 NarrowWithSaturation(System.Runtime.Intrinsics.Vector64 lower, System.Runtime.Intrinsics.Vector64 upper) { throw null; } public static System.Runtime.Intrinsics.Vector64 Negate(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static bool None(System.Runtime.Intrinsics.Vector64 vector, T value) { throw null; } public static bool NoneWhereAllBitsSet(System.Runtime.Intrinsics.Vector64 vector) { throw null; } @@ -1552,6 +1603,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector64 vector, [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector64 source, ref T destination, nuint elementOffset) { throw null; } public static System.Runtime.Intrinsics.Vector64 Subtract(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 SubtractSaturate(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static T Sum(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static T ToScalar(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 ToVector128Unsafe(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 8e41e2b955616a..cc700e2f34aa56 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Reflection; @@ -6672,5 +6673,267 @@ private void CountIndexOfLastIndexOfWhereAllBitsSetTest(T allBitsSet, T value [Fact] public void CountIndexOfLastIndexOfWhereAllBitsSetUInt64Test() => CountIndexOfLastIndexOfWhereAllBitsSetTest(ulong.MaxValue, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMaxTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector128 left = Vector128.CreateSequence(start, T.One); + Vector128 right = Vector128.Create(T.MaxValue - T.CreateTruncating(Vector128.Count) + T.One); + + Vector128 result = Vector128.AddSaturate(left, right); + + for (int i = 0; i < Vector128.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector128.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMinTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector128 left = Vector128.CreateSequence(start, T.NegativeOne); + Vector128 right = Vector128.Create(T.MinValue + T.CreateTruncating(Vector128.Count) - T.One); + + Vector128 result = Vector128.AddSaturate(left, right); + + for (int i = 0; i < Vector128.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector128.Count - 1]); + } + + [Fact] + public void AddSaturateByteTest() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateInt16Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt32Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt64Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateIntPtrTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateSByteTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateUInt16Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt32Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt64Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUIntPtrTest() => AddSaturateToMaxTest(1); + + private (Vector128 lower, Vector128 upper) GetNarrowWithSaturationInputs() + where TFrom : unmanaged, IMinMaxValue, INumber + where TTo : unmanaged, IMinMaxValue, INumber + { + Vector128 lower = Vector128.Create(TFrom.CreateTruncating(TTo.MaxValue) - TFrom.CreateTruncating(Vector128.Count) + TFrom.One) + + Vector128.CreateSequence(TFrom.One, TFrom.One); + + Vector128 upper = Vector128.Create(TFrom.CreateTruncating(TTo.MinValue) + TFrom.CreateTruncating(Vector128.Count) - TFrom.One) + - Vector128.CreateSequence(TFrom.One, TFrom.One); + + return (lower, upper); + } + + private void NarrowWithSaturationTest(Vector128 lower, Vector128 upper, Vector128 result) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + for (int i = 0; i < Vector128.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(lower[i]); + Assert.Equal(expectedResult, result[i]); + } + + for (int i = 0; i < Vector128.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(upper[i]); + Assert.Equal(expectedResult, result[Vector128.Count + i]); + } + } + + [Fact] + public void NarrowWithSaturationInt16Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt32Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt64Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt16Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt32Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt64Test() + { + (Vector128 lower, Vector128 upper) = GetNarrowWithSaturationInputs(); + Vector128 result = Vector128.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMaxTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector128 left = Vector128.Create(T.MaxValue - T.CreateTruncating(Vector128.Count) + T.One); + Vector128 right = Vector128.CreateSequence(start, T.NegativeOne); + + Vector128 result = Vector128.SubtractSaturate(left, right); + + for (int i = 0; i < Vector128.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector128.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMinTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector128 left = Vector128.Create(T.MinValue + T.CreateTruncating(Vector128.Count) - T.One); + Vector128 right = Vector128.CreateSequence(start, T.One); + + Vector128 result = Vector128.SubtractSaturate(left, right); + + for (int i = 0; i < Vector128.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector128.Count - 1]); + } + + [Fact] + public void SubtractSaturateByteTest() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateInt16Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt32Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt64Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateIntPtrTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateSByteTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateUInt16Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt32Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt64Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUIntPtrTest() => SubtractSaturateToMinTest(1); } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index c29de431d1a081..644b6fff368c1a 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; @@ -7848,5 +7849,267 @@ private void CountIndexOfLastIndexOfWhereAllBitsSetTest(T allBitsSet, T value [Fact] public void CountIndexOfLastIndexOfWhereAllBitsSetUInt64Test() => CountIndexOfLastIndexOfWhereAllBitsSetTest(ulong.MaxValue, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMaxTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector256 left = Vector256.CreateSequence(start, T.One); + Vector256 right = Vector256.Create(T.MaxValue - T.CreateTruncating(Vector256.Count) + T.One); + + Vector256 result = Vector256.AddSaturate(left, right); + + for (int i = 0; i < Vector256.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector256.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMinTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector256 left = Vector256.CreateSequence(start, T.NegativeOne); + Vector256 right = Vector256.Create(T.MinValue + T.CreateTruncating(Vector256.Count) - T.One); + + Vector256 result = Vector256.AddSaturate(left, right); + + for (int i = 0; i < Vector256.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector256.Count - 1]); + } + + [Fact] + public void AddSaturateByteTest() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateInt16Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt32Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt64Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateIntPtrTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateSByteTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateUInt16Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt32Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt64Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUIntPtrTest() => AddSaturateToMaxTest(1); + + private (Vector256 lower, Vector256 upper) GetNarrowWithSaturationInputs() + where TFrom : unmanaged, IMinMaxValue, INumber + where TTo : unmanaged, IMinMaxValue, INumber + { + Vector256 lower = Vector256.Create(TFrom.CreateTruncating(TTo.MaxValue) - TFrom.CreateTruncating(Vector256.Count) + TFrom.One) + + Vector256.CreateSequence(TFrom.One, TFrom.One); + + Vector256 upper = Vector256.Create(TFrom.CreateTruncating(TTo.MinValue) + TFrom.CreateTruncating(Vector256.Count) - TFrom.One) + - Vector256.CreateSequence(TFrom.One, TFrom.One); + + return (lower, upper); + } + + private void NarrowWithSaturationTest(Vector256 lower, Vector256 upper, Vector256 result) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + for (int i = 0; i < Vector256.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(lower[i]); + Assert.Equal(expectedResult, result[i]); + } + + for (int i = 0; i < Vector256.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(upper[i]); + Assert.Equal(expectedResult, result[Vector256.Count + i]); + } + } + + [Fact] + public void NarrowWithSaturationInt16Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt32Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt64Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt16Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt32Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt64Test() + { + (Vector256 lower, Vector256 upper) = GetNarrowWithSaturationInputs(); + Vector256 result = Vector256.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMaxTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector256 left = Vector256.Create(T.MaxValue - T.CreateTruncating(Vector256.Count) + T.One); + Vector256 right = Vector256.CreateSequence(start, T.NegativeOne); + + Vector256 result = Vector256.SubtractSaturate(left, right); + + for (int i = 0; i < Vector256.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector256.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMinTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector256 left = Vector256.Create(T.MinValue + T.CreateTruncating(Vector256.Count) - T.One); + Vector256 right = Vector256.CreateSequence(start, T.One); + + Vector256 result = Vector256.SubtractSaturate(left, right); + + for (int i = 0; i < Vector256.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector256.Count - 1]); + } + + [Fact] + public void SubtractSaturateByteTest() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateInt16Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt32Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt64Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateIntPtrTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateSByteTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateUInt16Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt32Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt64Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUIntPtrTest() => SubtractSaturateToMinTest(1); } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index 2f1d298406f930..637a930e55c5e6 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; @@ -7631,5 +7632,267 @@ private void CountIndexOfLastIndexOfWhereAllBitsSetTest(T allBitsSet, T value [Fact] public void CountIndexOfLastIndexOfWhereAllBitsSetUInt64Test() => CountIndexOfLastIndexOfWhereAllBitsSetTest(ulong.MaxValue, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMaxTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector512 left = Vector512.CreateSequence(start, T.One); + Vector512 right = Vector512.Create(T.MaxValue - T.CreateTruncating(Vector512.Count) + T.One); + + Vector512 result = Vector512.AddSaturate(left, right); + + for (int i = 0; i < Vector512.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector512.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMinTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector512 left = Vector512.CreateSequence(start, T.NegativeOne); + Vector512 right = Vector512.Create(T.MinValue + T.CreateTruncating(Vector512.Count) - T.One); + + Vector512 result = Vector512.AddSaturate(left, right); + + for (int i = 0; i < Vector512.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector512.Count - 1]); + } + + [Fact] + public void AddSaturateByteTest() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateInt16Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt32Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt64Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateIntPtrTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateSByteTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateUInt16Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt32Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt64Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUIntPtrTest() => AddSaturateToMaxTest(1); + + private (Vector512 lower, Vector512 upper) GetNarrowWithSaturationInputs() + where TFrom : unmanaged, IMinMaxValue, INumber + where TTo : unmanaged, IMinMaxValue, INumber + { + Vector512 lower = Vector512.Create(TFrom.CreateTruncating(TTo.MaxValue) - TFrom.CreateTruncating(Vector512.Count) + TFrom.One) + + Vector512.CreateSequence(TFrom.One, TFrom.One); + + Vector512 upper = Vector512.Create(TFrom.CreateTruncating(TTo.MinValue) + TFrom.CreateTruncating(Vector512.Count) - TFrom.One) + - Vector512.CreateSequence(TFrom.One, TFrom.One); + + return (lower, upper); + } + + private void NarrowWithSaturationTest(Vector512 lower, Vector512 upper, Vector512 result) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + for (int i = 0; i < Vector512.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(lower[i]); + Assert.Equal(expectedResult, result[i]); + } + + for (int i = 0; i < Vector512.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(upper[i]); + Assert.Equal(expectedResult, result[Vector512.Count + i]); + } + } + + [Fact] + public void NarrowWithSaturationInt16Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt32Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt64Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt16Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt32Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt64Test() + { + (Vector512 lower, Vector512 upper) = GetNarrowWithSaturationInputs(); + Vector512 result = Vector512.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMaxTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector512 left = Vector512.Create(T.MaxValue - T.CreateTruncating(Vector512.Count) + T.One); + Vector512 right = Vector512.CreateSequence(start, T.NegativeOne); + + Vector512 result = Vector512.SubtractSaturate(left, right); + + for (int i = 0; i < Vector512.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector512.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMinTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector512 left = Vector512.Create(T.MinValue + T.CreateTruncating(Vector512.Count) - T.One); + Vector512 right = Vector512.CreateSequence(start, T.One); + + Vector512 result = Vector512.SubtractSaturate(left, right); + + for (int i = 0; i < Vector512.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector512.Count - 1]); + } + + [Fact] + public void SubtractSaturateByteTest() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateInt16Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt32Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt64Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateIntPtrTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateSByteTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateUInt16Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt32Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt64Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUIntPtrTest() => SubtractSaturateToMinTest(1); } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index 189b56d3ce536d..94965ae1a919a0 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; @@ -5936,5 +5937,267 @@ private void CountIndexOfLastIndexOfWhereAllBitsSetTest(T allBitsSet, T value [Fact] public void CountIndexOfLastIndexOfWhereAllBitsSetUInt64Test() => CountIndexOfLastIndexOfWhereAllBitsSetTest(ulong.MaxValue, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMaxTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector64 left = Vector64.CreateSequence(start, T.One); + Vector64 right = Vector64.Create(T.MaxValue - T.CreateTruncating(Vector64.Count) + T.One); + + Vector64 result = Vector64.AddSaturate(left, right); + + for (int i = 0; i < Vector64.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector64.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddSaturateToMinTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector64 left = Vector64.CreateSequence(start, T.NegativeOne); + Vector64 right = Vector64.Create(T.MinValue + T.CreateTruncating(Vector64.Count) - T.One); + + Vector64 result = Vector64.AddSaturate(left, right); + + for (int i = 0; i < Vector64.Count - 1; i++) + { + T expectedResult = left[i] + right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector64.Count - 1]); + } + + [Fact] + public void AddSaturateByteTest() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateInt16Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt32Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateInt64Test() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateIntPtrTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateSByteTest() + { + AddSaturateToMinTest(-1); + AddSaturateToMaxTest(+1); + } + + [Fact] + public void AddSaturateUInt16Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt32Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUInt64Test() => AddSaturateToMaxTest(1); + + [Fact] + public void AddSaturateUIntPtrTest() => AddSaturateToMaxTest(1); + + private (Vector64 lower, Vector64 upper) GetNarrowWithSaturationInputs() + where TFrom : unmanaged, IMinMaxValue, INumber + where TTo : unmanaged, IMinMaxValue, INumber + { + Vector64 lower = Vector64.Create(TFrom.CreateTruncating(TTo.MaxValue) - TFrom.CreateTruncating(Vector64.Count) + TFrom.One) + + Vector64.CreateSequence(TFrom.One, TFrom.One); + + Vector64 upper = Vector64.Create(TFrom.CreateTruncating(TTo.MinValue) + TFrom.CreateTruncating(Vector64.Count) - TFrom.One) + - Vector64.CreateSequence(TFrom.One, TFrom.One); + + return (lower, upper); + } + + private void NarrowWithSaturationTest(Vector64 lower, Vector64 upper, Vector64 result) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + for(int i = 0; i < Vector64.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(lower[i]); + Assert.Equal(expectedResult, result[i]); + } + + for (int i = 0; i < Vector64.Count; i++) + { + TTo expectedResult = TTo.CreateSaturating(upper[i]); + Assert.Equal(expectedResult, result[Vector64.Count + i]); + } + } + + [Fact] + public void NarrowWithSaturationInt16Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt32Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationInt64Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt16Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt32Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [Fact] + public void NarrowWithSaturationUInt64Test() + { + (Vector64 lower, Vector64 upper) = GetNarrowWithSaturationInputs(); + Vector64 result = Vector64.NarrowWithSaturation(lower, upper); + NarrowWithSaturationTest(lower, upper, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMaxTest(T start) + where T : struct, ISignedNumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.NegativeOne); + + Vector64 left = Vector64.Create(T.MaxValue - T.CreateTruncating(Vector64.Count) + T.One); + Vector64 right = Vector64.CreateSequence(start, T.NegativeOne); + + Vector64 result = Vector64.SubtractSaturate(left, right); + + for (int i = 0; i < Vector64.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MaxValue, result[Vector64.Count - 1]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SubtractSaturateToMinTest(T start) + where T : struct, INumber, IMinMaxValue + { + // We just take it as a parameter to prevent constant folding + Debug.Assert(start == T.One); + + Vector64 left = Vector64.Create(T.MinValue + T.CreateTruncating(Vector64.Count) - T.One); + Vector64 right = Vector64.CreateSequence(start, T.One); + + Vector64 result = Vector64.SubtractSaturate(left, right); + + for (int i = 0; i < Vector64.Count - 1; i++) + { + T expectedResult = left[i] - right[i]; + Assert.Equal(expectedResult, result[i]); + } + + Assert.Equal(T.MinValue, result[Vector64.Count - 1]); + } + + [Fact] + public void SubtractSaturateByteTest() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateInt16Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt32Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateInt64Test() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateIntPtrTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateSByteTest() + { + SubtractSaturateToMinTest(+1); + SubtractSaturateToMaxTest(-1); + } + + [Fact] + public void SubtractSaturateUInt16Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt32Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUInt64Test() => SubtractSaturateToMinTest(1); + + [Fact] + public void SubtractSaturateUIntPtrTest() => SubtractSaturateToMinTest(1); } }