// Copyright 2015 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 #include #include #include #include "src/api/api-inl.h" #include "src/codegen/assembler-inl.h" #include "src/objects/heap-number-inl.h" #include "test/cctest/cctest.h" #include "test/cctest/compiler/value-helper.h" #include "test/cctest/wasm/wasm-run-utils.h" #include "test/common/wasm/test-signatures.h" #include "test/common/wasm/wasm-macro-gen.h" namespace v8 { namespace internal { namespace wasm { #define ADD_CODE(vec, ...) \ do { \ byte __buf[] = {__VA_ARGS__}; \ for (size_t i = 0; i < sizeof(__buf); i++) vec.push_back(__buf[i]); \ } while (false) namespace { // A helper for generating predictable but unique argument values that // are easy to debug (e.g. with misaligned stacks). class PredictableInputValues { public: int base_; explicit PredictableInputValues(int base) : base_(base) {} double arg_d(int which) { return base_ * which + ((which & 1) * 0.5); } float arg_f(int which) { return base_ * which + ((which & 1) * 0.25); } int32_t arg_i(int which) { return base_ * which + ((which & 1) * kMinInt); } int64_t arg_l(int which) { return base_ * which + ((which & 1) * (0x04030201LL << 32)); } }; ManuallyImportedJSFunction CreateJSSelector(FunctionSig* sig, int which) { const int kMaxParams = 11; static const char* formals[kMaxParams] = {"", "a", "a,b", "a,b,c", "a,b,c,d", "a,b,c,d,e", "a,b,c,d,e,f", "a,b,c,d,e,f,g", "a,b,c,d,e,f,g,h", "a,b,c,d,e,f,g,h,i", "a,b,c,d,e,f,g,h,i,j"}; CHECK_LT(which, static_cast(sig->parameter_count())); CHECK_LT(static_cast(sig->parameter_count()), kMaxParams); i::EmbeddedVector source; char param = 'a' + which; SNPrintF(source, "(function(%s) { return %c; })", formals[sig->parameter_count()], param); Handle js_function = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source.begin())))); ManuallyImportedJSFunction import = {sig, js_function}; return import; } } // namespace WASM_COMPILED_EXEC_TEST(Run_Int32Sub_jswrapped) { WasmRunner r(execution_tier); BUILD(r, WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); r.CheckCallViaJS(33, 44, 11); r.CheckCallViaJS(-8723487, -8000000, 723487); } WASM_COMPILED_EXEC_TEST(Run_Float32Div_jswrapped) { WasmRunner r(execution_tier); BUILD(r, WASM_F32_DIV(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); r.CheckCallViaJS(92, 46, 0.5); r.CheckCallViaJS(64, -16, -0.25); } WASM_COMPILED_EXEC_TEST(Run_Float64Add_jswrapped) { WasmRunner r(execution_tier); BUILD(r, WASM_F64_ADD(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))); r.CheckCallViaJS(3, 2, 1); r.CheckCallViaJS(-5.5, -5.25, -0.25); } WASM_COMPILED_EXEC_TEST(Run_I32Popcount_jswrapped) { WasmRunner r(execution_tier); BUILD(r, WASM_I32_POPCNT(WASM_LOCAL_GET(0))); r.CheckCallViaJS(2, 9); r.CheckCallViaJS(3, 11); r.CheckCallViaJS(6, 0x3F); } WASM_COMPILED_EXEC_TEST(Run_CallJS_Add_jswrapped) { TestSignatures sigs; HandleScope scope(CcTest::InitIsolateOnce()); const char* source = "(function(a) { return a + 99; })"; Handle js_function = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source)))); ManuallyImportedJSFunction import = {sigs.i_i(), js_function}; WasmRunner r(execution_tier, &import); uint32_t js_index = 0; BUILD(r, WASM_CALL_FUNCTION(js_index, WASM_LOCAL_GET(0))); r.CheckCallViaJS(101, 2); r.CheckCallViaJS(199, 100); r.CheckCallViaJS(-666666801, -666666900); } WASM_COMPILED_EXEC_TEST(Run_IndirectCallJSFunction) { Isolate* isolate = CcTest::InitIsolateOnce(); HandleScope scope(isolate); TestSignatures sigs; const char* source = "(function(a, b, c) { if(c) return a; return b; })"; Handle js_function = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source)))); ManuallyImportedJSFunction import = {sigs.i_iii(), js_function}; WasmRunner r(execution_tier, &import); const uint32_t js_index = 0; const int32_t left = -2; const int32_t right = 3; WasmFunctionCompiler& rc_fn = r.NewFunction(sigs.i_i(), "rc"); byte sig_index = r.builder().AddSignature(sigs.i_iii()); uint16_t indirect_function_table[] = {static_cast(js_index)}; r.builder().AddIndirectFunctionTable(indirect_function_table, arraysize(indirect_function_table)); BUILD(rc_fn, WASM_CALL_INDIRECT(sig_index, WASM_I32V(left), WASM_I32V(right), WASM_LOCAL_GET(0), WASM_I32V(js_index))); Handle args_left[] = {isolate->factory()->NewNumber(1)}; r.CheckCallApplyViaJS(left, rc_fn.function_index(), args_left, 1); Handle args_right[] = {isolate->factory()->NewNumber(0)}; r.CheckCallApplyViaJS(right, rc_fn.function_index(), args_right, 1); } void RunJSSelectTest(TestExecutionTier tier, int which) { const int kMaxParams = 8; PredictableInputValues inputs(0x100); ValueType type = kWasmF64; ValueType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type}; for (int num_params = which + 1; num_params < kMaxParams; num_params++) { HandleScope scope(CcTest::InitIsolateOnce()); FunctionSig sig(1, num_params, types); ManuallyImportedJSFunction import = CreateJSSelector(&sig, which); WasmRunner r(tier, &import); uint32_t js_index = 0; WasmFunctionCompiler& t = r.NewFunction(&sig); { std::vector code; for (int i = 0; i < num_params; i++) { ADD_CODE(code, WASM_F64(inputs.arg_d(i))); } ADD_CODE(code, kExprCallFunction, static_cast(js_index)); size_t end = code.size(); code.push_back(0); t.Build(&code[0], &code[end]); } double expected = inputs.arg_d(which); r.CheckCallApplyViaJS(expected, t.function_index(), nullptr, 0); } } WASM_COMPILED_EXEC_TEST(Run_JSSelect_0) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 0); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_1) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 1); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_2) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 2); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_3) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 3); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_4) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 4); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_5) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 5); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_6) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 6); } WASM_COMPILED_EXEC_TEST(Run_JSSelect_7) { CcTest::InitializeVM(); RunJSSelectTest(execution_tier, 7); } void RunWASMSelectTest(TestExecutionTier tier, int which) { PredictableInputValues inputs(0x200); Isolate* isolate = CcTest::InitIsolateOnce(); const int kMaxParams = 8; for (int num_params = which + 1; num_params < kMaxParams; num_params++) { ValueType type = kWasmF64; ValueType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type}; FunctionSig sig(1, num_params, types); WasmRunner r(tier); WasmFunctionCompiler& t = r.NewFunction(&sig); BUILD(t, WASM_LOCAL_GET(which)); Handle args[] = { isolate->factory()->NewNumber(inputs.arg_d(0)), isolate->factory()->NewNumber(inputs.arg_d(1)), isolate->factory()->NewNumber(inputs.arg_d(2)), isolate->factory()->NewNumber(inputs.arg_d(3)), isolate->factory()->NewNumber(inputs.arg_d(4)), isolate->factory()->NewNumber(inputs.arg_d(5)), isolate->factory()->NewNumber(inputs.arg_d(6)), isolate->factory()->NewNumber(inputs.arg_d(7)), }; double expected = inputs.arg_d(which); r.CheckCallApplyViaJS(expected, t.function_index(), args, kMaxParams); } } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_0) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 0); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_1) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 1); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_2) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 2); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_3) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 3); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_4) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 4); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_5) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 5); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_6) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 6); } WASM_COMPILED_EXEC_TEST(Run_WASMSelect_7) { CcTest::InitializeVM(); RunWASMSelectTest(execution_tier, 7); } void RunWASMSelectAlignTest(TestExecutionTier tier, int num_args, int num_params) { PredictableInputValues inputs(0x300); Isolate* isolate = CcTest::InitIsolateOnce(); const int kMaxParams = 10; DCHECK_LE(num_args, kMaxParams); ValueType type = kWasmF64; ValueType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type, type, type}; FunctionSig sig(1, num_params, types); for (int which = 0; which < num_params; which++) { WasmRunner r(tier); WasmFunctionCompiler& t = r.NewFunction(&sig); BUILD(t, WASM_LOCAL_GET(which)); Handle args[] = {isolate->factory()->NewNumber(inputs.arg_d(0)), isolate->factory()->NewNumber(inputs.arg_d(1)), isolate->factory()->NewNumber(inputs.arg_d(2)), isolate->factory()->NewNumber(inputs.arg_d(3)), isolate->factory()->NewNumber(inputs.arg_d(4)), isolate->factory()->NewNumber(inputs.arg_d(5)), isolate->factory()->NewNumber(inputs.arg_d(6)), isolate->factory()->NewNumber(inputs.arg_d(7)), isolate->factory()->NewNumber(inputs.arg_d(8)), isolate->factory()->NewNumber(inputs.arg_d(9))}; double nan = std::numeric_limits::quiet_NaN(); double expected = which < num_args ? inputs.arg_d(which) : nan; r.CheckCallApplyViaJS(expected, t.function_index(), args, num_args); } } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_0) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 0, 1); RunWASMSelectAlignTest(execution_tier, 0, 2); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_1) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 1, 2); RunWASMSelectAlignTest(execution_tier, 1, 3); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_2) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 2, 3); RunWASMSelectAlignTest(execution_tier, 2, 4); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_3) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 3, 3); RunWASMSelectAlignTest(execution_tier, 3, 4); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_4) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 4, 3); RunWASMSelectAlignTest(execution_tier, 4, 4); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_7) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 7, 5); RunWASMSelectAlignTest(execution_tier, 7, 6); RunWASMSelectAlignTest(execution_tier, 7, 7); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_8) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 8, 5); RunWASMSelectAlignTest(execution_tier, 8, 6); RunWASMSelectAlignTest(execution_tier, 8, 7); RunWASMSelectAlignTest(execution_tier, 8, 8); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_9) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 9, 6); RunWASMSelectAlignTest(execution_tier, 9, 7); RunWASMSelectAlignTest(execution_tier, 9, 8); RunWASMSelectAlignTest(execution_tier, 9, 9); } WASM_COMPILED_EXEC_TEST(Run_WASMSelectAlign_10) { CcTest::InitializeVM(); RunWASMSelectAlignTest(execution_tier, 10, 7); RunWASMSelectAlignTest(execution_tier, 10, 8); RunWASMSelectAlignTest(execution_tier, 10, 9); RunWASMSelectAlignTest(execution_tier, 10, 10); } void RunJSSelectAlignTest(TestExecutionTier tier, int num_args, int num_params) { PredictableInputValues inputs(0x400); Isolate* isolate = CcTest::InitIsolateOnce(); Factory* factory = isolate->factory(); const int kMaxParams = 10; CHECK_LE(num_args, kMaxParams); CHECK_LE(num_params, kMaxParams); ValueType type = kWasmF64; ValueType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type, type, type}; FunctionSig sig(1, num_params, types); i::AccountingAllocator allocator; Zone zone(&allocator, ZONE_NAME); // Build the calling code. std::vector code; for (int i = 0; i < num_params; i++) { ADD_CODE(code, WASM_LOCAL_GET(i)); } uint8_t imported_js_index = 0; ADD_CODE(code, kExprCallFunction, imported_js_index); size_t end = code.size(); code.push_back(0); // Call different select JS functions. for (int which = 0; which < num_params; which++) { HandleScope scope(isolate); ManuallyImportedJSFunction import = CreateJSSelector(&sig, which); WasmRunner r(tier, &import); WasmFunctionCompiler& t = r.NewFunction(&sig); t.Build(&code[0], &code[end]); Handle args[] = { factory->NewNumber(inputs.arg_d(0)), factory->NewNumber(inputs.arg_d(1)), factory->NewNumber(inputs.arg_d(2)), factory->NewNumber(inputs.arg_d(3)), factory->NewNumber(inputs.arg_d(4)), factory->NewNumber(inputs.arg_d(5)), factory->NewNumber(inputs.arg_d(6)), factory->NewNumber(inputs.arg_d(7)), factory->NewNumber(inputs.arg_d(8)), factory->NewNumber(inputs.arg_d(9)), }; double nan = std::numeric_limits::quiet_NaN(); double expected = which < num_args ? inputs.arg_d(which) : nan; r.CheckCallApplyViaJS(expected, t.function_index(), args, num_args); } } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_0) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 0, 1); RunJSSelectAlignTest(execution_tier, 0, 2); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_1) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 1, 2); RunJSSelectAlignTest(execution_tier, 1, 3); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_2) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 2, 3); RunJSSelectAlignTest(execution_tier, 2, 4); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_3) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 3, 3); RunJSSelectAlignTest(execution_tier, 3, 4); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_4) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 4, 3); RunJSSelectAlignTest(execution_tier, 4, 4); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_7) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 7, 3); RunJSSelectAlignTest(execution_tier, 7, 4); RunJSSelectAlignTest(execution_tier, 7, 4); RunJSSelectAlignTest(execution_tier, 7, 4); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_8) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 8, 5); RunJSSelectAlignTest(execution_tier, 8, 6); RunJSSelectAlignTest(execution_tier, 8, 7); RunJSSelectAlignTest(execution_tier, 8, 8); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_9) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 9, 6); RunJSSelectAlignTest(execution_tier, 9, 7); RunJSSelectAlignTest(execution_tier, 9, 8); RunJSSelectAlignTest(execution_tier, 9, 9); } WASM_COMPILED_EXEC_TEST(Run_JSSelectAlign_10) { CcTest::InitializeVM(); RunJSSelectAlignTest(execution_tier, 10, 7); RunJSSelectAlignTest(execution_tier, 10, 8); RunJSSelectAlignTest(execution_tier, 10, 9); RunJSSelectAlignTest(execution_tier, 10, 10); } // Set up a test with an import, so we can return call it. // Create a javascript function that returns left or right arguments // depending on the value of the third argument // function (a,b,c){ if(c)return a; return b; } void RunPickerTest(TestExecutionTier tier, bool indirect) { EXPERIMENTAL_FLAG_SCOPE(return_call); Isolate* isolate = CcTest::InitIsolateOnce(); HandleScope scope(isolate); TestSignatures sigs; const char* source = "(function(a,b,c) { if(c)return a; return b; })"; Handle js_function = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source)))); ManuallyImportedJSFunction import = {sigs.i_iii(), js_function}; WasmRunner r(tier, &import); const uint32_t js_index = 0; const int32_t left = -2; const int32_t right = 3; WasmFunctionCompiler& rc_fn = r.NewFunction(sigs.i_i(), "rc"); if (indirect) { byte sig_index = r.builder().AddSignature(sigs.i_iii()); uint16_t indirect_function_table[] = {static_cast(js_index)}; r.builder().AddIndirectFunctionTable(indirect_function_table, arraysize(indirect_function_table)); BUILD(rc_fn, WASM_RETURN_CALL_INDIRECT(sig_index, WASM_I32V(left), WASM_I32V(right), WASM_LOCAL_GET(0), WASM_I32V(js_index))); } else { BUILD(rc_fn, WASM_RETURN_CALL_FUNCTION(js_index, WASM_I32V(left), WASM_I32V(right), WASM_LOCAL_GET(0))); } Handle args_left[] = {isolate->factory()->NewNumber(1)}; r.CheckCallApplyViaJS(left, rc_fn.function_index(), args_left, 1); Handle args_right[] = {isolate->factory()->NewNumber(0)}; r.CheckCallApplyViaJS(right, rc_fn.function_index(), args_right, 1); } WASM_COMPILED_EXEC_TEST(Run_ReturnCallImportedFunction) { RunPickerTest(execution_tier, false); } WASM_COMPILED_EXEC_TEST(Run_ReturnCallIndirectImportedFunction) { RunPickerTest(execution_tier, true); } #undef ADD_CODE } // namespace wasm } // namespace internal } // namespace v8