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