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);
}
/*