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