diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b5f1f4217c2f2..b368c8675292c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2405,9 +2405,11 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index e14f8409e5fb5..088b510453cd5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -8,6 +8,7 @@ using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics.Wasm; // Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson: // http://graphics.stanford.edu/~seander/bithacks.html @@ -95,7 +96,7 @@ public static class BitOperations [CLSCompliant(false)] public static uint RoundUpToPowerOf2(uint value) { - if (Lzcnt.IsSupported || ArmBase.IsSupported || X86Base.IsSupported) + if (Lzcnt.IsSupported || ArmBase.IsSupported || X86Base.IsSupported || WasmBase.IsSupported) { #if TARGET_64BIT return (uint)(0x1_0000_0000ul >> LeadingZeroCount(value - 1)); @@ -127,7 +128,7 @@ public static uint RoundUpToPowerOf2(uint value) [CLSCompliant(false)] public static ulong RoundUpToPowerOf2(ulong value) { - if (Lzcnt.X64.IsSupported || ArmBase.Arm64.IsSupported) + if (Lzcnt.X64.IsSupported || ArmBase.Arm64.IsSupported || WasmBase.IsSupported) { int shift = 64 - LeadingZeroCount(value - 1); return (1ul ^ (ulong)(shift >> 6)) << shift; @@ -195,6 +196,11 @@ public static int LeadingZeroCount(uint value) return ArmBase.LeadingZeroCount(value); } + if (WasmBase.IsSupported) + { + return WasmBase.LeadingZeroCount(value); + } + // Unguarded fallback contract is 0->31, BSR contract is 0->undefined if (value == 0) { @@ -232,6 +238,11 @@ public static int LeadingZeroCount(ulong value) return ArmBase.Arm64.LeadingZeroCount(value); } + if (WasmBase.IsSupported) + { + return WasmBase.LeadingZeroCount(value); + } + if (X86Base.X64.IsSupported) { // BSR contract is 0->undefined @@ -305,6 +316,11 @@ public static int Log2(uint value) return 31 ^ ArmBase.LeadingZeroCount(value); } + if (WasmBase.IsSupported) + { + return 31 ^ WasmBase.LeadingZeroCount(value); + } + // BSR returns the log2 result directly. However BSR is slower than LZCNT // on AMD processors, so we leave it as a fallback only. if (X86Base.IsSupported) @@ -342,6 +358,11 @@ public static int Log2(ulong value) return (int)X86Base.X64.BitScanReverse(value); } + if (WasmBase.IsSupported) + { + return 63 ^ WasmBase.LeadingZeroCount(value); + } + uint hi = (uint)(value >> 32); if (hi == 0) @@ -551,6 +572,11 @@ public static int TrailingZeroCount(uint value) return ArmBase.LeadingZeroCount(ArmBase.ReverseElementBits(value)); } + if (WasmBase.IsSupported) + { + return WasmBase.TrailingZeroCount(value); + } + // Unguarded fallback contract is 0->0, BSF contract is 0->undefined if (value == 0) { @@ -599,6 +625,11 @@ public static int TrailingZeroCount(ulong value) return ArmBase.Arm64.LeadingZeroCount(ArmBase.Arm64.ReverseElementBits(value)); } + if (WasmBase.IsSupported) + { + return WasmBase.TrailingZeroCount(value); + } + if (X86Base.X64.IsSupported) { // BSF contract is 0->undefined diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.PlatformNotSupported.cs new file mode 100644 index 0000000000000..2f0faac2eb160 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.PlatformNotSupported.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Wasm +{ + internal abstract class WasmBase + { + public static bool IsSupported => false; + + /// + /// i32.clz + /// + public static int LeadingZeroCount(int value) { throw new PlatformNotSupportedException(); } + + /// + /// i32.clz + /// + public static int LeadingZeroCount(uint value) { throw new PlatformNotSupportedException(); } + + /// + /// i64.clz + /// + public static int LeadingZeroCount(long value) { throw new PlatformNotSupportedException(); } + + /// + /// i64.clz + /// + public static int LeadingZeroCount(ulong value) { throw new PlatformNotSupportedException(); } + + /// + /// i32.ctz + /// + public static int TrailingZeroCount(int value) { throw new PlatformNotSupportedException(); } + + /// + /// i32.ctz + /// + public static int TrailingZeroCount(uint value) { throw new PlatformNotSupportedException(); } + + /// + /// i64.ctz + /// + public static int TrailingZeroCount(long value) { throw new PlatformNotSupportedException(); } + + /// + /// i64.ctz + /// + public static int TrailingZeroCount(ulong value) { throw new PlatformNotSupportedException(); } + + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.cs new file mode 100644 index 0000000000000..4d24ab84bc986 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Wasm/WasmBase.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Wasm +{ + [Intrinsic] + internal abstract class WasmBase + { + public static bool IsSupported { get; } + + /// + /// i32.clz + /// + public static int LeadingZeroCount(int value) => LeadingZeroCount(value); + + /// + /// i32.clz + /// + public static int LeadingZeroCount(uint value) => LeadingZeroCount(value); + + /// + /// i64.clz + /// + public static int LeadingZeroCount(long value) => LeadingZeroCount(value); + + /// + /// i64.clz + /// + public static int LeadingZeroCount(ulong value) => LeadingZeroCount(value); + + /// + /// i32.ctz + /// + public static int TrailingZeroCount(int value) => TrailingZeroCount(value); + + /// + /// i32.ctz + /// + public static int TrailingZeroCount(uint value) => TrailingZeroCount(value); + + /// + /// i64.ctz + /// + public static int TrailingZeroCount(long value) => TrailingZeroCount(value); + + /// + /// i64.ctz + /// + public static int TrailingZeroCount(ulong value) => TrailingZeroCount(value); + } +} diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 005298f0fabb4..f7fdc9e9771d7 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -8416,6 +8416,7 @@ parse_cpu_features (const gchar *attr) else if (!strcmp (attr + prefix, "dotprod")) feature = MONO_CPU_ARM64_DP; #elif defined(TARGET_WASM) + // MONO_CPU_WASM_BASE is unconditionally set in mini_get_cpu_features. if (!strcmp (attr + prefix, "simd")) feature = MONO_CPU_WASM_SIMD; #else diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 9f5993a414d13..a2348e0f379fe 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -88,6 +88,10 @@ mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignat if (ins) return ins; } + + ins = mono_emit_common_intrinsics (cfg, cmethod, fsig, args); + if (ins) + return ins; #endif return ins; @@ -2046,6 +2050,10 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (ins) return ins; } + + ins = mono_emit_common_intrinsics (cfg, cmethod, fsig, args); + if (ins) + return ins; #endif /* Fallback if SIMD is disabled */ diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index e65399ba94784..c5f8d470c5a9a 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -9691,7 +9691,7 @@ MONO_RESTORE_WARNING #endif /* defined(TARGET_X86) || defined(TARGET_AMD64) */ // Shared between ARM64 and X86 -#if defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_AMD64) +#if defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_WASM) case OP_LZCNT32: case OP_LZCNT64: { IntrinsicId iid = ins->opcode == OP_LZCNT32 ? INTRINS_CTLZ_I32 : INTRINS_CTLZ_I64; diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 6d66a06a55485..73f1e0ab3a718 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -4452,6 +4452,10 @@ mini_get_cpu_features (MonoCompile* cfg) features |= MONO_CPU_ARM64_NEON; #endif +#if defined(TARGET_WASM) + // All wasm VMs have this set + features |= MONO_CPU_WASM_BASE; +#endif // apply parameters passed via -mattr return (features | mono_cpu_features_enabled) & ~mono_cpu_features_disabled; } diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 5a6a061b9ffbb..b40e8fb4fb621 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2881,7 +2881,8 @@ typedef enum { | MONO_CPU_X86_AES | MONO_CPU_X86_POPCNT | MONO_CPU_X86_FMA, #endif #ifdef TARGET_WASM - MONO_CPU_WASM_SIMD = 1 << 1, + MONO_CPU_WASM_BASE = 1 << 1, + MONO_CPU_WASM_SIMD = 1 << 2, #endif #ifdef TARGET_ARM64 MONO_CPU_ARM64_BASE = 1 << 1, @@ -2921,6 +2922,7 @@ MonoCPUFeatures mono_arch_get_cpu_features (void); #ifdef MONO_ARCH_SIMD_INTRINSICS void mono_simd_simplify_indirection (MonoCompile *cfg); void mono_simd_decompose_intrinsic (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins); +MonoInst* mono_emit_common_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args); MonoInst* mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args); MonoInst* mono_emit_simd_field_load (MonoCompile *cfg, MonoClassField *field, MonoInst *addr); void mono_simd_intrinsics_init (void); diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index 639665ab53227..6181f6336e8bf 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -4250,6 +4250,12 @@ emit_sum_vector (MonoCompile *cfg, MonoType *vector_type, MonoTypeEnum element_t return extract_first_element (cfg, vector_class, element_type, vsum->dreg); } +static SimdIntrinsic wasmbase_methods [] = { + {SN_LeadingZeroCount}, + {SN_TrailingZeroCount}, + {SN_get_IsSupported}, +}; + static SimdIntrinsic packedsimd_methods [] = { {SN_Add}, {SN_And}, @@ -4269,56 +4275,79 @@ static SimdIntrinsic packedsimd_methods [] = { }; static const IntrinGroup supported_wasm_intrinsics [] = { - { "PackedSimd", 0, packedsimd_methods, sizeof (packedsimd_methods) }, + { "PackedSimd", MONO_CPU_WASM_SIMD, packedsimd_methods, sizeof (packedsimd_methods) }, +}; + +static const IntrinGroup supported_wasm_common_intrinsics [] = { + { "WasmBase", MONO_CPU_WASM_BASE, wasmbase_methods, sizeof (wasmbase_methods) }, }; static MonoInst* -emit_packedsimd_intrinsics ( +emit_wasm_supported_intrinsics ( MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoClass *klass, const IntrinGroup *intrin_group, const SimdIntrinsic *info, int id, MonoTypeEnum arg0_type, gboolean is_64bit) { - if (!is_element_type_primitive (fsig->params [0])) - return NULL; + MonoCPUFeatures feature = intrin_group->feature; - switch (id) { - case SN_Add: - case SN_Subtract: - case SN_Multiply: - return emit_simd_ins_for_binary_op (cfg, klass, fsig, args, arg0_type, id); - case SN_Negate: - return emit_simd_ins_for_unary_op (cfg, klass, fsig, args, arg0_type, id); - case SN_And: - return emit_simd_ins_for_sig (cfg, klass, OP_XBINOP_FORCEINT, XBINOP_FORCEINT_AND, arg0_type, fsig, args); - case SN_Bitmask: - return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_BITMASK, -1, -1, fsig, args); - case SN_CompareEqual: - return emit_simd_ins_for_sig (cfg, klass, type_enum_is_float (arg0_type) ? OP_XCOMPARE_FP : OP_XCOMPARE, CMP_EQ, arg0_type, fsig, args); - case SN_CompareNotEqual: - return emit_simd_ins_for_sig (cfg, klass, type_enum_is_float (arg0_type) ? OP_XCOMPARE_FP : OP_XCOMPARE, CMP_NE, arg0_type, fsig, args); - case SN_ExtractLane: { - int extract_op = type_to_xextract_op (arg0_type); - return emit_simd_ins_for_sig (cfg, klass, extract_op, -1, arg0_type, fsig, args); - } - case SN_ReplaceLane: { - int insert_op = type_to_xinsert_op (arg0_type); - MonoInst *ins = emit_simd_ins (cfg, klass, insert_op, args [0]->dreg, args [2]->dreg); - ins->sreg3 = args [1]->dreg; - ins->inst_c1 = arg0_type; - return ins; + if (feature == MONO_CPU_WASM_BASE) { + switch (id) { + case SN_LeadingZeroCount: + case SN_TrailingZeroCount: { + gboolean arg0_i32 = (arg0_type == MONO_TYPE_I4) || (arg0_type == MONO_TYPE_U4); +#if TARGET_SIZEOF_VOID_P == 4 + arg0_i32 = arg0_i32 || (arg0_type == MONO_TYPE_I) || (arg0_type == MONO_TYPE_U); +#endif + int opcode = id == SN_LeadingZeroCount ? (arg0_i32 ? OP_LZCNT32 : OP_LZCNT64) : (arg0_i32 ? OP_CTTZ32 : OP_CTTZ64); + + return emit_simd_ins_for_sig (cfg, klass, opcode, 0, arg0_type, fsig, args); + } } - case SN_Splat: { - MonoType *etype = get_vector_t_elem_type (fsig->ret); - g_assert (fsig->param_count == 1 && mono_metadata_type_equal (fsig->params [0], etype)); - return emit_simd_ins (cfg, klass, type_to_expand_op (etype), args [0]->dreg, -1); + } + + if (feature == MONO_CPU_WASM_SIMD) { + if (!is_element_type_primitive (fsig->params [0])) + return NULL; + + switch (id) { + case SN_Add: + case SN_Subtract: + case SN_Multiply: + return emit_simd_ins_for_binary_op (cfg, klass, fsig, args, arg0_type, id); + case SN_Negate: + return emit_simd_ins_for_unary_op (cfg, klass, fsig, args, arg0_type, id); + case SN_And: + return emit_simd_ins_for_sig (cfg, klass, OP_XBINOP_FORCEINT, XBINOP_FORCEINT_AND, arg0_type, fsig, args); + case SN_Bitmask: + return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_BITMASK, -1, -1, fsig, args); + case SN_CompareEqual: + return emit_simd_ins_for_sig (cfg, klass, type_enum_is_float (arg0_type) ? OP_XCOMPARE_FP : OP_XCOMPARE, CMP_EQ, arg0_type, fsig, args); + case SN_CompareNotEqual: + return emit_simd_ins_for_sig (cfg, klass, type_enum_is_float (arg0_type) ? OP_XCOMPARE_FP : OP_XCOMPARE, CMP_NE, arg0_type, fsig, args); + case SN_ExtractLane: { + int extract_op = type_to_xextract_op (arg0_type); + return emit_simd_ins_for_sig (cfg, klass, extract_op, -1, arg0_type, fsig, args); + } + case SN_ReplaceLane: { + int insert_op = type_to_xinsert_op (arg0_type); + MonoInst *ins = emit_simd_ins (cfg, klass, insert_op, args [0]->dreg, args [2]->dreg); + ins->sreg3 = args [1]->dreg; + ins->inst_c1 = arg0_type; + return ins; + } + case SN_Splat: { + MonoType *etype = get_vector_t_elem_type (fsig->ret); + g_assert (fsig->param_count == 1 && mono_metadata_type_equal (fsig->params [0], etype)); + return emit_simd_ins (cfg, klass, type_to_expand_op (etype), args [0]->dreg, -1); + } + case SN_Dot: + return emit_simd_ins_for_sig (cfg, klass, OP_XOP_X_X_X, INTRINS_WASM_DOT, -1, fsig, args); + case SN_Shuffle: + return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_SHUFFLE, -1, -1, fsig, args); + case SN_Swizzle: + return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_SWIZZLE, -1, -1, fsig, args); } - case SN_Dot: - return emit_simd_ins_for_sig (cfg, klass, OP_XOP_X_X_X, INTRINS_WASM_DOT, -1, fsig, args); - case SN_Shuffle: - return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_SHUFFLE, -1, -1, fsig, args); - case SN_Swizzle: - return emit_simd_ins_for_sig (cfg, klass, OP_WASM_SIMD_SWIZZLE, -1, -1, fsig, args); } g_assert_not_reached (); @@ -4399,7 +4428,7 @@ arch_emit_simd_intrinsics (const char *class_ns, const char *class_name, MonoCom if (!strcmp (class_ns, "System.Runtime.Intrinsics.Wasm")) { return emit_hardware_intrinsics (cfg, cmethod, fsig, args, supported_wasm_intrinsics, sizeof (supported_wasm_intrinsics), - emit_packedsimd_intrinsics); + emit_wasm_supported_intrinsics); } if (!strcmp (class_ns, "System.Runtime.Intrinsics")) { @@ -4423,8 +4452,32 @@ arch_emit_simd_intrinsics (const char *class_ns, const char *class_name, MonoCom } #endif +#if defined(TARGET_WASM) +static MonoInst* -mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +arch_emit_common_intrinsics (const char *class_ns, const char *class_name, MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + if (!strcmp (class_ns, "System.Runtime.Intrinsics.Wasm")) { + return emit_hardware_intrinsics (cfg, cmethod, fsig, args, + supported_wasm_common_intrinsics, sizeof (supported_wasm_common_intrinsics), + emit_wasm_supported_intrinsics); + } + + return NULL; +} +#else +static +MonoInst* +arch_emit_common_intrinsics (const char *class_ns, const char *class_name, MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + return NULL; +} +#endif + +typedef MonoInst* (*EmitCallback)(const char *class_ns, const char *class_name, MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args); + +static MonoInst* +emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, EmitCallback ecb) { const char *class_name; const char *class_ns; @@ -4440,7 +4493,20 @@ mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (m_class_get_nested_in (cmethod->klass)) class_ns = m_class_get_name_space (m_class_get_nested_in (cmethod->klass)); - return arch_emit_simd_intrinsics (class_ns, class_name, cfg, cmethod, fsig, args); + + return ecb (class_ns, class_name, cfg, cmethod, fsig, args); +} + +MonoInst* +mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + return emit_intrinsics (cfg, cmethod, fsig, args, arch_emit_simd_intrinsics); +} + +MonoInst* +mono_emit_common_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + return emit_intrinsics (cfg, cmethod, fsig, args, arch_emit_common_intrinsics); } /*