Skip to content

Commit

Permalink
[wasm] Add intrinsics for clz and ctz instructions (dotnet#77777)
Browse files Browse the repository at this point in the history
* [wasm] Add intrinsics for clz and ctz instructions

Add intrinsics for leading and trailing zero count for wasm.

Introduce new internal class WasmBase for non-SIMD wasm intrinsics.
These are always used in AOT build, unlike SIMD intrinsics, which are
disabled by default.

* Fix CI build

* Fix CI build again

* Review feedback

Refactor the code to avoid adding ifdefs

* Fix build

* Fix build

* Fix build

* Fix CI build
  • Loading branch information
radekdoulik authored Nov 8, 2022
1 parent 62837e5 commit 134968a
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2405,9 +2405,11 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Arm\Sha256.PlatformNotSupported.cs" />
</ItemGroup>
<ItemGroup Condition="'$(SupportsWasmIntrinsics)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Wasm\WasmBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Wasm\PackedSimd.cs" />
</ItemGroup>
<ItemGroup Condition="'$(SupportsWasmIntrinsics)' != 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Wasm\WasmBase.PlatformNotSupported.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Wasm\PackedSimd.PlatformNotSupported.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeaturePortableThreadPool)' == 'true'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// i32.clz
/// </summary>
public static int LeadingZeroCount(int value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i32.clz
/// </summary>
public static int LeadingZeroCount(uint value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i64.clz
/// </summary>
public static int LeadingZeroCount(long value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i64.clz
/// </summary>
public static int LeadingZeroCount(ulong value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i32.ctz
/// </summary>
public static int TrailingZeroCount(int value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i32.ctz
/// </summary>
public static int TrailingZeroCount(uint value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i64.ctz
/// </summary>
public static int TrailingZeroCount(long value) { throw new PlatformNotSupportedException(); }

/// <summary>
/// i64.ctz
/// </summary>
public static int TrailingZeroCount(ulong value) { throw new PlatformNotSupportedException(); }

}
}
Original file line number Diff line number Diff line change
@@ -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; }

/// <summary>
/// i32.clz
/// </summary>
public static int LeadingZeroCount(int value) => LeadingZeroCount(value);

/// <summary>
/// i32.clz
/// </summary>
public static int LeadingZeroCount(uint value) => LeadingZeroCount(value);

/// <summary>
/// i64.clz
/// </summary>
public static int LeadingZeroCount(long value) => LeadingZeroCount(value);

/// <summary>
/// i64.clz
/// </summary>
public static int LeadingZeroCount(ulong value) => LeadingZeroCount(value);

/// <summary>
/// i32.ctz
/// </summary>
public static int TrailingZeroCount(int value) => TrailingZeroCount(value);

/// <summary>
/// i32.ctz
/// </summary>
public static int TrailingZeroCount(uint value) => TrailingZeroCount(value);

/// <summary>
/// i64.ctz
/// </summary>
public static int TrailingZeroCount(long value) => TrailingZeroCount(value);

/// <summary>
/// i64.ctz
/// </summary>
public static int TrailingZeroCount(ulong value) => TrailingZeroCount(value);
}
}
1 change: 1 addition & 0 deletions src/mono/mono/mini/aot-compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/mono/mono/mini/intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/mono/mono/mini/mini.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 3 additions & 1 deletion src/mono/mono/mini/mini.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 134968a

Please sign in to comment.