From a3d0161ca24970792d04f12974af3eae55585faf Mon Sep 17 00:00:00 2001 From: Alan Hayward Date: Wed, 17 Apr 2024 13:51:52 +0100 Subject: [PATCH] JIT ARM64-SVE: Add AddAcross --- src/coreclr/jit/codegenarm64test.cpp | 16 +- src/coreclr/jit/emitarm64sve.cpp | 29 +- src/coreclr/jit/hwintrinsic.h | 145 ++++----- src/coreclr/jit/hwintrinsiclistarm64sve.h | 1 + .../Arm/Sve.PlatformNotSupported.cs | 62 ++++ .../src/System/Runtime/Intrinsics/Arm/Sve.cs | 63 ++++ .../ref/System.Runtime.Intrinsics.cs | 10 + .../GenerateHWIntrinsicTests_Arm.cs | 58 ++-- .../HardwareIntrinsics/Arm/Shared/Helpers.cs | 153 +++++++++ .../_SveMinimalUnaryOpTestTemplate.template | 302 ++++++++++++++++++ .../Shared/_SveUnaryOpTestTemplate.template | 2 +- 11 files changed, 731 insertions(+), 110 deletions(-) create mode 100644 src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMinimalUnaryOpTestTemplate.template diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 52633ed6733e6..2057a72323bf7 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5314,11 +5314,11 @@ void CodeGen::genArm64EmitterUnitTestsSve() #endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED // IF_SVE_AI_3A - theEmitter->emitIns_R_R_R(INS_sve_saddv, EA_1BYTE, REG_V1, REG_P4, REG_V2, + theEmitter->emitIns_R_R_R(INS_sve_saddv, EA_SCALABLE, REG_V1, REG_P4, REG_V2, INS_OPTS_SCALABLE_B); // SADDV
, , . - theEmitter->emitIns_R_R_R(INS_sve_saddv, EA_2BYTE, REG_V2, REG_P5, REG_V3, + theEmitter->emitIns_R_R_R(INS_sve_saddv, EA_SCALABLE, REG_V2, REG_P5, REG_V3, INS_OPTS_SCALABLE_H); // SADDV
, , . - theEmitter->emitIns_R_R_R(INS_sve_uaddv, EA_4BYTE, REG_V3, REG_P6, REG_V4, + theEmitter->emitIns_R_R_R(INS_sve_uaddv, EA_SCALABLE, REG_V3, REG_P6, REG_V4, INS_OPTS_SCALABLE_S); // UADDV
, , . // IF_SVE_AJ_3A @@ -6768,15 +6768,15 @@ void CodeGen::genArm64EmitterUnitTestsSve() #endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED // IF_SVE_HE_3A - theEmitter->emitIns_R_R_R(INS_sve_faddv, EA_2BYTE, REG_V21, REG_P7, REG_V7, + theEmitter->emitIns_R_R_R(INS_sve_faddv, EA_SCALABLE, REG_V21, REG_P7, REG_V7, INS_OPTS_SCALABLE_H); // FADDV , , . - theEmitter->emitIns_R_R_R(INS_sve_fmaxnmv, EA_2BYTE, REG_V22, REG_P6, REG_V6, + theEmitter->emitIns_R_R_R(INS_sve_fmaxnmv, EA_SCALABLE, REG_V22, REG_P6, REG_V6, INS_OPTS_SCALABLE_H); // FMAXNMV , , . - theEmitter->emitIns_R_R_R(INS_sve_fmaxv, EA_4BYTE, REG_V23, REG_P5, REG_V5, + theEmitter->emitIns_R_R_R(INS_sve_fmaxv, EA_SCALABLE, REG_V23, REG_P5, REG_V5, INS_OPTS_SCALABLE_S); // FMAXV , , . - theEmitter->emitIns_R_R_R(INS_sve_fminnmv, EA_8BYTE, REG_V24, REG_P4, REG_V4, + theEmitter->emitIns_R_R_R(INS_sve_fminnmv, EA_SCALABLE, REG_V24, REG_P4, REG_V4, INS_OPTS_SCALABLE_D); // FMINNMV , , . - theEmitter->emitIns_R_R_R(INS_sve_fminv, EA_4BYTE, REG_V25, REG_P3, REG_V3, + theEmitter->emitIns_R_R_R(INS_sve_fminv, EA_SCALABLE, REG_V25, REG_P3, REG_V3, INS_OPTS_SCALABLE_S); // FMINV , , . // IF_SVE_HQ_3A diff --git a/src/coreclr/jit/emitarm64sve.cpp b/src/coreclr/jit/emitarm64sve.cpp index 3eadf273290f8..19a1e89482a0e 100644 --- a/src/coreclr/jit/emitarm64sve.cpp +++ b/src/coreclr/jit/emitarm64sve.cpp @@ -3060,7 +3060,6 @@ void emitter::emitInsSve_R_R_R(instruction ins, break; case INS_sve_saddv: - case INS_sve_uaddv: assert(isFloatReg(reg1)); assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); @@ -3069,6 +3068,15 @@ void emitter::emitInsSve_R_R_R(instruction ins, fmt = IF_SVE_AI_3A; break; + case INS_sve_uaddv: + assert(isFloatReg(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(insOptsScalableStandard(opt)); + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_AI_3A; + break; + case INS_sve_addqv: unreached(); // TODO-SVE: Not yet supported. assert(isVectorRegister(reg1)); @@ -4059,7 +4067,7 @@ void emitter::emitInsSve_R_R_R(instruction ins, assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); assert(insOptsScalableFloat(opt)); - assert(isValidVectorElemsizeSveFloat(size)); + assert(isScalableVectorSize(size)); assert(insScalableOptsNone(sopt)); fmt = IF_SVE_HE_3A; break; @@ -4069,7 +4077,7 @@ void emitter::emitInsSve_R_R_R(instruction ins, assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); assert(insOptsScalableFloat(opt)); - assert(isValidVectorElemsizeSveFloat(size)); + assert(isScalableVectorSize(size)); assert(insScalableOptsNone(sopt)); fmt = IF_SVE_HJ_3A; break; @@ -12618,7 +12626,7 @@ void emitter::emitInsSveSanityCheck(instrDesc* id) assert(isVectorRegister(id->idReg1())); // ddddd assert(isLowPredicateRegister(id->idReg2())); // ggg assert(isVectorRegister(id->idReg3())); // mmmmm - assert(isValidVectorElemsizeSveFloat(id->idOpSize())); + assert(isScalableVectorSize(id->idOpSize())); break; // Scalable to general register. @@ -13211,11 +13219,20 @@ void emitter::emitInsSveSanityCheck(instrDesc* id) // Scalable, widening to scalar SIMD. case IF_SVE_AI_3A: // ........xx...... ...gggnnnnnddddd -- SVE integer add reduction (predicated) - assert(insOptsScalableWide(id->idInsOpt())); // xx + switch (id->idIns()) + { + case INS_sve_saddv: + assert(insOptsScalableWide(id->idInsOpt())); // xx + break; + + default: + assert(insOptsScalableStandard(id->idInsOpt())); // xx + break; + } assert(isVectorRegister(id->idReg1())); // ddddd assert(isLowPredicateRegister(id->idReg2())); // ggg assert(isVectorRegister(id->idReg3())); // mmmmm - assert(isValidVectorElemsizeWidening(id->idOpSize())); + assert(isScalableVectorSize(id->idOpSize())); break; // Scalable, possibly FP. diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 82cf179c742f4..dc1d2918b88d5 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -70,175 +70,176 @@ enum HWIntrinsicCategory : uint8_t #else #error Unsupported platform #endif + enum HWIntrinsicFlag : unsigned int { HW_Flag_NoFlag = 0, // Commutative // - if a binary-op intrinsic is commutative (e.g., Add, Multiply), its op1 can be contained - HW_Flag_Commutative = 0x1, + HW_Flag_Commutative = (1 << 0), // NoCodeGen // - should be transformed in the compiler front-end, cannot reach CodeGen - HW_Flag_NoCodeGen = 0x2, + HW_Flag_NoCodeGen = (1 << 1), // Multi-instruction // - that one intrinsic can generate multiple instructions - HW_Flag_MultiIns = 0x4, + HW_Flag_MultiIns = (1 << 2), // Select base type using the first argument type - HW_Flag_BaseTypeFromFirstArg = 0x8, + HW_Flag_BaseTypeFromFirstArg = (1 << 3), // Select base type using the second argument type - HW_Flag_BaseTypeFromSecondArg = 0x10, + HW_Flag_BaseTypeFromSecondArg = (1 << 4), // Indicates compFloatingPointUsed does not need to be set. - HW_Flag_NoFloatingPointUsed = 0x20, + HW_Flag_NoFloatingPointUsed = (1 << 5), // NoJmpTable IMM // the imm intrinsic does not need jumptable fallback when it gets non-const argument - HW_Flag_NoJmpTableIMM = 0x40, + HW_Flag_NoJmpTableIMM = (1 << 6), // Special codegen // the intrinsics need special rules in CodeGen, // but may be table-driven in the front-end - HW_Flag_SpecialCodeGen = 0x80, + HW_Flag_SpecialCodeGen = (1 << 7), // Special import // the intrinsics need special rules in importer, // but may be table-driven in the back-end - HW_Flag_SpecialImport = 0x100, + HW_Flag_SpecialImport = (1 << 8), // The intrinsic returns result in multiple registers. - HW_Flag_MultiReg = 0x200, + HW_Flag_MultiReg = (1 << 9), + + // The intrinsic has some barrier special side effect that should be tracked + HW_Flag_SpecialSideEffect_Barrier = (1 << 10), + + // The intrinsic has some other special side effect that should be tracked + HW_Flag_SpecialSideEffect_Other = (1 << 11), + + HW_Flag_SpecialSideEffectMask = (HW_Flag_SpecialSideEffect_Barrier | HW_Flag_SpecialSideEffect_Other), -// The below is for defining platform-specific flags + // MaybeNoJmpTable IMM + // the imm intrinsic may not need jumptable fallback when it gets non-const argument + HW_Flag_MaybeNoJmpTableIMM = (1 << 12), + + HW_Flag_CanBenefitFromConstantProp = (1 << 13), + + // Used as a base for shifting the platform specific flags. + HW_Flag_PlatformBase = 13, +#define HW_TARGET_FLAG(id) (unsigned int)(1 << (id + HW_Flag_PlatformBase)) + +// Platform-specific flags #if defined(TARGET_XARCH) // Full range IMM intrinsic // - the immediate value is valid on the full range of imm8 (0-255) - HW_Flag_FullRangeIMM = 0x400, + HW_Flag_FullRangeIMM = HW_TARGET_FLAG(1), // Maybe IMM // the intrinsic has either imm or Vector overloads - HW_Flag_MaybeIMM = 0x800, + HW_Flag_MaybeIMM = HW_TARGET_FLAG(2), // Copy Upper bits // some SIMD scalar intrinsics need the semantics of copying upper bits from the source operand - HW_Flag_CopyUpperBits = 0x1000, + HW_Flag_CopyUpperBits = HW_TARGET_FLAG(3), // Maybe Memory Load/Store // - some intrinsics may have pointer overloads but without HW_Category_MemoryLoad/HW_Category_MemoryStore - HW_Flag_MaybeMemoryLoad = 0x2000, - HW_Flag_MaybeMemoryStore = 0x4000, + HW_Flag_MaybeMemoryLoad = HW_TARGET_FLAG(4), + HW_Flag_MaybeMemoryStore = HW_TARGET_FLAG(5), // No Read/Modify/Write Semantics // the intrinsic doesn't have read/modify/write semantics in two/three-operand form. - HW_Flag_NoRMWSemantics = 0x8000, + HW_Flag_NoRMWSemantics = HW_TARGET_FLAG(6), // NoContainment // the intrinsic cannot be handled by containment, // all the intrinsic that have explicit memory load/store semantics should have this flag - HW_Flag_NoContainment = 0x10000, + HW_Flag_NoContainment = HW_TARGET_FLAG(7), // Returns Per-Element Mask // the intrinsic returns a vector containing elements that are either "all bits set" or "all bits clear" // this output can be used as a per-element mask - HW_Flag_ReturnsPerElementMask = 0x20000, + HW_Flag_ReturnsPerElementMask = HW_TARGET_FLAG(8), // AvxOnlyCompatible // the intrinsic can be used on hardware with AVX but not AVX2 support - HW_Flag_AvxOnlyCompatible = 0x40000, + HW_Flag_AvxOnlyCompatible = HW_TARGET_FLAG(9), // MaybeCommutative // - if a binary-op intrinsic is maybe commutative (e.g., Max or Min for float/double), its op1 can possibly be // contained - HW_Flag_MaybeCommutative = 0x80000, + HW_Flag_MaybeCommutative = HW_TARGET_FLAG(10), // The intrinsic has no EVEX compatible form - HW_Flag_NoEvexSemantics = 0x100000, + HW_Flag_NoEvexSemantics = HW_TARGET_FLAG(11), + + // The intrinsic is an RMW intrinsic + HW_Flag_RmwIntrinsic = HW_TARGET_FLAG(12), + + // The intrinsic is a FusedMultiplyAdd intrinsic + HW_Flag_FmaIntrinsic = HW_TARGET_FLAG(13), + + // The intrinsic is a PermuteVar2x intrinsic + HW_Flag_PermuteVar2x = HW_TARGET_FLAG(14), + + // The intrinsic is an embedded broadcast compatible intrinsic + HW_Flag_EmbBroadcastCompatible = HW_TARGET_FLAG(15), + + // The intrinsic is an embedded rounding compatible intrinsic + HW_Flag_EmbRoundingCompatible = HW_TARGET_FLAG(16), + + // The intrinsic is an embedded masking incompatible intrinsic + HW_Flag_EmbMaskingIncompatible = HW_TARGET_FLAG(17), #elif defined(TARGET_ARM64) // The intrinsic has an immediate operand // - the value can be (and should be) encoded in a corresponding instruction when the operand value is constant - HW_Flag_HasImmediateOperand = 0x400, + HW_Flag_HasImmediateOperand = HW_TARGET_FLAG(1), // The intrinsic has read/modify/write semantics in multiple-operands form. - HW_Flag_HasRMWSemantics = 0x800, + HW_Flag_HasRMWSemantics = HW_TARGET_FLAG(2), // The intrinsic operates on the lower part of a SIMD register // - the upper part of the source registers are ignored // - the upper part of the destination register is zeroed - HW_Flag_SIMDScalar = 0x1000, + HW_Flag_SIMDScalar = HW_TARGET_FLAG(3), // The intrinsic supports some sort of containment analysis - HW_Flag_SupportsContainment = 0x2000, + HW_Flag_SupportsContainment = HW_TARGET_FLAG(4), // The intrinsic needs consecutive registers - HW_Flag_NeedsConsecutiveRegisters = 0x4000, + HW_Flag_NeedsConsecutiveRegisters = HW_TARGET_FLAG(5), // The intrinsic uses scalable registers - HW_Flag_Scalable = 0x8000, + HW_Flag_Scalable = HW_TARGET_FLAG(6), // Returns Per-Element Mask // the intrinsic returns a vector containing elements that are either "all bits set" or "all bits clear" // this output can be used as a per-element mask - HW_Flag_ReturnsPerElementMask = 0x10000, + HW_Flag_ReturnsPerElementMask = HW_TARGET_FLAG(7), // The intrinsic uses a mask in arg1 to select elements present in the result - HW_Flag_ExplicitMaskedOperation = 0x20000, + HW_Flag_ExplicitMaskedOperation = HW_TARGET_FLAG(8), // The intrinsic uses a mask in arg1 to select elements present in the result, and must use a low register. - HW_Flag_LowMaskedOperation = 0x40000, + HW_Flag_LowMaskedOperation = HW_TARGET_FLAG(9), // The intrinsic can optionally use a mask in arg1 to select elements present in the result, which is not present in // the API call - HW_Flag_OptionalEmbeddedMaskedOperation = 0x80000, + HW_Flag_OptionalEmbeddedMaskedOperation = HW_TARGET_FLAG(10), // The intrinsic uses a mask in arg1 to select elements present in the result, which is not present in the API call - HW_Flag_EmbeddedMaskedOperation = 0x100000, + HW_Flag_EmbeddedMaskedOperation = HW_TARGET_FLAG(11), + + // The intrinsic has an enum operand. Using this implies HW_Flag_HasImmediateOperand. + HW_Flag_HasEnumOperand = HW_TARGET_FLAG(12), #else #error Unsupported platform #endif - - // The intrinsic has some barrier special side effect that should be tracked - HW_Flag_SpecialSideEffect_Barrier = 0x200000, - - // The intrinsic has some other special side effect that should be tracked - HW_Flag_SpecialSideEffect_Other = 0x400000, - - HW_Flag_SpecialSideEffectMask = (HW_Flag_SpecialSideEffect_Barrier | HW_Flag_SpecialSideEffect_Other), - - // MaybeNoJmpTable IMM - // the imm intrinsic may not need jumptable fallback when it gets non-const argument - HW_Flag_MaybeNoJmpTableIMM = 0x800000, - -#if defined(TARGET_XARCH) - // The intrinsic is an RMW intrinsic - HW_Flag_RmwIntrinsic = 0x1000000, - - // The intrinsic is a FusedMultiplyAdd intrinsic - HW_Flag_FmaIntrinsic = 0x2000000, - - // The intrinsic is a PermuteVar2x intrinsic - HW_Flag_PermuteVar2x = 0x4000000, - - // The intrinsic is an embedded broadcast compatible intrinsic - HW_Flag_EmbBroadcastCompatible = 0x8000000, - - // The intrinsic is an embedded rounding compatible intrinsic - HW_Flag_EmbRoundingCompatible = 0x10000000, - - // The intrinsic is an embedded masking incompatible intrinsic - HW_Flag_EmbMaskingIncompatible = 0x20000000, -#elif defined(TARGET_ARM64) - - // The intrinsic has an enum operand. Using this implies HW_Flag_HasImmediateOperand. - HW_Flag_HasEnumOperand = 0x1000000, - -#endif // TARGET_XARCH - - HW_Flag_CanBenefitFromConstantProp = 0x80000000, }; #if defined(TARGET_XARCH) diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index ef9740e455652..538f42363bbe1 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -19,6 +19,7 @@ // Sve HARDWARE_INTRINSIC(Sve, Abs, -1, -1, false, {INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_fabs, INS_sve_fabs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation) HARDWARE_INTRINSIC(Sve, Add, -1, -1, false, {INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_fadd, INS_sve_fadd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, AddAcross, -1, 1, true, {INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_faddv, INS_sve_faddv}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation) HARDWARE_INTRINSIC(Sve, ConditionalSelect, -1, 3, true, {INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_SupportsContainment) HARDWARE_INTRINSIC(Sve, Count16BitElements, 0, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cnth, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_NoFloatingPointUsed) HARDWARE_INTRINSIC(Sve, Count32BitElements, 0, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cntw, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_NoFloatingPointUsed) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index 3b992e440ef6b..1b7bf1618a22f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -148,6 +148,68 @@ internal Arm64() { } /// public static unsafe Vector Add(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// AddAcross : Add reduction + + /// + /// float64_t svaddv[_f64](svbool_t pg, svfloat64_t op) + /// FADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svaddv[_s16](svbool_t pg, svint16_t op) + /// SADDV Dresult, Pg, Zop.H + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svaddv[_s32](svbool_t pg, svint32_t op) + /// SADDV Dresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svaddv[_s8](svbool_t pg, svint8_t op) + /// SADDV Dresult, Pg, Zop.B + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svaddv[_s64](svbool_t pg, svint64_t op) + /// UADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svaddv[_f32](svbool_t pg, svfloat32_t op) + /// FADDV Sresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svaddv[_u8](svbool_t pg, svuint8_t op) + /// UADDV Dresult, Pg, Zop.B + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svaddv[_u16](svbool_t pg, svuint16_t op) + /// UADDV Dresult, Pg, Zop.H + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svaddv[_u32](svbool_t pg, svuint32_t op) + /// UADDV Dresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svaddv[_u64](svbool_t pg, svuint64_t op) + /// UADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + /// ConditionalSelect : Conditionally select elements diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index 0f4f57dad8e9b..1221ee4ec2d68 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -177,6 +177,69 @@ internal Arm64() { } public static unsafe Vector Add(Vector left, Vector right) => Add(left, right); + /// AddAcross : Add reduction + + /// + /// float64_t svaddv[_f64](svbool_t pg, svfloat64_t op) + /// FADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// int64_t svaddv[_s16](svbool_t pg, svint16_t op) + /// SADDV Dresult, Pg, Zop.H + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// int64_t svaddv[_s32](svbool_t pg, svint32_t op) + /// SADDV Dresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// int64_t svaddv[_s8](svbool_t pg, svint8_t op) + /// SADDV Dresult, Pg, Zop.B + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// int64_t svaddv[_s64](svbool_t pg, svint64_t op) + /// UADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// float32_t svaddv[_f32](svbool_t pg, svfloat32_t op) + /// FADDV Sresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// uint64_t svaddv[_u8](svbool_t pg, svuint8_t op) + /// UADDV Dresult, Pg, Zop.B + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// uint64_t svaddv[_u16](svbool_t pg, svuint16_t op) + /// UADDV Dresult, Pg, Zop.H + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// uint64_t svaddv[_u32](svbool_t pg, svuint32_t op) + /// UADDV Dresult, Pg, Zop.S + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// + /// uint64_t svaddv[_u64](svbool_t pg, svuint64_t op) + /// UADDV Dresult, Pg, Zop.D + /// + public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + + /// ConditionalSelect : Conditionally select elements /// 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 881100ff95976..72470a15147e7 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -4187,6 +4187,16 @@ internal Arm64() { } public static System.Numerics.Vector Add(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 Add(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } public static ulong Count16BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } public static ulong Count32BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } public static ulong Count64BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index db964d4793e1b..df91f8878d27a 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -98,29 +98,30 @@ (string templateFileName, string outputTemplateName, Dictionary templateData)[] Templates = new[] { - ("_UnaryOpScalarTestTemplate.template", "DuplicateTest.template", new Dictionary { ["TemplateName"] = "Duplicate", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_ImmUnaryOpTestTemplate.template", "ImmUnOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_ImmUnaryOpTestTemplate.template", "VecImmUnOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_ImmTernaryOpTestTemplate.template", "ImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_ImmOpTestTemplate.template", "ImmOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_ImmBinaryOpTestTemplate.template", "ImmBinOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_ImmBinaryOpTestTemplate.template", "VecImmBinOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_BinaryOpTestTemplate.template", "SimpleBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_TernaryOpTestTemplate.template", "VecTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_ImmTernaryOpTestTemplate.template", "VecImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_ImmTernaryOpTestTemplate.template", "SimpleImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_UnaryOpTestTemplate.template", "SimpleUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_UnaryOpTestTemplate.template", "SimpleVecOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_BinaryOpTestTemplate.template", "VecPairBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecPairBinOpTest_ValidationLogic }), - ("_BinaryOp_SveTestTemplate.template", "SveVecPairBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecPairBinOpTest_ValidationLogic }), - ("_UnaryOpTestTemplate.template", "VecReduceUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecReduceOpTest_ValidationLogic }), - ("_BinaryOpTestTemplate.template", "VecBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), - ("_TernaryOpTestTemplate.template", "SimpleTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), - ("_UnaryOpTestTemplate.template", "SecureHashUnOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), - ("_BinaryOpTestTemplate.template", "SecureHashBinOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), - ("_TernaryOpTestTemplate.template", "SecureHashTernOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), - ("_SveUnaryOpTestTemplate.template", "SveSimpleVecOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), - ("_SveBinaryOpTestTemplate.template", "SveVecBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), + ("_UnaryOpScalarTestTemplate.template", "DuplicateTest.template", new Dictionary { ["TemplateName"] = "Duplicate", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_ImmUnaryOpTestTemplate.template", "ImmUnOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_ImmUnaryOpTestTemplate.template", "VecImmUnOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_ImmTernaryOpTestTemplate.template", "ImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_ImmOpTestTemplate.template", "ImmOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_ImmBinaryOpTestTemplate.template", "ImmBinOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_ImmBinaryOpTestTemplate.template", "VecImmBinOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_BinaryOpTestTemplate.template", "SimpleBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_TernaryOpTestTemplate.template", "VecTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_ImmTernaryOpTestTemplate.template", "VecImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_ImmTernaryOpTestTemplate.template", "SimpleImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_UnaryOpTestTemplate.template", "SimpleUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_UnaryOpTestTemplate.template", "SimpleVecOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_BinaryOpTestTemplate.template", "VecPairBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecPairBinOpTest_ValidationLogic }), + ("_BinaryOp_SveTestTemplate.template", "SveVecPairBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecPairBinOpTest_ValidationLogic }), + ("_UnaryOpTestTemplate.template", "VecReduceUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecReduceOpTest_ValidationLogic }), + ("_BinaryOpTestTemplate.template", "VecBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_TernaryOpTestTemplate.template", "SimpleTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleOpTest_ValidationLogic }), + ("_UnaryOpTestTemplate.template", "SecureHashUnOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), + ("_BinaryOpTestTemplate.template", "SecureHashBinOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), + ("_TernaryOpTestTemplate.template", "SecureHashTernOpTest.template", new Dictionary { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), + ("_SveUnaryOpTestTemplate.template", "SveSimpleVecOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), + ("_SveBinaryOpTestTemplate.template", "SveVecBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), + ("_SveMinimalUnaryOpTestTemplate.template", "SveVecReduceUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecReduceOpTest_ValidationLogic }), }; (string templateFileName, Dictionary templateData)[] AdvSimdInputs = new [] @@ -2909,6 +2910,17 @@ ("SveVecBinOpTest.template", new Dictionary { ["TestName"] = "Sve_Add_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Add", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.Add(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.Add(left[i], right[i])"}), ("SveVecBinOpTest.template", new Dictionary { ["TestName"] = "Sve_Add_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Add", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.Add(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.Add(left[i], right[i])"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossRecursivePairwise(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0.0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossRecursivePairwise(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0.0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_long_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWideningLong(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_long_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWideningLong(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_long_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWidening(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateReduceOpResult"] = "Helpers.AddAcross(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_ulong_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWideningULong(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_ulong_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWideningULong(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateReduceOpResult"] = "Helpers.AddAcrossWidening(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveVecReduceUnOpTest.template", new Dictionary { ["TestName"] = "Sve_AddAcross_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AddAcross", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateReduceOpResult"] = "Helpers.AddAcross(firstOp) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), + ("SveConditionalSelect.template", new Dictionary { ["TestName"] = "Sve_ConditionalSelect_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), ("SveConditionalSelect.template", new Dictionary { ["TestName"] = "Sve_ConditionalSelect_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), ("SveConditionalSelect.template", new Dictionary { ["TestName"] = "Sve_ConditionalSelect_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index 4b44b29337573..232bfdcbb2121 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -1443,6 +1443,8 @@ public static float CompareTest(float left, float right) public static short AddAcrossWidening(sbyte[] op1) => Reduce(AddWidening, op1); + public static long AddAcrossWideningLong(sbyte[] op1) => Reduce(AddWidening, op1); + public static short AddPairwiseWidening(sbyte[] op1, int i) => AddWidening(op1[2 * i], op1[2 * i + 1]); public static short AddPairwiseWideningAndAdd(short[] op1, sbyte[] op2, int i) => (short)(op1[i] + AddWidening(op2[2 * i], op2[2 * i + 1])); @@ -1469,6 +1471,8 @@ private static sbyte HighNarrowing(short op1, bool round) public static short AddWidening(short op1, sbyte op2) => (short)(op1 + op2); + public static long AddWidening(long op1, sbyte op2) => (long)(op1 + (long)op2); + public static short AddWideningUpper(sbyte[] op1, sbyte[] op2, int i) => AddWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); public static short AddWideningUpper(short[] op1, sbyte[] op2, int i) => AddWidening(op1[i], op2[i + op2.Length / 2]); @@ -1533,6 +1537,18 @@ private static short Reduce(Func reduceOp, sbyte[] op1) return acc; } + private static long Reduce(Func reduceOp, sbyte[] op1) + { + long acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + public static uint AbsoluteDifferenceWidening(short op1, short op2) => op1 < op2 ? (uint)(op2 - op1) : (uint)(op1 - op2); public static uint AbsoluteDifferenceWideningUpper(short[] op1, short[] op2, int i) => AbsoluteDifferenceWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); @@ -1543,6 +1559,8 @@ private static short Reduce(Func reduceOp, sbyte[] op1) public static int AddAcrossWidening(short[] op1) => Reduce(AddWidening, op1); + public static long AddAcrossWideningLong(short[] op1) => Reduce(AddWidening, op1); + public static int AddPairwiseWidening(short[] op1, int i) => AddWidening(op1[2 * i], op1[2 * i + 1]); public static int AddPairwiseWideningAndAdd(int[] op1, short[] op2, int i) => (int)(op1[i] + AddWidening(op2[2 * i], op2[2 * i + 1])); @@ -1569,6 +1587,8 @@ private static short HighNarrowing(int op1, bool round) public static int AddWidening(int op1, short op2) => (int)(op1 + op2); + public static long AddWidening(long op1, short op2) => (long)(op1 + (long)op2); + public static int AddWideningUpper(short[] op1, short[] op2, int i) => AddWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); public static int AddWideningUpper(int[] op1, short[] op2, int i) => AddWidening(op1[i], op2[i + op2.Length / 2]); @@ -1633,6 +1653,18 @@ private static int Reduce(Func reduceOp, short[] op1) return acc; } + private static long Reduce(Func reduceOp, short[] op1) + { + long acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + public static ulong AbsoluteDifferenceWidening(int op1, int op2) => op1 < op2 ? (ulong)(op2 - op1) : (ulong)(op1 - op2); public static ulong AbsoluteDifferenceWideningUpper(int[] op1, int[] op2, int i) => AbsoluteDifferenceWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); @@ -1743,6 +1775,8 @@ private static long Reduce(Func reduceOp, int[] op1) public static ushort AddAcrossWidening(byte[] op1) => Reduce(AddWidening, op1); + public static ulong AddAcrossWideningULong(byte[] op1) => Reduce(AddWidening, op1); + public static ushort AddPairwiseWidening(byte[] op1, int i) => AddWidening(op1[2 * i], op1[2 * i + 1]); public static ushort AddPairwiseWideningAndAdd(ushort[] op1, byte[] op2, int i) => (ushort)(op1[i] + AddWidening(op2[2 * i], op2[2 * i + 1])); @@ -1769,6 +1803,8 @@ private static byte HighNarrowing(ushort op1, bool round) public static ushort AddWidening(ushort op1, byte op2) => (ushort)(op1 + op2); + public static ulong AddWidening(ulong op1, byte op2) => (ulong)(op1 + (ulong)op2); + public static ushort AddWideningUpper(byte[] op1, byte[] op2, int i) => AddWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); public static ushort AddWideningUpper(ushort[] op1, byte[] op2, int i) => AddWidening(op1[i], op2[i + op2.Length / 2]); @@ -1833,6 +1869,18 @@ private static ushort Reduce(Func reduceOp, byte[] op1) return acc; } + private static ulong Reduce(Func reduceOp, byte[] op1) + { + ulong acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + public static uint AbsoluteDifferenceWidening(ushort op1, ushort op2) => op1 < op2 ? (uint)(op2 - op1) : (uint)(op1 - op2); public static uint AbsoluteDifferenceWideningUpper(ushort[] op1, ushort[] op2, int i) => AbsoluteDifferenceWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); @@ -1843,6 +1891,8 @@ private static ushort Reduce(Func reduceOp, byte[] op1) public static uint AddAcrossWidening(ushort[] op1) => Reduce(AddWidening, op1); + public static ulong AddAcrossWideningULong(ushort[] op1) => Reduce(AddWidening, op1); + public static uint AddPairwiseWidening(ushort[] op1, int i) => AddWidening(op1[2 * i], op1[2 * i + 1]); public static uint AddPairwiseWideningAndAdd(uint[] op1, ushort[] op2, int i) => (uint)(op1[i] + AddWidening(op2[2 * i], op2[2 * i + 1])); @@ -1869,6 +1919,8 @@ private static ushort HighNarrowing(uint op1, bool round) public static uint AddWidening(uint op1, ushort op2) => (uint)(op1 + op2); + public static ulong AddWidening(ulong op1, ushort op2) => (ulong)(op1 + (ulong)op2); + public static uint AddWideningUpper(ushort[] op1, ushort[] op2, int i) => AddWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); public static uint AddWideningUpper(uint[] op1, ushort[] op2, int i) => AddWidening(op1[i], op2[i + op2.Length / 2]); @@ -1933,6 +1985,18 @@ private static uint Reduce(Func reduceOp, ushort[] op1) return acc; } + private static ulong Reduce(Func reduceOp, ushort[] op1) + { + ulong acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + public static ulong AbsoluteDifferenceWidening(uint op1, uint op2) => op1 < op2 ? (ulong)(op2 - op1) : (ulong)(op1 - op2); public static ulong AbsoluteDifferenceWideningUpper(uint[] op1, uint[] op2, int i) => AbsoluteDifferenceWidening(op1[i + op1.Length / 2], op2[i + op2.Length / 2]); @@ -2033,6 +2097,9 @@ private static ulong Reduce(Func reduceOp, uint[] op1) return acc; } + public static double AddWidening(double op1, float op2) => (double)(op1 + (double)op2); + + private static bool SignedSatQ(short val, out sbyte result) { bool saturated = false; @@ -5134,6 +5201,42 @@ private static uint Reduce(Func reduceOp, uint[] op1) return acc; } + public static long AddAcross(long[] op1) => Reduce(Add, op1); + + public static long MaxAcross(long[] op1) => Reduce(Max, op1); + + public static long MinAcross(long[] op1) => Reduce(Min, op1); + + private static long Reduce(Func reduceOp, long[] op1) + { + long acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + + public static ulong AddAcross(ulong[] op1) => Reduce(Add, op1); + + public static ulong MaxAcross(ulong[] op1) => Reduce(Max, op1); + + public static ulong MinAcross(ulong[] op1) => Reduce(Min, op1); + + private static ulong Reduce(Func reduceOp, ulong[] op1) + { + ulong acc = op1[0]; + + for (int i = 1; i < op1.Length; i++) + { + acc = reduceOp(acc, op1[i]); + } + + return acc; + } + public static float AddAcross(float[] op1) => Reduce(Add, op1); public static float MaxAcross(float[] op1) => Reduce(Max, op1); @@ -5152,6 +5255,31 @@ private static float Reduce(Func reduceOp, float[] op1) return acc; } + public static float AddAcrossRecursivePairwise(float[] op1) => ReduceRecursivePairwise(Add, op1); + + private static float ReduceRecursivePairwise(Func reduceOp, float[] op1) + { + if (op1.Length == 2) + { + return reduceOp(op1[0], op1[1]); + } + + if (op1.Length % 2 != 0) + { + return float.NaN; + } + + float[] l = new float[op1.Length / 2]; + Array.Copy(op1, 0, l, 0, (op1.Length / 2)); + float l_reduced = ReduceRecursivePairwise(reduceOp, l); + + float[] r = new float[op1.Length / 2]; + Array.Copy(op1, (op1.Length / 2), r, 0, (op1.Length / 2)); + float r_reduced = ReduceRecursivePairwise(reduceOp, r); + + return reduceOp(l_reduced, r_reduced); + } + public static double AddAcross(double[] op1) => Reduce(Add, op1); public static double MaxAcross(double[] op1) => Reduce(Max, op1); @@ -5170,6 +5298,31 @@ private static double Reduce(Func reduceOp, double[] op1 return acc; } + public static double AddAcrossRecursivePairwise(double[] op1) => ReduceRecursivePairwise(Add, op1); + + private static double ReduceRecursivePairwise(Func reduceOp, double[] op1) + { + if (op1.Length == 2) + { + return reduceOp(op1[0], op1[1]); + } + + if (op1.Length % 2 != 0) + { + return double.NaN; + } + + double[] l = new double[op1.Length / 2]; + Array.Copy(op1, 0, l, 0, (op1.Length / 2)); + double l_reduced = ReduceRecursivePairwise(reduceOp, l); + + double[] r = new double[op1.Length / 2]; + Array.Copy(op1, (op1.Length / 2), r, 0, (op1.Length / 2)); + double r_reduced = ReduceRecursivePairwise(reduceOp, r); + + return reduceOp(l_reduced, r_reduced); + } + public static float MaxNumberAcross(float[] op1) => Reduce(MaxNumber, op1); public static float MinNumberAcross(float[] op1) => Reduce(MinNumber, op1); diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMinimalUnaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMinimalUnaryOpTestTemplate.template new file mode 100644 index 0000000000000..782f77de3520e --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMinimalUnaryOpTestTemplate.template @@ -0,0 +1,302 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TemplateName}UnaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class {TemplateName}UnaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {RetBaseType}[] outArray, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfoutArray) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfoutArray: {sizeOfoutArray}"); + } + + this.inArray1 = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario({TemplateName}UnaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld1); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld1, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + + private DataTable _dataTable; + + public {TemplateName}UnaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + _dataTable = new DataTable(_data1, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var result = {Isa}.{Method}(op1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, outArray, method); + } + + private void ValidateResult(void* op1, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {TemplateValidationLogic} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpTestTemplate.template index 557442de4cf0b..119cc5d88cb6f 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpTestTemplate.template @@ -191,7 +191,7 @@ namespace JIT.HardwareIntrinsics.Arm { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); - {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); var result = {Isa}.{Method}( {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr))