Skip to content

[wasm][interp] Jiterp packedsimd shifts #114669

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Diagnostics.CodeAnalysis;
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Wasm;
using System.Tests;
Expand Down Expand Up @@ -84,19 +83,38 @@ public unsafe void FloatingPointOperationsTest()
Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), floorResult);
}


[Fact]
public unsafe void LoadStoreTest()
{
int[] values = new int[] { 1, 2, 3, 4 };
int[] values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
fixed (int* ptr = values)
{
var loaded = PackedSimd.LoadVector128(ptr);
var loaded2 = PackedSimd.LoadVector128(ptr + 4);

Assert.Equal(Vector128.Create(1, 2, 3, 4), loaded);
Assert.Equal(Vector128.Create(5, 6, 7, 8), loaded2);

var vl = Vector128.LoadUnsafe(ref values[0], (nuint)0);
var vl2 = Vector128.LoadUnsafe(ref values[0], (nuint)4);
Assert.Equal(loaded, vl);
Assert.Equal(loaded2, vl2);

vl = Vector128.Load(ptr);
vl2 = Vector128.Load(ptr + 4);
Assert.Equal(loaded, vl);
Assert.Equal(loaded2, vl2);

Assert.Equal(Vector128.Create(1, 2, 3, 4), loaded);
Assert.Equal(Vector128.Create(5, 6, 7, 8), loaded2);


int[] storeTarget = new int[4];
int[] storeTarget = new int[8];
fixed (int* storePtr = storeTarget)
{
PackedSimd.Store(storePtr, loaded);
PackedSimd.Store(storePtr + 4, loaded2);
Assert.Equal(values, storeTarget);
}
}
Expand Down Expand Up @@ -549,5 +567,49 @@ public void NativeUnsignedIntegerShiftTest()
Assert.Equal(Vector128.Create([(nuint)64, unchecked((nuint)(-64)), (nuint)128, unchecked((nuint)(-128))]), leftShift);
Assert.Equal(Vector128.Create([(nuint)4, (nuint)1073741820, (nuint)8, (nuint)1073741816]), rightShiftLogical);
}

[Fact]
public unsafe void ConvertNarrowingSaturateSignedTest()
{
var v1 = Vector128.Create(32767, 32768, -32768, -32769);
var v2 = Vector128.Create(100, 200, -100, -200);

var result = PackedSimd.ConvertNarrowingSaturateSigned(v1, v2);

Assert.Equal(Vector128.Create((short)32767, (short)32767, (short)-32768, (short)-32768, (short)100, (short)200, (short)-100, (short)-200), result);
}

[Fact]
public unsafe void ConvertNarrowingSaturateUnsignedTest()
{
var v1 = Vector128.Create(65535, 65536, -1, -100);
var v2 = Vector128.Create(100, 200, 300, 400);

var result = PackedSimd.ConvertNarrowingSaturateUnsigned(v1, v2);

Assert.Equal(Vector128.Create((ushort)65535, (ushort)65535, (ushort)0, (ushort)0, (ushort)100, (ushort)200, (ushort)300, (ushort)400), result);
}

[Fact]
public unsafe void BitmaskTest()
{
var v1 = Vector128.Create((byte)0b00000001, (byte)0b00000010, (byte)0b00000100, (byte)0b00001000,
(byte)0b00010000, (byte)0b00100000, (byte)0b01000000, (byte)0b10000000,
(byte)0b00000001, (byte)0b00000010, (byte)0b00000100, (byte)0b00001000,
(byte)0b00010000, (byte)0b10100000, (byte)0b01000000, (byte)0b10000000);

var v2 = Vector128.Create((ushort)0b1100001001100001, (ushort)0b0000000000000010, (ushort)0b0000000000000100, (ushort)0b0000000000001000,
(ushort)0b0000000000010000, (ushort)0b0000000000100000, (ushort)0b0000000001000000, (ushort)0b0000000010000000);

var v3 = Vector128.Create(0b10000000000000000000000000000001, 0b00000000000111111000000000000010,
0b00000000000000000000000000000100, 0b10000000000000000000000000001000);

var bitmask1 = PackedSimd.Bitmask(v1);
var bitmask2 = PackedSimd.Bitmask(v2);
var bitmask3 = PackedSimd.Bitmask(v3);
Assert.Equal(0b1010000010000000, bitmask1);
Assert.Equal(0b1, bitmask2);
Assert.Equal(0b1001, bitmask3);
}
}
}
20 changes: 20 additions & 0 deletions src/mono/browser/runtime/jiterpreter-tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,21 @@ export const simdShiftTable = new Set<SimdIntrinsic3>([
SimdIntrinsic3.V128_I2_URIGHT_SHIFT,
SimdIntrinsic3.V128_I4_URIGHT_SHIFT,
SimdIntrinsic3.V128_I8_URIGHT_SHIFT,

SimdIntrinsic3.ShiftLeftD1,
SimdIntrinsic3.ShiftLeftD2,
SimdIntrinsic3.ShiftLeftD4,
SimdIntrinsic3.ShiftLeftD8,

SimdIntrinsic3.ShiftRightArithmeticD1,
SimdIntrinsic3.ShiftRightArithmeticD2,
SimdIntrinsic3.ShiftRightArithmeticD4,
SimdIntrinsic3.ShiftRightArithmeticD8,

SimdIntrinsic3.ShiftRightLogicalD1,
SimdIntrinsic3.ShiftRightLogicalD2,
SimdIntrinsic3.ShiftRightLogicalD4,
SimdIntrinsic3.ShiftRightLogicalD8,
]);

export const simdExtractTable: { [intrinsic: number]: [laneCount: number, laneStoreOpcode: WasmOpcode] } = {
Expand Down Expand Up @@ -428,6 +443,11 @@ export const bitmaskTable: { [intrinsic: number]: WasmSimdOpcode } = {
[SimdIntrinsic2.V128_I2_EXTRACT_MSB]: WasmSimdOpcode.i16x8_bitmask,
[SimdIntrinsic2.V128_I4_EXTRACT_MSB]: WasmSimdOpcode.i32x4_bitmask,
[SimdIntrinsic2.V128_I8_EXTRACT_MSB]: WasmSimdOpcode.i64x2_bitmask,

[SimdIntrinsic2.BitmaskD1]: WasmSimdOpcode.i8x16_bitmask,
[SimdIntrinsic2.BitmaskD2]: WasmSimdOpcode.i16x8_bitmask,
[SimdIntrinsic2.BitmaskD4]: WasmSimdOpcode.i32x4_bitmask,
[SimdIntrinsic2.BitmaskD8]: WasmSimdOpcode.i64x2_bitmask,
};

export const createScalarTable: { [intrinsic: number]: [WasmOpcode, WasmSimdOpcode] } = {
Expand Down
17 changes: 9 additions & 8 deletions src/mono/browser/runtime/jiterpreter-trace-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3688,6 +3688,15 @@ function append_simd_4_load (builder: WasmBuilder, ip: MintOpcodePtr) {

function emit_simd_2 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic2): boolean {
const simple = <WasmSimdOpcode>cwraps.mono_jiterp_get_simd_opcode(1, index);
const bitmask = bitmaskTable[index];

if (bitmask) {
append_simd_2_load(builder, ip);
builder.appendSimd(bitmask);
append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.i32_store);
return true;
}

if (simple >= 0) {
if (simdLoadTable.has(index)) {
// Indirect load, so v1 is T** and res is Vector128*
Expand All @@ -3704,14 +3713,6 @@ function emit_simd_2 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrin
return true;
}

const bitmask = bitmaskTable[index];
if (bitmask) {
append_simd_2_load(builder, ip);
builder.appendSimd(bitmask);
append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.i32_store);
return true;
}

switch (index) {
case SimdIntrinsic2.V128_I1_CREATE_SCALAR:
case SimdIntrinsic2.V128_I2_CREATE_SCALAR:
Expand Down
11 changes: 9 additions & 2 deletions src/mono/mono/mini/interp/transform-simd.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ static guint16 packedsimd_alias_methods [] = {
SN_ConvertToSingle,
SN_Divide,
SN_Equals,
SN_ExtractMostSignificantBits,
SN_Floor,
SN_GreaterThan,
SN_GreaterThanOrEqual,
Expand All @@ -179,7 +180,6 @@ static guint16 packedsimd_alias_methods [] = {
SN_WidenUpper,
SN_Xor,
// operators
#if 0
SN_op_Addition,
SN_op_BitwiseAnd,
SN_op_BitwiseOr,
Expand All @@ -192,7 +192,6 @@ static guint16 packedsimd_alias_methods [] = {
SN_op_Subtraction,
SN_op_UnaryNegation,
SN_op_UnsignedRightShift,
#endif
};

static MonoTypeEnum
Expand Down Expand Up @@ -365,6 +364,7 @@ emit_common_simd_operations (TransformData *td, int id, int atype, int vector_si
else if (atype == MONO_TYPE_U1) *simd_intrins = INTERP_SIMD_INTRINSIC_V128_I1_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U2) *simd_intrins = INTERP_SIMD_INTRINSIC_V128_I2_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U4) *simd_intrins = INTERP_SIMD_INTRINSIC_V128_I4_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U8) *simd_intrins = INTERP_SIMD_INTRINSIC_V128_I8_URIGHT_SHIFT;
break;
case SN_op_Subtraction:
*simd_opcode = MINT_SIMD_INTRINS_P_PP;
Expand Down Expand Up @@ -691,6 +691,7 @@ emit_sri_vector128 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
else if (atype == MONO_TYPE_U1) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I1_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U2) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I2_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U4) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I4_URIGHT_SHIFT;
else if (atype == MONO_TYPE_U8) simd_intrins = INTERP_SIMD_INTRINSIC_V128_I8_URIGHT_SHIFT;
break;
case SN_Shuffle:
simd_opcode = MINT_SIMD_INTRINS_P_PP;
Expand Down Expand Up @@ -1163,6 +1164,9 @@ emit_sri_packedsimd (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
case SN_Equals:
cmethod_name = "CompareEqual";
break;
case SN_ExtractMostSignificantBits:
cmethod_name = "Bitmask";
break;
case SN_BitwiseAnd:
case SN_op_BitwiseAnd:
cmethod_name = "And";
Expand All @@ -1177,6 +1181,8 @@ emit_sri_packedsimd (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
break;
case SN_Load:
case SN_LoadUnsafe:
if (csignature->param_count != 1)
return FALSE;
cmethod_name = "LoadVector128";
break;
case SN_Round:
Expand Down Expand Up @@ -1229,6 +1235,7 @@ emit_sri_packedsimd (TransformData *td, MonoMethod *cmethod, MonoMethodSignature
break;
case SN_ConvertToInt32:
cmethod_name = "ConvertToInt32Saturate";
break;
case SN_ShiftLeft:
case SN_ShiftRightLogical:
case SN_ShiftRightArithmetic:
Expand Down
Loading