// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/compilation-environment.h" #include "src/wasm/wasm-tier.h" #include "test/cctest/cctest.h" #include "test/cctest/wasm/wasm-run-utils.h" #include "test/common/wasm/flag-utils.h" #include "test/common/wasm/test-signatures.h" #include "test/common/wasm/wasm-macro-gen.h" namespace v8 { namespace internal { namespace wasm { namespace test_run_wasm_simd { #define WASM_SIMD_TEST(name) \ void RunWasm_##name##_Impl(LowerSimd lower_simd, \ TestExecutionTier execution_tier); \ TEST(RunWasm_##name##_simd_lowered) { \ EXPERIMENTAL_FLAG_SCOPE(simd); \ RunWasm_##name##_Impl(kLowerSimd, TestExecutionTier::kTurbofan); \ } \ void RunWasm_##name##_Impl(LowerSimd lower_simd, \ TestExecutionTier execution_tier) WASM_SIMD_TEST(I8x16ToF32x4) { WasmRunner r(execution_tier, lower_simd); float* g = r.builder().AddGlobal(kWasmS128); byte param1 = 0; BUILD(r, WASM_GLOBAL_SET( 0, WASM_SIMD_UNOP(kExprF32x4Sqrt, WASM_SIMD_I8x16_SPLAT(WASM_LOCAL_GET(param1)))), WASM_ONE); // Arbitrary pattern that doesn't end up creating a NaN. r.Call(0x5b); float f = bit_cast(0x5b5b5b5b); float actual = ReadLittleEndianValue(&g[0]); float expected = std::sqrt(f); CHECK_EQ(expected, actual); } WASM_SIMD_TEST(F64x2_Call_Return) { // Check that calling a function with i16x8 arguments, and returns i16x8, is // correctly lowered. The signature of the functions are always lowered to 4 // Word32, so each i16x8 needs to be correctly converted. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); WasmFunctionCompiler& fn = r.NewFunction(sigs.s_ss()); BUILD(fn, WASM_SIMD_BINOP(kExprF64x2Min, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); byte c1[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; byte c2[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f}; BUILD(r, WASM_SIMD_F64x2_EXTRACT_LANE( 0, WASM_CALL_FUNCTION(fn.function_index(), WASM_SIMD_CONSTANT(c1), WASM_SIMD_CONSTANT(c2)))); CHECK_EQ(0, r.Call(double{0}, bit_cast(0x7fefffffffffffff))); } WASM_SIMD_TEST(F32x4_Call_Return) { // Check that functions that return F32x4 are correctly lowered into 4 int32 // nodes. The signature of such functions are always lowered to 4 Word32, and // if the last operation before the return was a f32x4, it will need to be // bitcasted from float to int. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); // A simple function that just calls f32x4.neg on the param. WasmFunctionCompiler& fn = r.NewFunction(sigs.s_s()); BUILD(fn, WASM_SIMD_UNOP(kExprF32x4Neg, WASM_LOCAL_GET(0))); // TODO(v8:10507) // Use i32x4 splat since scalar lowering has a problem with f32x4 as a param // to a function call, the lowering is not correct yet. BUILD(r, WASM_SIMD_F32x4_EXTRACT_LANE( 0, WASM_CALL_FUNCTION(fn.function_index(), WASM_SIMD_F32x4_SPLAT(WASM_LOCAL_GET(0))))); CHECK_EQ(-1.0, r.Call(1)); } WASM_SIMD_TEST(I8x16_Call_Return) { // Check that calling a function with i8x16 arguments, and returns i8x16, is // correctly lowered. The signature of the functions are always lowered to 4 // Word32, so each i8x16 needs to be correctly converted. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); WasmFunctionCompiler& fn = r.NewFunction(sigs.s_ss()); BUILD(fn, WASM_SIMD_BINOP(kExprI8x16Add, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); BUILD(r, WASM_SIMD_I8x16_EXTRACT_LANE( 0, WASM_CALL_FUNCTION(fn.function_index(), WASM_SIMD_I8x16_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_I8x16_SPLAT(WASM_LOCAL_GET(0))))); CHECK_EQ(2, r.Call(1)); } WASM_SIMD_TEST(I16x8_Call_Return) { // Check that calling a function with i16x8 arguments, and returns i16x8, is // correctly lowered. The signature of the functions are always lowered to 4 // Word32, so each i16x8 needs to be correctly converted. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); WasmFunctionCompiler& fn = r.NewFunction(sigs.s_ss()); BUILD(fn, WASM_SIMD_BINOP(kExprI16x8Add, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); BUILD(r, WASM_SIMD_I16x8_EXTRACT_LANE( 0, WASM_CALL_FUNCTION(fn.function_index(), WASM_SIMD_I16x8_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_I16x8_SPLAT(WASM_LOCAL_GET(0))))); CHECK_EQ(2, r.Call(1)); } WASM_SIMD_TEST(I64x2_Call_Return) { // Check that calling a function with i64x2 arguments, and returns i64x2, is // correctly lowered. The signature of the functions are always lowered to 4 // Word32, so each i64x2 needs to be correctly converted. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); WasmFunctionCompiler& fn = r.NewFunction(sigs.s_ss()); BUILD(fn, WASM_SIMD_BINOP(kExprI64x2Add, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); BUILD(r, WASM_SIMD_I64x2_EXTRACT_LANE( 0, WASM_CALL_FUNCTION(fn.function_index(), WASM_SIMD_I64x2_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_I64x2_SPLAT(WASM_LOCAL_GET(0))))); CHECK_EQ(2, r.Call(1)); } WASM_SIMD_TEST(I8x16Eq_ToTest_S128Const) { // Test implementation of S128Const in scalar lowering, this test case was // causing a crash. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); byte c1[16] = {0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40}; byte c2[16] = {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02}; byte c3[16] = {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; BUILD(r, WASM_SIMD_BINOP(kExprI8x16Eq, WASM_SIMD_CONSTANT(c1), WASM_SIMD_CONSTANT(c2)), WASM_SIMD_CONSTANT(c3), WASM_SIMD_OP(kExprI8x16Eq), WASM_SIMD_OP(kExprI8x16ExtractLaneS), TO_BYTE(4)); CHECK_EQ(0xffffffff, r.Call()); } WASM_SIMD_TEST(F32x4_S128Const) { // Test that S128Const lowering is done correctly when it is used as an input // into a f32x4 operation. This was triggering a CHECK failure in the // register-allocator-verifier. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); // f32x4(1.0, 2.0, 3.0, 4.0) byte c1[16] = {0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x40}; // f32x4(5.0, 6.0, 7.0, 8.0) byte c2[16] = {0x00, 0x00, 0xa0, 0x40, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0x40, 0x00, 0x00, 0x00, 0x41}; BUILD(r, WASM_SIMD_BINOP(kExprF32x4Min, WASM_SIMD_CONSTANT(c1), WASM_SIMD_CONSTANT(c2)), WASM_SIMD_OP(kExprF32x4ExtractLane), TO_BYTE(0)); CHECK_EQ(1.0, r.Call()); } WASM_SIMD_TEST(AllTrue_DifferentShapes) { // Test all_true lowring with splats of different shapes. { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_I32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV8x16AllTrue)); CHECK_EQ(0, r.Call(0x00FF00FF)); } { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_I32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV16x8AllTrue)); CHECK_EQ(0, r.Call(0x000000FF)); } // Check float input to all_true. { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_F32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV16x8AllTrue)); CHECK_EQ(1, r.Call(0x000F000F)); } } WASM_SIMD_TEST(AnyTrue_DifferentShapes) { // Test any_true lowring with splats of different shapes. { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_I32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV8x16AnyTrue)); CHECK_EQ(0, r.Call(0x00000000)); } { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_I32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV16x8AnyTrue)); CHECK_EQ(1, r.Call(0x000000FF)); } // Check float input to any_true. { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_F32x4_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprV8x16AnyTrue)); CHECK_EQ(0, r.Call(0x00000000)); } } WASM_SIMD_TEST(V128_I64_PARAMS) { // This test exercises interaction between simd and int64 lowering. The // parameter indices were not correctly lowered because simd lowered a v128 in // the function signature into 4 word32, and int64 was still treating it as 1 // parameter. WasmRunner r(execution_tier, lower_simd); FunctionSig::Builder builder(r.zone(), 1, 2); builder.AddParam(kWasmS128); builder.AddParam(kWasmI64); builder.AddReturn(kWasmS128); FunctionSig* sig = builder.Build(); WasmFunctionCompiler& fn = r.NewFunction(sig); // Build a function that has both V128 and I64 arguments. BUILD(fn, WASM_SIMD_I64x2_REPLACE_LANE(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); BUILD(r, WASM_SIMD_I64x2_EXTRACT_LANE( 0, WASM_SIMD_I64x2_SPLAT(WASM_LOCAL_GET(0)))); CHECK_EQ(0, r.Call(0)); } WASM_SIMD_TEST(I8x16WidenS_I16x8NarrowU) { // Test any_true lowring with splats of different shapes. { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_SIMD_I16x8_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_I16x8_SPLAT(WASM_LOCAL_GET(0)), WASM_SIMD_OP(kExprI8x16UConvertI16x8), WASM_SIMD_OP(kExprI16x8SConvertI8x16Low), WASM_SIMD_OP(kExprI32x4ExtractLane), TO_BYTE(0)); CHECK_EQ(bit_cast(0xffffffff), r.Call(0x7fff)); } } WASM_SIMD_TEST(S128SelectWithF32x4) { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_LOCAL_GET(0), WASM_SIMD_OP(kExprI32x4Splat), WASM_LOCAL_GET(1), WASM_SIMD_OP(kExprF32x4Splat), WASM_LOCAL_GET(2), WASM_SIMD_OP(kExprI32x4Splat), WASM_SIMD_OP(kExprS128Select), WASM_SIMD_OP(kExprF32x4ExtractLane), 0); // Selection mask is all 0, so always select 2.0. CHECK_EQ(2.0, r.Call(1, 2.0, 0)); } WASM_SIMD_TEST(S128AndNotWithF32x4) { WasmRunner r(execution_tier, lower_simd); BUILD(r, WASM_LOCAL_GET(0), WASM_SIMD_OP(kExprI32x4Splat), WASM_LOCAL_GET(1), WASM_SIMD_OP(kExprF32x4Splat), WASM_SIMD_OP(kExprS128AndNot), WASM_SIMD_OP(kExprF32x4ExtractLane), 0); // 0x00700000 & !0x40800000 = 0x00700000 CHECK_EQ(bit_cast(0x700000), r.Call(0x00700000, bit_cast(0x40800000))); } WASM_SIMD_TEST(FunctionCallWithExtractLaneOutputAsArgument) { // This uses the result of an extract lane as an argument to a function call // to exercise lowering for kCall and make sure the the extract lane is // correctly replaced with a scalar. TestSignatures sigs; WasmRunner r(execution_tier, lower_simd); WasmFunctionCompiler& fn = r.NewFunction(sigs.f_f()); BUILD(fn, WASM_LOCAL_GET(0), WASM_LOCAL_GET(0), kExprF32Add); BUILD(r, WASM_LOCAL_GET(0), WASM_SIMD_OP(kExprI32x4Splat), WASM_SIMD_OP(kExprF32x4ExtractLane), 0, kExprCallFunction, fn.function_index(), WASM_SIMD_OP(kExprF32x4Splat), WASM_LOCAL_GET(0), WASM_SIMD_OP(kExprI32x4Splat), WASM_SIMD_OP(kExprI32x4Add), WASM_SIMD_OP(kExprI32x4ExtractLane), 0); CHECK_EQ(15, r.Call(5)); } } // namespace test_run_wasm_simd } // namespace wasm } // namespace internal } // namespace v8