OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <cstdint> |
| 6 |
| 7 #include "src/wasm/wasm-macro-gen.h" |
| 8 #include "src/wasm/wasm-objects.h" |
| 9 |
| 10 #include "test/cctest/cctest.h" |
| 11 #include "test/cctest/compiler/value-helper.h" |
| 12 #include "test/cctest/wasm/wasm-run-utils.h" |
| 13 |
| 14 using namespace v8::internal; |
| 15 using namespace v8::internal::wasm; |
| 16 namespace debug = v8::debug; |
| 17 |
| 18 /** |
| 19 * We test the interface from Wasm compiled code to the Wasm interpreter by |
| 20 * building a module with two functions. The external function is called from |
| 21 * this test, and will be compiled code. It takes its arguments and passes them |
| 22 * on to the internal function, which will be redirected to the interpreter. |
| 23 * If the internal function has an i64 parameter, is has to be replaced by two |
| 24 * i32 parameters on the external function. |
| 25 * The internal function just converts all its arguments to f64, sums them up |
| 26 * and returns the sum. |
| 27 */ |
| 28 namespace { |
| 29 |
| 30 template <typename T> |
| 31 class ArgPassingHelper { |
| 32 public: |
| 33 ArgPassingHelper(WasmRunnerBase& runner, WasmFunctionCompiler& inner_compiler, |
| 34 std::initializer_list<uint8_t> bytes_inner_function, |
| 35 std::initializer_list<uint8_t> bytes_outer_function, |
| 36 const T& expected_lambda) |
| 37 : isolate_(runner.main_isolate()), |
| 38 expected_lambda_(expected_lambda), |
| 39 debug_info_(WasmInstanceObject::GetOrCreateDebugInfo( |
| 40 runner.module().instance_object())) { |
| 41 std::vector<uint8_t> inner_code{bytes_inner_function}; |
| 42 inner_compiler.Build(inner_code.data(), |
| 43 inner_code.data() + inner_code.size()); |
| 44 |
| 45 std::vector<uint8_t> outer_code{bytes_outer_function}; |
| 46 runner.Build(outer_code.data(), outer_code.data() + outer_code.size()); |
| 47 |
| 48 WasmDebugInfo::RedirectToInterpreter(debug_info_, |
| 49 inner_compiler.function_index()); |
| 50 main_fun_wrapper_ = runner.module().WrapCode(runner.function_index()); |
| 51 } |
| 52 |
| 53 template <typename... Args> |
| 54 void CheckCall(Args... args) { |
| 55 Handle<Object> arg_objs[] = {isolate_->factory()->NewNumber(args)...}; |
| 56 |
| 57 uint64_t num_interpreted_before = debug_info_->NumInterpretedCalls(); |
| 58 Handle<Object> global(isolate_->context()->global_object(), isolate_); |
| 59 MaybeHandle<Object> retval = Execution::Call( |
| 60 isolate_, main_fun_wrapper_, global, arraysize(arg_objs), arg_objs); |
| 61 uint64_t num_interpreted_after = debug_info_->NumInterpretedCalls(); |
| 62 // Check that we really went through the interpreter. |
| 63 CHECK_EQ(num_interpreted_before + 1, num_interpreted_after); |
| 64 // Check the result. |
| 65 double result = retval.ToHandleChecked()->Number(); |
| 66 double expected = expected_lambda_(args...); |
| 67 CHECK_DOUBLE_EQ(expected, result); |
| 68 } |
| 69 |
| 70 private: |
| 71 Isolate* isolate_; |
| 72 T expected_lambda_; |
| 73 Handle<WasmDebugInfo> debug_info_; |
| 74 Handle<JSFunction> main_fun_wrapper_; |
| 75 }; |
| 76 |
| 77 template <typename T> |
| 78 static ArgPassingHelper<T> GetHelper( |
| 79 WasmRunnerBase& runner, WasmFunctionCompiler& inner_compiler, |
| 80 std::initializer_list<uint8_t> bytes_inner_function, |
| 81 std::initializer_list<uint8_t> bytes_outer_function, |
| 82 const T& expected_lambda) { |
| 83 return ArgPassingHelper<T>(runner, inner_compiler, bytes_inner_function, |
| 84 bytes_outer_function, expected_lambda); |
| 85 } |
| 86 |
| 87 } // namespace |
| 88 |
| 89 TEST(TestArgumentPassing_int32) { |
| 90 WasmRunner<int32_t, int32_t> runner(kExecuteCompiled); |
| 91 WasmFunctionCompiler& f2 = runner.NewFunction<int32_t, int32_t>(); |
| 92 |
| 93 auto helper = GetHelper( |
| 94 runner, f2, |
| 95 {// Return 2*<0> + 1. |
| 96 WASM_I32_ADD(WASM_I32_MUL(WASM_I32V_1(2), WASM_GET_LOCAL(0)), WASM_ONE)}, |
| 97 {// Call f2 with param <0>. |
| 98 WASM_GET_LOCAL(0), WASM_CALL_FUNCTION0(f2.function_index())}, |
| 99 [](int32_t a) { return 2 * a + 1; }); |
| 100 |
| 101 std::vector<int32_t> test_values = compiler::ValueHelper::int32_vector(); |
| 102 for (int32_t v : test_values) helper.CheckCall(v); |
| 103 } |
| 104 |
| 105 TEST(TestArgumentPassing_int64) { |
| 106 WasmRunner<double, int32_t, int32_t> runner(kExecuteCompiled); |
| 107 WasmFunctionCompiler& f2 = runner.NewFunction<double, int64_t>(); |
| 108 |
| 109 auto helper = GetHelper( |
| 110 runner, f2, |
| 111 {// Return (double)<0>. |
| 112 WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(0))}, |
| 113 {// Call f2 with param (<0> | (<1> << 32)). |
| 114 WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(0)), |
| 115 WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), |
| 116 WASM_I64V_1(32))), |
| 117 WASM_CALL_FUNCTION0(f2.function_index())}, |
| 118 [](int32_t a, int32_t b) { |
| 119 int64_t a64 = static_cast<int64_t>(a) & 0xffffffff; |
| 120 int64_t b64 = static_cast<int64_t>(b) << 32; |
| 121 return static_cast<double>(a64 | b64); |
| 122 }); |
| 123 |
| 124 std::vector<int32_t> test_values_i32 = compiler::ValueHelper::int32_vector(); |
| 125 for (int32_t v1 : test_values_i32) { |
| 126 for (int32_t v2 : test_values_i32) { |
| 127 helper.CheckCall(v1, v2); |
| 128 } |
| 129 } |
| 130 |
| 131 std::vector<int64_t> test_values_i64 = compiler::ValueHelper::int64_vector(); |
| 132 for (int64_t v : test_values_i64) { |
| 133 int32_t v1 = static_cast<int32_t>(v); |
| 134 int32_t v2 = static_cast<int32_t>(v >> 32); |
| 135 helper.CheckCall(v1, v2); |
| 136 helper.CheckCall(v2, v1); |
| 137 } |
| 138 } |
| 139 |
| 140 TEST(TestArgumentPassing_float_double) { |
| 141 WasmRunner<double, float> runner(kExecuteCompiled); |
| 142 WasmFunctionCompiler& f2 = runner.NewFunction<double, float>(); |
| 143 |
| 144 auto helper = GetHelper( |
| 145 runner, f2, |
| 146 {// Return 2*(double)<0> + 1. |
| 147 WASM_F64_ADD( |
| 148 WASM_F64_MUL(WASM_F64(2), WASM_F64_CONVERT_F32(WASM_GET_LOCAL(0))), |
| 149 WASM_F64(1))}, |
| 150 {// Call f2 with param <0>. |
| 151 WASM_GET_LOCAL(0), WASM_CALL_FUNCTION0(f2.function_index())}, |
| 152 [](float f) { return 2. * static_cast<double>(f) + 1.; }); |
| 153 |
| 154 std::vector<float> test_values = compiler::ValueHelper::float32_vector(); |
| 155 for (float f : test_values) helper.CheckCall(f); |
| 156 } |
| 157 |
| 158 TEST(TestArgumentPassing_double_double) { |
| 159 WasmRunner<double, double, double> runner(kExecuteCompiled); |
| 160 WasmFunctionCompiler& f2 = runner.NewFunction<double, double, double>(); |
| 161 |
| 162 auto helper = GetHelper(runner, f2, |
| 163 {// Return <0> + <1>. |
| 164 WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))}, |
| 165 {// Call f2 with params <0>, <1>. |
| 166 WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), |
| 167 WASM_CALL_FUNCTION0(f2.function_index())}, |
| 168 [](double a, double b) { return a + b; }); |
| 169 |
| 170 std::vector<double> test_values = compiler::ValueHelper::float64_vector(); |
| 171 for (double d1 : test_values) { |
| 172 for (double d2 : test_values) { |
| 173 helper.CheckCall(d1, d2); |
| 174 } |
| 175 } |
| 176 } |
| 177 |
| 178 TEST(TestArgumentPassing_AllTypes) { |
| 179 // The second and third argument will be combined to an i64. |
| 180 WasmRunner<double, int, int, int, float, double> runner(kExecuteCompiled); |
| 181 WasmFunctionCompiler& f2 = |
| 182 runner.NewFunction<double, int, int64_t, float, double>(); |
| 183 |
| 184 auto helper = GetHelper( |
| 185 runner, f2, |
| 186 { |
| 187 // Convert all arguments to double, add them and return the sum. |
| 188 WASM_F64_ADD( // <0+1+2> + <3> |
| 189 WASM_F64_ADD( // <0+1> + <2> |
| 190 WASM_F64_ADD( // <0> + <1> |
| 191 WASM_F64_SCONVERT_I32( |
| 192 WASM_GET_LOCAL(0)), // <0> to double |
| 193 WASM_F64_SCONVERT_I64( |
| 194 WASM_GET_LOCAL(1))), // <1> to double |
| 195 WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double |
| 196 WASM_GET_LOCAL(3)) // <3> |
| 197 }, |
| 198 {WASM_GET_LOCAL(0), // first arg |
| 199 WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), // second arg |
| 200 WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(2)), |
| 201 WASM_I64V_1(32))), |
| 202 WASM_GET_LOCAL(3), // third arg |
| 203 WASM_GET_LOCAL(4), // fourth arg |
| 204 WASM_CALL_FUNCTION0(f2.function_index())}, |
| 205 [](int32_t a, int32_t b, int32_t c, float d, double e) { |
| 206 return 0. + a + (static_cast<int64_t>(b) & 0xffffffff) + |
| 207 ((static_cast<int64_t>(c) & 0xffffffff) << 32) + d + e; |
| 208 }); |
| 209 |
| 210 auto CheckCall = [&](int32_t a, int64_t b, float c, double d) { |
| 211 int32_t b0 = static_cast<int32_t>(b); |
| 212 int32_t b1 = static_cast<int32_t>(b >> 32); |
| 213 helper.CheckCall(a, b0, b1, c, d); |
| 214 helper.CheckCall(a, b1, b0, c, d); |
| 215 }; |
| 216 |
| 217 std::vector<int32_t> test_values_i32 = compiler::ValueHelper::int32_vector(); |
| 218 std::vector<int64_t> test_values_i64 = compiler::ValueHelper::int64_vector(); |
| 219 std::vector<float> test_values_f32 = compiler::ValueHelper::float32_vector(); |
| 220 std::vector<double> test_values_f64 = compiler::ValueHelper::float64_vector(); |
| 221 size_t max_len = |
| 222 std::max(std::max(test_values_i32.size(), test_values_i64.size()), |
| 223 std::max(test_values_f32.size(), test_values_f64.size())); |
| 224 for (size_t i = 0; i < max_len; ++i) { |
| 225 int32_t i32 = test_values_i32[i % test_values_i32.size()]; |
| 226 int64_t i64 = test_values_i64[i % test_values_i64.size()]; |
| 227 float f32 = test_values_f32[i % test_values_f32.size()]; |
| 228 double f64 = test_values_f64[i % test_values_f64.size()]; |
| 229 CheckCall(i32, i64, f32, f64); |
| 230 } |
| 231 } |
OLD | NEW |