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 C to compiled wasm code by generating a wasm
22 : * function, creating a corresponding signature, compiling the c wasm entry for
23 : * that signature, and then calling that entry using different test values.
24 : * The result is compared against the expected result, computed from a lambda
25 : * passed to the CWasmEntryArgTester.
26 : */
27 : namespace {
28 :
29 : template <typename ReturnType, typename... Args>
30 48 : class CWasmEntryArgTester {
31 : public:
32 24 : CWasmEntryArgTester(std::initializer_list<uint8_t> wasm_function_bytes,
33 : std::function<ReturnType(Args...)> expected_fn)
34 : : runner_(ExecutionTier::kTurbofan),
35 : isolate_(runner_.main_isolate()),
36 : expected_fn_(expected_fn),
37 48 : sig_(runner_.template CreateSig<ReturnType, Args...>()) {
38 : std::vector<uint8_t> code{wasm_function_bytes};
39 24 : runner_.Build(code.data(), code.data() + code.size());
40 24 : wasm_code_ = runner_.builder().GetFunctionCode(0);
41 24 : Handle<WasmInstanceObject> instance(runner_.builder().instance_object());
42 : Handle<WasmDebugInfo> debug_info =
43 24 : WasmInstanceObject::GetOrCreateDebugInfo(instance);
44 24 : c_wasm_entry_fn_ = WasmDebugInfo::GetCWasmEntry(debug_info, sig_);
45 24 : }
46 :
47 : template <typename... Rest>
48 : void WriteToBuffer(Address buf, Rest... rest) {
49 : static_assert(sizeof...(rest) == 0, "this is the base case");
50 : }
51 :
52 : template <typename First, typename... Rest>
53 10984 : void WriteToBuffer(Address buf, First first, Rest... rest) {
54 : WriteUnalignedValue(buf, first);
55 10984 : WriteToBuffer(buf + sizeof(first), rest...);
56 10984 : }
57 :
58 11404 : void CheckCall(Args... args) {
59 11404 : std::vector<uint8_t> arg_buffer(sizeof...(args) * 8);
60 10064 : WriteToBuffer(reinterpret_cast<Address>(arg_buffer.data()), args...);
61 :
62 11404 : Handle<Object> receiver = isolate_->factory()->undefined_value();
63 : Handle<Object> buffer_obj(
64 11404 : Object(reinterpret_cast<Address>(arg_buffer.data())), isolate_);
65 11404 : CHECK(!buffer_obj->IsHeapObject());
66 11404 : Handle<Object> code_entry_obj(Object(wasm_code_->instruction_start()),
67 11404 : isolate_);
68 11404 : CHECK(!code_entry_obj->IsHeapObject());
69 : Handle<Object> call_args[]{code_entry_obj,
70 22808 : runner_.builder().instance_object(), buffer_obj};
71 : static_assert(
72 : arraysize(call_args) == compiler::CWasmEntryParameters::kNumParameters,
73 : "adapt this test");
74 11404 : wasm_code_->native_module()->SetExecutable(true);
75 22808 : MaybeHandle<Object> return_obj = Execution::Call(
76 11404 : isolate_, c_wasm_entry_fn_, receiver, arraysize(call_args), call_args);
77 11404 : CHECK(!return_obj.is_null());
78 11404 : CHECK(return_obj.ToHandleChecked()->IsSmi());
79 11404 : CHECK_EQ(0, Smi::ToInt(*return_obj.ToHandleChecked()));
80 :
81 : // Check the result.
82 : ReturnType result = ReadUnalignedValue<ReturnType>(
83 : reinterpret_cast<Address>(arg_buffer.data()));
84 : ReturnType expected = expected_fn_(args...);
85 : if (std::is_floating_point<ReturnType>::value) {
86 10848 : CHECK_DOUBLE_EQ(expected, result);
87 : } else {
88 556 : CHECK_EQ(expected, result);
89 : }
90 11404 : }
91 :
92 : private:
93 : WasmRunner<ReturnType, Args...> runner_;
94 : Isolate* isolate_;
95 : std::function<ReturnType(Args...)> expected_fn_;
96 : FunctionSig* sig_;
97 : Handle<JSFunction> c_wasm_entry_fn_;
98 : WasmCode* wasm_code_;
99 : };
100 :
101 : } // namespace
102 :
103 : // Pass int32_t, return int32_t.
104 26643 : TEST(TestCWasmEntryArgPassing_int32) {
105 : CWasmEntryArgTester<int32_t, int32_t> tester(
106 : {// Return 2*<0> + 1.
107 : WASM_I32_ADD(WASM_I32_MUL(WASM_I32V_1(2), WASM_GET_LOCAL(0)), WASM_ONE)},
108 : [](int32_t a) {
109 : return base::AddWithWraparound(base::MulWithWraparound(2, a), 1);
110 12 : });
111 :
112 236 : FOR_INT32_INPUTS(v) { tester.CheckCall(v); }
113 4 : }
114 :
115 : // Pass int64_t, return double.
116 26643 : TEST(TestCWasmEntryArgPassing_double_int64) {
117 : CWasmEntryArgTester<double, int64_t> tester(
118 : {// Return (double)<0>.
119 : WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(0))},
120 336 : [](int64_t a) { return static_cast<double>(a); });
121 :
122 328 : FOR_INT64_INPUTS(v) { tester.CheckCall(v); }
123 4 : }
124 :
125 : // Pass double, return int64_t.
126 26643 : TEST(TestCWasmEntryArgPassing_int64_double) {
127 : CWasmEntryArgTester<int64_t, double> tester(
128 : {// Return (int64_t)<0>.
129 : WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0))},
130 336 : [](double d) { return static_cast<int64_t>(d); });
131 :
132 328 : FOR_INT64_INPUTS(i) { tester.CheckCall(i); }
133 4 : }
134 :
135 : // Pass float, return double.
136 26643 : TEST(TestCWasmEntryArgPassing_float_double) {
137 : CWasmEntryArgTester<double, float> tester(
138 : {// Return 2*(double)<0> + 1.
139 : WASM_F64_ADD(
140 : WASM_F64_MUL(WASM_F64(2), WASM_F64_CONVERT_F32(WASM_GET_LOCAL(0))),
141 : WASM_F64(1))},
142 472 : [](float f) { return 2. * static_cast<double>(f) + 1.; });
143 :
144 464 : FOR_FLOAT32_INPUTS(f) { tester.CheckCall(f); }
145 4 : }
146 :
147 : // Pass two doubles, return double.
148 26643 : TEST(TestCWasmEntryArgPassing_double_double) {
149 : CWasmEntryArgTester<double, double, double> tester(
150 : {// Return <0> + <1>.
151 : WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))},
152 9616 : [](double a, double b) { return a + b; });
153 :
154 396 : FOR_FLOAT64_INPUTS(d1) {
155 9800 : FOR_FLOAT64_INPUTS(d2) { tester.CheckCall(d1, d2); }
156 : }
157 4 : }
158 :
159 : // Pass int32_t, int64_t, float and double, return double.
160 26643 : TEST(TestCWasmEntryArgPassing_AllTypes) {
161 : CWasmEntryArgTester<double, int32_t, int64_t, float, double> tester(
162 : {
163 : // Convert all arguments to double, add them and return the sum.
164 : WASM_F64_ADD( // <0+1+2> + <3>
165 : WASM_F64_ADD( // <0+1> + <2>
166 : WASM_F64_ADD( // <0> + <1>
167 : WASM_F64_SCONVERT_I32(
168 : WASM_GET_LOCAL(0)), // <0> to double
169 : WASM_F64_SCONVERT_I64(
170 : WASM_GET_LOCAL(1))), // <1> to double
171 : WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double
172 : WASM_GET_LOCAL(3)) // <3>
173 : },
174 : [](int32_t a, int64_t b, float c, double d) {
175 460 : return 0. + a + b + c + d;
176 472 : });
177 :
178 : Vector<const int32_t> test_values_i32 = compiler::ValueHelper::int32_vector();
179 : Vector<const int64_t> test_values_i64 = compiler::ValueHelper::int64_vector();
180 : Vector<const float> test_values_f32 = compiler::ValueHelper::float32_vector();
181 : Vector<const double> test_values_f64 =
182 : compiler::ValueHelper::float64_vector();
183 : size_t max_len =
184 4 : std::max(std::max(test_values_i32.size(), test_values_i64.size()),
185 8 : std::max(test_values_f32.size(), test_values_f64.size()));
186 924 : for (size_t i = 0; i < max_len; ++i) {
187 920 : int32_t i32 = test_values_i32[i % test_values_i32.size()];
188 920 : int64_t i64 = test_values_i64[i % test_values_i64.size()];
189 920 : float f32 = test_values_f32[i % test_values_f32.size()];
190 920 : double f64 = test_values_f64[i % test_values_f64.size()];
191 460 : tester.CheckCall(i32, i64, f32, f64);
192 : }
193 4 : }
194 :
195 : } // namespace wasm
196 : } // namespace internal
197 79917 : } // namespace v8
|