// 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/wasm/wasm-macro-gen.h" #include "test/cctest/cctest.h" #include "test/cctest/compiler/value-helper.h" #include "test/cctest/wasm/test-signatures.h" #include "test/cctest/wasm/wasm-run-utils.h" using namespace v8::base; using namespace v8::internal; using namespace v8::internal::compiler; using namespace v8::internal::wasm; #define BUILD(r, ...) \ do { \ byte code[] = {__VA_ARGS__}; \ r.Build(code, code + arraysize(code)); \ } while (false) #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)); } }; uint32_t AddJSSelector(TestingModule* module, 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); return module->AddJsFunction(sig, source.start()); } void EXPECT_CALL(double expected, Handle jsfunc, Handle* buffer, int count) { Isolate* isolate = jsfunc->GetIsolate(); Handle global(isolate->context()->global_object(), isolate); MaybeHandle retval = Execution::Call(isolate, jsfunc, global, count, buffer); CHECK(!retval.is_null()); Handle result = retval.ToHandleChecked(); if (result->IsSmi()) { CHECK_EQ(expected, Smi::cast(*result)->value()); } else { CHECK(result->IsHeapNumber()); CheckFloatEq(expected, HeapNumber::cast(*result)->value()); } } void EXPECT_CALL(double expected, Handle jsfunc, double a, double b) { Isolate* isolate = jsfunc->GetIsolate(); Handle buffer[] = {isolate->factory()->NewNumber(a), isolate->factory()->NewNumber(b)}; EXPECT_CALL(expected, jsfunc, buffer, 2); } } // namespace TEST(Run_Int32Sub_jswrapped) { TestSignatures sigs; TestingModule module; WasmFunctionCompiler t(sigs.i_ii(), &module); BUILD(t, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); EXPECT_CALL(33, jsfunc, 44, 11); EXPECT_CALL(-8723487, jsfunc, -8000000, 723487); } TEST(Run_Float32Div_jswrapped) { TestSignatures sigs; TestingModule module; WasmFunctionCompiler t(sigs.f_ff(), &module); BUILD(t, WASM_F32_DIV(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); EXPECT_CALL(92, jsfunc, 46, 0.5); EXPECT_CALL(64, jsfunc, -16, -0.25); } TEST(Run_Float64Add_jswrapped) { TestSignatures sigs; TestingModule module; WasmFunctionCompiler t(sigs.d_dd(), &module); BUILD(t, WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); EXPECT_CALL(3, jsfunc, 2, 1); EXPECT_CALL(-5.5, jsfunc, -5.25, -0.25); } TEST(Run_I32Popcount_jswrapped) { TestSignatures sigs; TestingModule module; WasmFunctionCompiler t(sigs.i_i(), &module); BUILD(t, WASM_I32_POPCNT(WASM_GET_LOCAL(0))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); EXPECT_CALL(2, jsfunc, 9, 0); EXPECT_CALL(3, jsfunc, 11, 0); EXPECT_CALL(6, jsfunc, 0x3F, 0); } TEST(Run_CallJS_Add_jswrapped) { TestSignatures sigs; TestingModule module; WasmFunctionCompiler t(sigs.i_i(), &module); uint32_t js_index = module.AddJsFunction(sigs.i_i(), "(function(a) { return a + 99; })"); BUILD(t, WASM_CALL_FUNCTION(js_index, WASM_GET_LOCAL(0))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); EXPECT_CALL(101, jsfunc, 2, -8); EXPECT_CALL(199, jsfunc, 100, -1); EXPECT_CALL(-666666801, jsfunc, -666666900, -1); } void RunJSSelectTest(int which) { const int kMaxParams = 8; PredictableInputValues inputs(0x100); LocalType type = kAstF64; LocalType 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); TestingModule module; uint32_t js_index = AddJSSelector(&module, &sig, which); WasmFunctionCompiler t(&sig, &module); { std::vector code; ADD_CODE(code, kExprCallFunction, static_cast(js_index)); for (int i = 0; i < num_params; i++) { ADD_CODE(code, WASM_F64(inputs.arg_d(i))); } size_t end = code.size(); code.push_back(0); t.Build(&code[0], &code[end]); } Handle jsfunc = module.WrapCode(t.CompileAndAdd()); double expected = inputs.arg_d(which); EXPECT_CALL(expected, jsfunc, 0.0, 0.0); } } TEST(Run_JSSelect_0) { RunJSSelectTest(0); } TEST(Run_JSSelect_1) { RunJSSelectTest(1); } TEST(Run_JSSelect_2) { RunJSSelectTest(2); } TEST(Run_JSSelect_3) { RunJSSelectTest(3); } TEST(Run_JSSelect_4) { RunJSSelectTest(4); } TEST(Run_JSSelect_5) { RunJSSelectTest(5); } TEST(Run_JSSelect_6) { RunJSSelectTest(6); } TEST(Run_JSSelect_7) { RunJSSelectTest(7); } void RunWASMSelectTest(int which) { PredictableInputValues inputs(0x200); Isolate* isolate = CcTest::InitIsolateOnce(); const int kMaxParams = 8; for (int num_params = which + 1; num_params < kMaxParams; num_params++) { LocalType type = kAstF64; LocalType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type}; FunctionSig sig(1, num_params, types); TestingModule module; WasmFunctionCompiler t(&sig, &module); BUILD(t, WASM_GET_LOCAL(which)); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); 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); EXPECT_CALL(expected, jsfunc, args, kMaxParams); } } TEST(Run_WASMSelect_0) { RunWASMSelectTest(0); } TEST(Run_WASMSelect_1) { RunWASMSelectTest(1); } TEST(Run_WASMSelect_2) { RunWASMSelectTest(2); } TEST(Run_WASMSelect_3) { RunWASMSelectTest(3); } TEST(Run_WASMSelect_4) { RunWASMSelectTest(4); } TEST(Run_WASMSelect_5) { RunWASMSelectTest(5); } TEST(Run_WASMSelect_6) { RunWASMSelectTest(6); } TEST(Run_WASMSelect_7) { RunWASMSelectTest(7); } void RunWASMSelectAlignTest(int num_args, int num_params) { PredictableInputValues inputs(0x300); Isolate* isolate = CcTest::InitIsolateOnce(); const int kMaxParams = 10; DCHECK_LE(num_args, kMaxParams); LocalType type = kAstF64; LocalType 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++) { TestingModule module; WasmFunctionCompiler t(&sig, &module); BUILD(t, WASM_GET_LOCAL(which)); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); 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; EXPECT_CALL(expected, jsfunc, args, num_args); } } TEST(Run_WASMSelectAlign_0) { RunWASMSelectAlignTest(0, 1); RunWASMSelectAlignTest(0, 2); } TEST(Run_WASMSelectAlign_1) { RunWASMSelectAlignTest(1, 2); RunWASMSelectAlignTest(1, 3); } TEST(Run_WASMSelectAlign_2) { RunWASMSelectAlignTest(2, 3); RunWASMSelectAlignTest(2, 4); } TEST(Run_WASMSelectAlign_3) { RunWASMSelectAlignTest(3, 3); RunWASMSelectAlignTest(3, 4); } TEST(Run_WASMSelectAlign_4) { RunWASMSelectAlignTest(4, 3); RunWASMSelectAlignTest(4, 4); } TEST(Run_WASMSelectAlign_7) { RunWASMSelectAlignTest(7, 5); RunWASMSelectAlignTest(7, 6); RunWASMSelectAlignTest(7, 7); } TEST(Run_WASMSelectAlign_8) { RunWASMSelectAlignTest(8, 5); RunWASMSelectAlignTest(8, 6); RunWASMSelectAlignTest(8, 7); RunWASMSelectAlignTest(8, 8); } TEST(Run_WASMSelectAlign_9) { RunWASMSelectAlignTest(9, 6); RunWASMSelectAlignTest(9, 7); RunWASMSelectAlignTest(9, 8); RunWASMSelectAlignTest(9, 9); } TEST(Run_WASMSelectAlign_10) { RunWASMSelectAlignTest(10, 7); RunWASMSelectAlignTest(10, 8); RunWASMSelectAlignTest(10, 9); RunWASMSelectAlignTest(10, 10); } void RunJSSelectAlignTest(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); LocalType type = kAstF64; LocalType types[kMaxParams + 1] = {type, type, type, type, type, type, type, type, type, type, type}; FunctionSig sig(1, num_params, types); // Build the calling code. std::vector code; ADD_CODE(code, kExprCallFunction, 0); for (int i = 0; i < num_params; i++) { ADD_CODE(code, WASM_GET_LOCAL(i)); } 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); TestingModule module; uint32_t js_index = AddJSSelector(&module, &sig, which); CHECK_EQ(0, js_index); WasmFunctionCompiler t(&sig, &module); t.Build(&code[0], &code[end]); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); 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; EXPECT_CALL(expected, jsfunc, args, num_args); } } TEST(Run_JSSelectAlign_0) { RunJSSelectAlignTest(0, 1); RunJSSelectAlignTest(0, 2); } TEST(Run_JSSelectAlign_1) { RunJSSelectAlignTest(1, 2); RunJSSelectAlignTest(1, 3); } TEST(Run_JSSelectAlign_2) { RunJSSelectAlignTest(2, 3); RunJSSelectAlignTest(2, 4); } TEST(Run_JSSelectAlign_3) { RunJSSelectAlignTest(3, 3); RunJSSelectAlignTest(3, 4); } TEST(Run_JSSelectAlign_4) { RunJSSelectAlignTest(4, 3); RunJSSelectAlignTest(4, 4); } TEST(Run_JSSelectAlign_7) { RunJSSelectAlignTest(7, 3); RunJSSelectAlignTest(7, 4); RunJSSelectAlignTest(7, 4); RunJSSelectAlignTest(7, 4); } TEST(Run_JSSelectAlign_8) { RunJSSelectAlignTest(8, 5); RunJSSelectAlignTest(8, 6); RunJSSelectAlignTest(8, 7); RunJSSelectAlignTest(8, 8); } TEST(Run_JSSelectAlign_9) { RunJSSelectAlignTest(9, 6); RunJSSelectAlignTest(9, 7); RunJSSelectAlignTest(9, 8); RunJSSelectAlignTest(9, 9); } TEST(Run_JSSelectAlign_10) { RunJSSelectAlignTest(10, 7); RunJSSelectAlignTest(10, 8); RunJSSelectAlignTest(10, 9); RunJSSelectAlignTest(10, 10); }