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