diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index fac3e6c6216fe1..17637cad75039f 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -709,6 +709,30 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector64_AddSaturate: + case NI_Vector128_AddSaturate: + { + assert(sig->numArgs == 2); + + if (simdSize == 8 && varTypeIsLong(simdBaseType)) + { + break; + } + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + } + else + { + retNode = + gtNewSimdHWIntrinsicNode(retType, op1, op2, NI_AdvSimd_AddSaturate, simdBaseJitType, simdSize); + } + break; + } + case NI_AdvSimd_BitwiseClear: case NI_Vector64_AndNot: case NI_Vector128_AndNot: @@ -2097,6 +2121,30 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector64_SubtractSaturate: + case NI_Vector128_SubtractSaturate: + { + assert(sig->numArgs == 2); + + if (simdSize == 8 && varTypeIsLong(simdBaseType)) + { + break; + } + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + if (varTypeIsFloating(simdBaseType)) + { + retNode = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + } + else + { + retNode = + gtNewSimdHWIntrinsicNode(retType, op1, op2, NI_AdvSimd_SubtractSaturate, simdBaseJitType, simdSize); + } + break; + } + case NI_Vector64_op_LeftShift: case NI_Vector128_op_LeftShift: { diff --git a/src/coreclr/jit/hwintrinsiclistarm64.h b/src/coreclr/jit/hwintrinsiclistarm64.h index 3f8c9ebf40dfaa..7694bb3bab3481 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) @@ -86,6 +87,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) @@ -122,6 +124,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) @@ -199,6 +202,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 cf1562838b9807..4b5f90f7cb3409 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) @@ -104,6 +105,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_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, 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_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) @@ -141,6 +143,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) @@ -214,6 +217,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_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, 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_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) @@ -252,6 +256,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) @@ -325,6 +330,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_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, 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 8c5f27234053f4..70517187535d72 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -1384,6 +1384,44 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector128_AddSaturate: + case NI_Vector256_AddSaturate: + case NI_Vector512_AddSaturate: + { + assert(sig->numArgs == 2); + + if (varTypeIsFloating(simdBaseType)) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + retNode = gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); + } + else if (varTypeIsByte(simdBaseType) || varTypeIsShort(simdBaseType)) + { + intrinsic = NI_Illegal; + if (simdSize == 16 && compOpportunisticallyDependsOn(InstructionSet_SSE2)) + { + intrinsic = NI_SSE2_AddSaturate; + } + else if (simdSize == 32 && compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + intrinsic = NI_AVX2_AddSaturate; + } + else if (simdSize == 64 && compOpportunisticallyDependsOn(InstructionSet_AVX512BW)) + { + intrinsic = NI_AVX512BW_AddSaturate; + } + + if (intrinsic != NI_Illegal) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + } + break; + } + case NI_SSE_AndNot: case NI_SSE2_AndNot: case NI_AVX_AndNot: @@ -3615,6 +3653,44 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Vector128_SubtractSaturate: + case NI_Vector256_SubtractSaturate: + case NI_Vector512_SubtractSaturate: + { + assert(sig->numArgs == 2); + + if (varTypeIsFloating(simdBaseType)) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + retNode = gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); + } + else if (varTypeIsByte(simdBaseType) || varTypeIsShort(simdBaseType)) + { + intrinsic = NI_Illegal; + if (simdSize == 16 && compOpportunisticallyDependsOn(InstructionSet_SSE2)) + { + intrinsic = NI_SSE2_SubtractSaturate; + } + else if (simdSize == 32 && compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + intrinsic = NI_AVX2_SubtractSaturate; + } + else if (simdSize == 64 && compOpportunisticallyDependsOn(InstructionSet_AVX512BW)) + { + intrinsic = NI_AVX512BW_SubtractSaturate; + } + + if (intrinsic != NI_Illegal) + { + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + } + } + break; + } + case NI_Vector128_Sum: case NI_Vector256_Sum: case NI_Vector512_Sum: diff --git a/src/libraries/Common/tests/System/GenericMathTestMemberData.cs b/src/libraries/Common/tests/System/GenericMathTestMemberData.cs index 0e19ac826dfdda..97f44771ca8c38 100644 --- a/src/libraries/Common/tests/System/GenericMathTestMemberData.cs +++ b/src/libraries/Common/tests/System/GenericMathTestMemberData.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Numerics; using Xunit; namespace System.Tests @@ -1927,5 +1928,220 @@ public static IEnumerable TruncateSingle yield return new object[] { -3.14159f, -3.0f }; } } + + public static IEnumerable AddSaturateSByte => AddSaturateSignedInteger(); + public static IEnumerable AddSaturateInt16 => AddSaturateSignedInteger(); + public static IEnumerable AddSaturateInt32 => AddSaturateSignedInteger(); + public static IEnumerable AddSaturateInt64 => AddSaturateSignedInteger(); + + private static IEnumerable AddSaturateSignedInteger() where T : INumber, IMinMaxValue + { + yield return new object[] { T.Zero, T.Zero, T.Zero }; + yield return new object[] { T.Zero, T.CreateChecked(10), T.CreateChecked(10) }; + yield return new object[] { T.Zero, T.CreateChecked(-10), T.CreateChecked(-10) }; + yield return new object[] { T.Zero, T.MinValue, T.MinValue }; + yield return new object[] { T.Zero, T.MaxValue, T.MaxValue }; + + yield return new object[] { T.CreateChecked(10), T.Zero, T.CreateChecked(10) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(5), T.CreateChecked(15) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(-5), T.CreateChecked(5) }; + yield return new object[] { T.CreateChecked(10), T.MinValue, T.MinValue + T.CreateChecked(10) }; + yield return new object[] { T.CreateChecked(10), T.MaxValue, T.MaxValue }; + yield return new object[] { T.CreateChecked(10), T.MaxValue - T.CreateChecked(5), T.MaxValue }; + + yield return new object[] { T.CreateChecked(-10), T.Zero, T.CreateChecked(-10) }; + yield return new object[] { T.CreateChecked(-10), T.CreateChecked(5), T.CreateChecked(-5) }; + yield return new object[] { T.CreateChecked(-10), T.CreateChecked(-5), T.CreateChecked(-15) }; + yield return new object[] { T.CreateChecked(-10), T.MinValue, T.MinValue }; + yield return new object[] { T.CreateChecked(-10), T.MaxValue, T.MaxValue - T.CreateChecked(10) }; + + yield return new object[] { T.MinValue, T.Zero, T.MinValue }; + yield return new object[] { T.MinValue, T.CreateChecked(10), T.MinValue + T.CreateChecked(10) }; + yield return new object[] { T.MinValue, T.CreateChecked(-10), T.MinValue }; + yield return new object[] { T.MinValue, T.MinValue, T.MinValue }; + yield return new object[] { T.MinValue, T.MaxValue, -T.One }; + + yield return new object[] { T.MaxValue, T.Zero, T.MaxValue }; + yield return new object[] { T.MaxValue, T.CreateChecked(10), T.MaxValue }; + yield return new object[] { T.MaxValue, T.CreateChecked(-10), T.MaxValue - T.CreateChecked(10) }; + yield return new object[] { T.MaxValue, T.MinValue, -T.One }; + yield return new object[] { T.MaxValue, T.MaxValue, T.MaxValue }; + + yield return new object[] { T.MaxValue - T.CreateChecked(5), T.CreateChecked(10), T.MaxValue }; + } + + public static IEnumerable AddSaturateByte => AddSaturateUnsignedInteger(); + public static IEnumerable AddSaturateUInt16 => AddSaturateUnsignedInteger(); + public static IEnumerable AddSaturateUInt32 => AddSaturateUnsignedInteger(); + public static IEnumerable AddSaturateUInt64 => AddSaturateUnsignedInteger(); + + private static IEnumerable AddSaturateUnsignedInteger() where T : INumber, IMinMaxValue + { + yield return new object[] { T.Zero, T.Zero, T.Zero }; + yield return new object[] { T.Zero, T.CreateChecked(10), T.CreateChecked(10) }; + yield return new object[] { T.Zero, T.MaxValue, T.MaxValue }; + + yield return new object[] { T.CreateChecked(10), T.Zero, T.CreateChecked(10) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(5), T.CreateChecked(15) }; + yield return new object[] { T.CreateChecked(10), T.MaxValue - T.CreateChecked(5), T.MaxValue }; + yield return new object[] { T.CreateChecked(10), T.MaxValue, T.MaxValue }; + + yield return new object[] { T.MaxValue, T.Zero, T.MaxValue }; + yield return new object[] { T.MaxValue, T.CreateChecked(10), T.MaxValue }; + yield return new object[] { T.MaxValue, T.MaxValue, T.MaxValue }; + + yield return new object[] { T.MaxValue - T.CreateChecked(5), T.CreateChecked(10), T.MaxValue }; + } + + public static IEnumerable AddSaturateDouble + { + get + { + yield return new object[] { double.NegativeInfinity, 1.0, double.NegativeInfinity }; + yield return new object[] { double.MinValue, 1.0, double.MinValue }; + yield return new object[] { -1.0, 1.0, 0.0 }; + yield return new object[] { -MinNormalDouble, 1.0, 1.0 }; + yield return new object[] { -MaxSubnormalDouble, 1.0, 1.0 }; + yield return new object[] { -double.Epsilon, 1.0, 1.0 }; + yield return new object[] { -0.0, 1.0, 1.0 }; + yield return new object[] { double.NaN, 1.0, double.NaN }; + yield return new object[] { 0.0, 1.0, 1.0 }; + yield return new object[] { double.Epsilon, 1.0, 1.0 }; + yield return new object[] { MaxSubnormalDouble, 1.0, 1.0 }; + yield return new object[] { MinNormalDouble, 1.0, 1.0 }; + yield return new object[] { 1.0, 1.0, 2.0 }; + yield return new object[] { double.MaxValue, 1.0, double.MaxValue }; + yield return new object[] { double.PositiveInfinity, 1.0, double.PositiveInfinity }; + } + } + + public static IEnumerable AddSaturateSingle + { + get + { + yield return new object[] { float.NegativeInfinity, 1.0f, float.NegativeInfinity }; + yield return new object[] { float.MinValue, 1.0f, float.MinValue }; + yield return new object[] { -1.0f, 1.0f, 0.0f }; + yield return new object[] { -MinNormalDouble, 1.0f, 1.0f }; + yield return new object[] { -MaxSubnormalDouble, 1.0f, 1.0f }; + yield return new object[] { -float.Epsilon, 1.0f, 1.0f }; + yield return new object[] { -0.0f, 1.0f, 1.0f }; + yield return new object[] { float.NaN, 1.0f, float.NaN }; + yield return new object[] { 0.0f, 1.0f, 1.0f }; + yield return new object[] { float.Epsilon, 1.0f, 1.0f }; + yield return new object[] { MaxSubnormalDouble, 1.0f, 1.0f }; + yield return new object[] { MinNormalDouble, 1.0f, 1.0f }; + yield return new object[] { 1.0f, 1.0f, 2.0f }; + yield return new object[] { float.MaxValue, 1.0f, float.MaxValue }; + yield return new object[] { float.PositiveInfinity, 1.0f, float.PositiveInfinity }; + } + } + + public static IEnumerable SubtractSaturateByte => SubtractSaturateUnsignedInteger(); + public static IEnumerable SubtractSaturateUInt16 => SubtractSaturateUnsignedInteger(); + public static IEnumerable SubtractSaturateUInt32 => SubtractSaturateUnsignedInteger(); + public static IEnumerable SubtractSaturateUInt64 => SubtractSaturateUnsignedInteger(); + + private static IEnumerable SubtractSaturateUnsignedInteger() where T : INumber, IMinMaxValue + { + yield return new object[] { T.Zero, T.Zero, T.Zero }; + yield return new object[] { T.Zero, T.CreateChecked(10), T.Zero }; + yield return new object[] { T.Zero, T.MaxValue, T.Zero }; + + yield return new object[] { T.CreateChecked(10), T.Zero, T.CreateChecked(10) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(5), T.CreateChecked(5) }; + yield return new object[] { T.CreateChecked(10), T.MaxValue, T.Zero }; + + yield return new object[] { T.MaxValue, T.Zero, T.MaxValue }; + yield return new object[] { T.MaxValue, T.CreateChecked(10), T.MaxValue - T.CreateChecked(10) }; + yield return new object[] { T.MaxValue, T.MaxValue, T.Zero }; + + yield return new object[] { T.MaxValue - T.CreateChecked(5), T.CreateChecked(10), T.MaxValue - T.CreateChecked(15) }; + } + + public static IEnumerable SubtractSaturateSByte => SubtractSaturateSignedInteger(); + public static IEnumerable SubtractSaturateInt16 => SubtractSaturateSignedInteger(); + public static IEnumerable SubtractSaturateInt32 => SubtractSaturateSignedInteger(); + public static IEnumerable SubtractSaturateInt64 => SubtractSaturateSignedInteger(); + + private static IEnumerable SubtractSaturateSignedInteger() where T : INumber, IMinMaxValue + { + yield return new object[] { T.Zero, T.Zero, T.Zero }; + yield return new object[] { T.Zero, T.CreateChecked(10), T.CreateChecked(-10) }; + yield return new object[] { T.Zero, T.CreateChecked(-10), T.CreateChecked(10) }; + yield return new object[] { T.Zero, T.MinValue, T.MaxValue }; + yield return new object[] { T.Zero, T.MaxValue, -T.MaxValue }; + + yield return new object[] { T.CreateChecked(10), T.Zero, T.CreateChecked(10) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(5), T.CreateChecked(5) }; + yield return new object[] { T.CreateChecked(10), T.CreateChecked(-5), T.CreateChecked(15) }; + yield return new object[] { T.CreateChecked(10), T.MinValue, T.MaxValue }; + yield return new object[] { T.CreateChecked(10), T.MaxValue, -(T.MaxValue - T.CreateChecked(10)) }; + yield return new object[] { T.CreateChecked(10), T.MinValue + T.CreateChecked(5), T.MaxValue }; + + yield return new object[] { T.CreateChecked(-10), T.Zero, T.CreateChecked(-10) }; + yield return new object[] { T.CreateChecked(-10), T.CreateChecked(5), T.CreateChecked(-15) }; + yield return new object[] { T.CreateChecked(-10), T.CreateChecked(-5), T.CreateChecked(-5) }; + yield return new object[] { T.CreateChecked(-10), T.MinValue, -(T.MinValue + T.CreateChecked(10)) }; + yield return new object[] { T.CreateChecked(-10), T.MaxValue, T.MinValue }; + + yield return new object[] { T.MinValue, T.Zero, T.MinValue }; + yield return new object[] { T.MinValue, T.CreateChecked(10), T.MinValue }; + yield return new object[] { T.MinValue, T.CreateChecked(-10), T.MinValue + T.CreateChecked(10) }; + yield return new object[] { T.MinValue, T.MinValue, T.Zero }; + yield return new object[] { T.MinValue, T.MaxValue, T.MinValue }; + + yield return new object[] { T.MaxValue, T.Zero, T.MaxValue }; + yield return new object[] { T.MaxValue, T.CreateChecked(10), T.MaxValue - T.CreateChecked(10) }; + yield return new object[] { T.MaxValue, T.CreateChecked(-10), T.MaxValue }; + yield return new object[] { T.MaxValue, T.MinValue, T.MaxValue }; + yield return new object[] { T.MaxValue, T.MaxValue, T.Zero }; + + yield return new object[] { T.MinValue + T.CreateChecked(5), T.CreateChecked(-10), T.MinValue + T.CreateChecked(15) }; + } + + public static IEnumerable SubtractSaturateDouble + { + get + { + yield return new object[] { double.NegativeInfinity, 1.0, double.NegativeInfinity }; + yield return new object[] { double.MinValue, 1.0, double.MinValue }; + yield return new object[] { -1.0, 1.0, -2.0 }; + yield return new object[] { -MinNormalDouble, 1.0, -1.0 }; + yield return new object[] { -MaxSubnormalDouble, 1.0, -1.0 }; + yield return new object[] { -double.Epsilon, 1.0, -1.0 }; + yield return new object[] { -0.0, 1.0, -1.0 }; + yield return new object[] { double.NaN, 1.0, double.NaN }; + yield return new object[] { 0.0, 1.0, -1.0 }; + yield return new object[] { double.Epsilon, 1.0, -1.0 }; + yield return new object[] { MaxSubnormalDouble, 1.0, -1.0 }; + yield return new object[] { MinNormalDouble, 1.0, -1.0 }; + yield return new object[] { 1.0, 1.0, 0.0 }; + yield return new object[] { double.MaxValue, 1.0, double.MaxValue }; + yield return new object[] { double.PositiveInfinity, 1.0, double.PositiveInfinity }; + } + } + + public static IEnumerable SubtractSaturateSingle + { + get + { + yield return new object[] { float.NegativeInfinity, 1.0f, float.NegativeInfinity }; + yield return new object[] { float.MinValue, 1.0f, float.MinValue }; + yield return new object[] { -1.0f, 1.0f, -2.0f }; + yield return new object[] { -MinNormalDouble, 1.0f, -1.0f }; + yield return new object[] { -MaxSubnormalDouble, 1.0f, -1.0f }; + yield return new object[] { -float.Epsilon, 1.0f, -1.0f }; + yield return new object[] { -0.0f, 1.0f, -1.0f }; + yield return new object[] { float.NaN, 1.0, float.NaN }; + yield return new object[] { 0.0f, 1.0f, -1.0f }; + yield return new object[] { float.Epsilon, 1.0f, -1.0f }; + yield return new object[] { MaxSubnormalDouble, 1.0f, -1.0f }; + yield return new object[] { MinNormalDouble, 1.0f, -1.0f }; + yield return new object[] { 1.0f, 1.0f, 0.0f }; + yield return new object[] { float.MaxValue, 1.0f, float.MaxValue }; + yield return new object[] { float.PositiveInfinity, 1.0f, float.PositiveInfinity }; + } + } } } 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 443d0c64b6f1a0..cca3d7d1f42476 100644 --- a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs +++ b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs @@ -204,6 +204,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 AddSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Add(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector AndNot(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector As(this System.Numerics.Vector vector) { throw null; } @@ -420,6 +421,7 @@ public static partial class Vector public static void StoreUnsafe(this System.Numerics.Vector source, ref T destination) { throw null; } [System.CLSCompliantAttribute(false)] public static void StoreUnsafe(this System.Numerics.Vector source, ref T destination, nuint elementOffset) { throw null; } + public static System.Numerics.Vector SubtractSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Subtract(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; } diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 28ed4f701d4693..83502d47ba3f9e 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -1141,6 +1141,56 @@ private void TestAdditionOverflow() where T : struct, INumber }); } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateByte(byte left, byte right, byte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt16(short left, short right, short expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt16(ushort left, ushort right, ushort expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt32(int left, int right, int expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt32(uint left, uint right, uint expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt64(long left, long right, long expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt64(ulong left, ulong right, ulong expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateDouble(double left, double right, double expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSingle(float left, float right, float expected) => TestAddSaturate(left, right, expected); + + private void TestAddSaturate(T left, T right, T expected) where T : struct, INumber + { + var actual = Vector.AddSaturate(Vector.Create(left), Vector.Create(right)); + ValidateVector(actual, + (index, val) => + { + Assert.Equal(expected, val); + }); + } + [Fact] public void SubtractionByte() { TestSubtraction(); } [Fact] @@ -1206,6 +1256,56 @@ private void TestSubtractionOverflow() where T : struct, INumber }); } + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateByte(byte left, byte right, byte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt16(short left, short right, short expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt16(ushort left, ushort right, ushort expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt32(int left, int right, int expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt32(uint left, uint right, uint expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt64(long left, long right, long expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt64(ulong left, ulong right, ulong expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateDouble(double left, double right, double expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSingle(float left, float right, float expected) => TestSubtractSaturate(left, right, expected); + + private void TestSubtractSaturate(T left, T right, T expected) where T : struct, INumber + { + var actual = Vector.SubtractSaturate(Vector.Create(left), Vector.Create(right)); + ValidateVector(actual, + (index, val) => + { + Assert.Equal(expected, val); + }); + } + [Fact] public void MultiplicationByte() { TestMultiplication(); } [Fact] 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 821aae27da87f4..f81efbc5720917 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace System.Numerics @@ -62,6 +63,18 @@ public static Vector Abs(Vector value) [Intrinsic] public static Vector Add(Vector left, Vector right) => left + right; + /// Performs saturating addition on two vectors. + /// The vector to add with . + /// The vector to add with . + /// The type of the elements in the vector. + /// The saturated sum of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector AddSaturate(Vector left, Vector right) + { + return VectorMath.AddSaturate, T>(left, right); + } + /// Computes the bitwise-and of a given vector and the ones complement of another vector. /// The vector to bitwise-and with . /// The vector to that is ones-complemented before being bitwise-and with . @@ -2647,6 +2660,18 @@ public static void StoreUnsafe(this Vector source, ref T destination, nuin [Intrinsic] public static Vector Subtract(Vector left, Vector right) => left - right; + /// Performs saturating subtraction on two vectors. + /// The vector from which will be subtracted. + /// The vector to subtract from . + /// The type of the elements in the vector. + /// The saturated difference of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector SubtractSaturate(Vector left, Vector right) + { + return VectorMath.SubtractSaturate, T>(left, right); + } + /// /// Returns the sum of all elements inside the vector. /// 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 8fb3436cf3cf8e..0c68c294880c99 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 @@ -84,6 +84,26 @@ public static Vector128 Abs(Vector128 vector) [Intrinsic] public static Vector128 Add(Vector128 left, Vector128 right) => left + right; + /// Performs saturating addition on two vectors. + /// The vector to add with . + /// The vector to add with . + /// The type of the elements in the vector. + /// The saturated sum of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 AddSaturate(Vector128 left, Vector128 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.AddSaturate, T>(left, right); + } + + return Create( + Vector64.AddSaturate(left._lower, right._lower), + Vector64.AddSaturate(left._upper, right._upper) + ); + } + /// Computes the bitwise-and of a given vector and the ones complement of another vector. /// The type of the elements in the vector. /// The vector to bitwise-and with . @@ -3396,6 +3416,26 @@ public static void StoreUnsafe(this Vector128 source, ref T destination, n [Intrinsic] public static Vector128 Subtract(Vector128 left, Vector128 right) => left - right; + /// Performs saturating subtraction on two vectors. + /// The vector from which will be subtracted. + /// The vector to subtract from . + /// The type of the elements in the vector. + /// The saturated difference of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 SubtractSaturate(Vector128 left, Vector128 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.SubtractSaturate, T>(left, right); + } + + 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. @@ -3895,55 +3935,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))] - internal static Vector128 AddSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.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))] - internal static Vector128 SubtractSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.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))] - internal static Vector128 AddSaturate(Vector128 left, Vector128 right) - { - if (Sse2.IsSupported) - { - return Sse2.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 6708b81a203610..c8c707ae8fd7cc 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 @@ -88,6 +88,26 @@ public static Vector256 Abs(Vector256 vector) [Intrinsic] public static Vector256 Add(Vector256 left, Vector256 right) => left + right; + /// Performs saturating addition on two vectors. + /// The vector to add with . + /// The vector to add with . + /// The type of the elements in the vector. + /// The saturated sum of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSaturate(Vector256 left, Vector256 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.AddSaturate, T>(left, right); + } + + return Create( + Vector128.AddSaturate(left._lower, right._lower), + Vector128.AddSaturate(left._upper, right._upper) + ); + } + /// Computes the bitwise-and of a given vector and the ones complement of another vector. /// The type of the elements in the vector. /// The vector to bitwise-and with . @@ -3263,6 +3283,26 @@ public static void StoreUnsafe(this Vector256 source, ref T destination, n [Intrinsic] public static Vector256 Subtract(Vector256 left, Vector256 right) => left - right; + /// Performs saturating subtraction on two vectors. + /// The vector from which will be subtracted. + /// The vector to subtract from . + /// The type of the elements in the vector. + /// The saturated difference of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSaturate(Vector256 left, Vector256 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.SubtractSaturate, T>(left, right); + } + + 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. 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 2b683f06524f45..8324a6c42bc21b 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 @@ -88,6 +88,26 @@ public static Vector512 Abs(Vector512 vector) [Intrinsic] public static Vector512 Add(Vector512 left, Vector512 right) => left + right; + /// Performs saturating addition on two vectors. + /// The vector to add with . + /// The vector to add with . + /// The type of the elements in the vector. + /// The saturated sum of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSaturate(Vector512 left, Vector512 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.AddSaturate, T>(left, right); + } + + return Create( + Vector256.AddSaturate(left._lower, right._upper), + Vector256.AddSaturate(left._upper, right._upper) + ); + } + /// Computes the bitwise-and of a given vector and the ones complement of another vector. /// The type of the elements in the vector. /// The vector to bitwise-and with . @@ -3290,6 +3310,26 @@ public static void StoreUnsafe(this Vector512 source, ref T destination, n [Intrinsic] public static Vector512 Subtract(Vector512 left, Vector512 right) => left - right; + /// Performs saturating subtraction on two vectors. + /// The vector from which will be subtracted. + /// The vector to subtract from . + /// The type of the elements in the vector. + /// The saturated difference of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSaturate(Vector512 left, Vector512 right) + { + if (IsHardwareAccelerated) + { + return VectorMath.SubtractSaturate, T>(left, right); + } + + return Create( + Vector256.SubtractSaturate(left._lower, right._upper), + 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 570933c7abbba2..cf21a167a8629e 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 @@ -67,6 +67,18 @@ public static Vector64 Abs(Vector64 vector) [Intrinsic] public static Vector64 Add(Vector64 left, Vector64 right) => left + right; + /// Performs saturating addition on two vectors. + /// The vector to add with . + /// The vector to add with . + /// The type of the elements in the vector. + /// The saturated sum of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 AddSaturate(Vector64 left, Vector64 right) + { + return VectorMath.AddSaturate, T>(left, right); + } + /// Computes the bitwise-and of a given vector and the ones complement of another vector. /// The type of the elements in the vector. /// The vector to bitwise-and with . @@ -3275,6 +3287,18 @@ public static void StoreUnsafe(this Vector64 source, ref T destination, nu [Intrinsic] public static Vector64 Subtract(Vector64 left, Vector64 right) => left - right; + /// Performs saturating subtraction on two vectors. + /// The vector from which will be subtracted. + /// The vector to subtract from . + /// The type of the elements in the vector. + /// The saturated difference of and . + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 SubtractSaturate(Vector64 left, Vector64 right) + { + return VectorMath.SubtractSaturate, T>(left, right); + } + /// Computes the sum of all elements in a vector. /// The type of the elements in the vector. /// The vector whose elements will be summed. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs index fbd5af0fa1b783..30382388b068c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs @@ -1588,6 +1588,74 @@ public static TVector MinNumber(TVector x, TVector y) return TVector.Min(x, y); } + public static TVector AddSaturate(TVector left, TVector right) + where TVector : unmanaged, ISimdVector + { + TVector sum = left + right; + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return sum; + } + + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + // Equivalent to sum < left ? T.MaxValue : sum + return TVector.LessThan(sum, left) | sum; + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + // Condition is equivalent to: (sign(left) != sign(sum)) && (sign(left) == sign(right)) + // Overflow result is equivalent to: left < 0 ? T.MinValue : T.MaxValue + return TVector.ConditionalSelect( + TVector.IsNegative(TVector.AndNot(left ^ sum, left ^ right)), + (TVector.AllBitsSet >>> 1) ^ TVector.IsNegative(left), + sum); + } + + public static TVector SubtractSaturate(TVector left, TVector right) + where TVector : unmanaged, ISimdVector + { + TVector diff = left - right; + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return diff; + } + + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + // Equivalent to diff <= left ? diff : 0 + return TVector.LessThanOrEqual(diff, left) & diff; + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + // Condition is equivalent to: (sign(left) != sign(diff)) && (sign(left) != sign(right)) + // Overflow result is equivalent to: left < 0 ? T.MinValue : T.MaxValue + return TVector.ConditionalSelect( + TVector.IsNegative((left ^ diff) & (left ^ right)), + (TVector.AllBitsSet >>> 1) ^ TVector.IsNegative(left), + diff); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TVector RadiansToDegrees(TVector radians) where TVector : unmanaged, ISimdVector 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 352820d85007c3..b165802024ccc3 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -12,6 +12,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 AddSaturate(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { 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 AndNot(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 AsByte(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -315,6 +316,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector128 vector, public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector128 source, ref T destination) { throw null; } [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 SubtractSaturate(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 Subtract(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; } @@ -398,6 +400,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 AddSaturate(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { 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 AndNot(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static System.Runtime.Intrinsics.Vector256 AsByte(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -694,6 +697,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector256 vector, public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector256 source, ref T destination) { throw null; } [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 SubtractSaturate(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static System.Runtime.Intrinsics.Vector256 Subtract(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; } @@ -777,6 +781,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 AddSaturate(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { 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 AndNot(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 AsByte(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1074,6 +1079,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector512 vector, public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector512 source, ref T destination) { throw null; } [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 SubtractSaturate(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Subtract(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; } @@ -1155,6 +1161,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 AddSaturate(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { 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 AndNot(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static System.Runtime.Intrinsics.Vector64 AsByte(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } @@ -1419,6 +1426,7 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector64 vector, public static void StoreUnsafe(this System.Runtime.Intrinsics.Vector64 source, ref T destination) { throw null; } [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 SubtractSaturate(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static System.Runtime.Intrinsics.Vector64 Subtract(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; } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 09153068d91439..a1fefbc79c6093 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -5393,5 +5393,97 @@ public void TruncateSingleTest(float value, float expectedResult) Vector128 actualResult = Vector128.Truncate(Vector128.Create(value)); AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Zero); } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateByte(byte left, byte right, byte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt16(short left, short right, short expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt16(ushort left, ushort right, ushort expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt32(int left, int right, int expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt32(uint left, uint right, uint expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt64(long left, long right, long expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt64(ulong left, ulong right, ulong expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateDouble(double left, double right, double expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSingle(float left, float right, float expected) => TestAddSaturate(left, right, expected); + + private void TestAddSaturate(T left, T right, T expected) + { + Vector128 actualResult = Vector128.AddSaturate(Vector128.Create(left), Vector128.Create(right)); + Assert.Equal(Vector128.Create(expected), actualResult); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateByte(byte left, byte right, byte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt16(short left, short right, short expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt16(ushort left, ushort right, ushort expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt32(int left, int right, int expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt32(uint left, uint right, uint expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt64(long left, long right, long expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt64(ulong left, ulong right, ulong expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateDouble(double left, double right, double expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSingle(float left, float right, float expected) => TestSubtractSaturate(left, right, expected); + + private void TestSubtractSaturate(T left, T right, T expected) + { + Vector128 actualResult = Vector128.SubtractSaturate(Vector128.Create(left), Vector128.Create(right)); + Assert.Equal(Vector128.Create(expected), actualResult); + } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index d16e5eed481809..e23db25c4aa094 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -6410,5 +6410,97 @@ public void TruncateSingleTest(float value, float expectedResult) Vector256 actualResult = Vector256.Truncate(Vector256.Create(value)); AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Zero); } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateByte(byte left, byte right, byte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt16(short left, short right, short expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt16(ushort left, ushort right, ushort expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt32(int left, int right, int expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt32(uint left, uint right, uint expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt64(long left, long right, long expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt64(ulong left, ulong right, ulong expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateDouble(double left, double right, double expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSingle(float left, float right, float expected) => TestAddSaturate(left, right, expected); + + private void TestAddSaturate(T left, T right, T expected) + { + Vector256 actualResult = Vector256.AddSaturate(Vector256.Create(left), Vector256.Create(right)); + Assert.Equal(Vector256.Create(expected), actualResult); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateByte(byte left, byte right, byte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt16(short left, short right, short expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt16(ushort left, ushort right, ushort expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt32(int left, int right, int expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt32(uint left, uint right, uint expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt64(long left, long right, long expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt64(ulong left, ulong right, ulong expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateDouble(double left, double right, double expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSingle(float left, float right, float expected) => TestSubtractSaturate(left, right, expected); + + private void TestSubtractSaturate(T left, T right, T expected) + { + Vector256 actualResult = Vector256.SubtractSaturate(Vector256.Create(left), Vector256.Create(right)); + Assert.Equal(Vector256.Create(expected), actualResult); + } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index f996d82369615f..bc1a628df15ca5 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5843,5 +5843,97 @@ public void TruncateSingleTest(float value, float expectedResult) Vector512 actualResult = Vector512.Truncate(Vector512.Create(value)); AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Zero); } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateByte(byte left, byte right, byte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt16(short left, short right, short expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt16(ushort left, ushort right, ushort expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt32(int left, int right, int expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt32(uint left, uint right, uint expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt64(long left, long right, long expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt64(ulong left, ulong right, ulong expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateDouble(double left, double right, double expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSingle(float left, float right, float expected) => TestAddSaturate(left, right, expected); + + private void TestAddSaturate(T left, T right, T expected) + { + Vector512 actualResult = Vector512.AddSaturate(Vector512.Create(left), Vector512.Create(right)); + Assert.Equal(Vector512.Create(expected), actualResult); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateByte(byte left, byte right, byte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt16(short left, short right, short expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt16(ushort left, ushort right, ushort expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt32(int left, int right, int expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt32(uint left, uint right, uint expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt64(long left, long right, long expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt64(ulong left, ulong right, ulong expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateDouble(double left, double right, double expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSingle(float left, float right, float expected) => TestSubtractSaturate(left, right, expected); + + private void TestSubtractSaturate(T left, T right, T expected) + { + Vector512 actualResult = Vector512.SubtractSaturate(Vector512.Create(left), Vector512.Create(right)); + Assert.Equal(Vector512.Create(expected), actualResult); + } } } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index c049c08515a083..cebc07750fc222 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -4801,5 +4801,97 @@ public void TruncateSingleTest(float value, float expectedResult) Vector64 actualResult = Vector64.Truncate(Vector64.Create(value)); AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Zero); } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateByte(byte left, byte right, byte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt16(short left, short right, short expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt16(ushort left, ushort right, ushort expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt32(int left, int right, int expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt32(uint left, uint right, uint expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateInt64(long left, long right, long expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateUInt64(ulong left, ulong right, ulong expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateDouble(double left, double right, double expected) => TestAddSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.AddSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void AddSaturateSingle(float left, float right, float expected) => TestAddSaturate(left, right, expected); + + private void TestAddSaturate(T left, T right, T expected) + { + Vector64 actualResult = Vector64.AddSaturate(Vector64.Create(left), Vector64.Create(right)); + Assert.Equal(Vector64.Create(expected), actualResult); + } + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateByte(byte left, byte right, byte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSByte), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSByte(sbyte left, sbyte right, sbyte expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt16(short left, short right, short expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt16), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt16(ushort left, ushort right, ushort expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt32(int left, int right, int expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt32), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt32(uint left, uint right, uint expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateInt64(long left, long right, long expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateUInt64), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateUInt64(ulong left, ulong right, ulong expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateDouble), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateDouble(double left, double right, double expected) => TestSubtractSaturate(left, right, expected); + + [Theory] + [MemberData(nameof(GenericMathTestMemberData.SubtractSaturateSingle), MemberType = typeof(GenericMathTestMemberData))] + public void SubtractSaturateSingle(float left, float right, float expected) => TestSubtractSaturate(left, right, expected); + + private void TestSubtractSaturate(T left, T right, T expected) + { + Vector64 actualResult = Vector64.SubtractSaturate(Vector64.Create(left), Vector64.Create(right)); + Assert.Equal(Vector64.Create(expected), actualResult); + } } }