Line data Source code
1 : // Copyright 2014 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 <cmath>
6 : #include <functional>
7 : #include <limits>
8 : #include <memory>
9 :
10 : #include "src/assembler.h"
11 : #include "src/base/bits.h"
12 : #include "src/compiler.h"
13 : #include "src/compiler/linkage.h"
14 : #include "src/compiler/wasm-compiler.h"
15 : #include "src/machine-type.h"
16 : #include "src/macro-assembler.h"
17 : #include "src/objects-inl.h"
18 : #include "src/wasm/function-compiler.h"
19 : #include "src/wasm/wasm-engine.h"
20 : #include "src/wasm/wasm-objects-inl.h"
21 : #include "src/wasm/wasm-opcodes.h"
22 : #include "test/cctest/cctest.h"
23 : #include "test/cctest/compiler/codegen-tester.h"
24 : #include "test/cctest/compiler/value-helper.h"
25 :
26 : namespace v8 {
27 : namespace internal {
28 : namespace compiler {
29 :
30 : namespace {
31 :
32 752 : CallDescriptor* CreateCallDescriptor(Zone* zone, int return_count,
33 : int param_count, MachineType type) {
34 752 : wasm::FunctionSig::Builder builder(zone, return_count, param_count);
35 :
36 7792 : for (int i = 0; i < param_count; i++) {
37 3520 : builder.AddParam(wasm::ValueTypes::ValueTypeFor(type));
38 : }
39 :
40 32944 : for (int i = 0; i < return_count; i++) {
41 16096 : builder.AddReturn(wasm::ValueTypes::ValueTypeFor(type));
42 : }
43 752 : return compiler::GetWasmCallDescriptor(zone, builder.Build());
44 : }
45 :
46 14176 : Node* MakeConstant(RawMachineAssembler& m, MachineType type, int value) {
47 14176 : switch (type.representation()) {
48 : case MachineRepresentation::kWord32:
49 3544 : return m.Int32Constant(static_cast<int32_t>(value));
50 : case MachineRepresentation::kWord64:
51 3544 : return m.Int64Constant(static_cast<int64_t>(value));
52 : case MachineRepresentation::kFloat32:
53 3544 : return m.Float32Constant(static_cast<float>(value));
54 : case MachineRepresentation::kFloat64:
55 3544 : return m.Float64Constant(static_cast<double>(value));
56 : default:
57 0 : UNREACHABLE();
58 : }
59 : }
60 :
61 5024 : Node* Add(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
62 5024 : switch (type.representation()) {
63 : case MachineRepresentation::kWord32:
64 1256 : return m.Int32Add(a, b);
65 : case MachineRepresentation::kWord64:
66 1256 : return m.Int64Add(a, b);
67 : case MachineRepresentation::kFloat32:
68 1256 : return m.Float32Add(a, b);
69 : case MachineRepresentation::kFloat64:
70 1256 : return m.Float64Add(a, b);
71 : default:
72 0 : UNREACHABLE();
73 : }
74 : }
75 :
76 5312 : Node* Sub(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
77 5312 : switch (type.representation()) {
78 : case MachineRepresentation::kWord32:
79 1328 : return m.Int32Sub(a, b);
80 : case MachineRepresentation::kWord64:
81 1328 : return m.Int64Sub(a, b);
82 : case MachineRepresentation::kFloat32:
83 1328 : return m.Float32Sub(a, b);
84 : case MachineRepresentation::kFloat64:
85 1328 : return m.Float64Sub(a, b);
86 : default:
87 0 : UNREACHABLE();
88 : }
89 : }
90 :
91 1824 : Node* Mul(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
92 1824 : switch (type.representation()) {
93 : case MachineRepresentation::kWord32:
94 456 : return m.Int32Mul(a, b);
95 : case MachineRepresentation::kWord64:
96 456 : return m.Int64Mul(a, b);
97 : case MachineRepresentation::kFloat32:
98 456 : return m.Float32Mul(a, b);
99 : case MachineRepresentation::kFloat64:
100 456 : return m.Float64Mul(a, b);
101 : default:
102 0 : UNREACHABLE();
103 : }
104 : }
105 :
106 896 : Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) {
107 896 : switch (type.representation()) {
108 : case MachineRepresentation::kWord32:
109 : return a;
110 : case MachineRepresentation::kWord64:
111 224 : return m.TruncateInt64ToInt32(a);
112 : case MachineRepresentation::kFloat32:
113 224 : return m.TruncateFloat32ToInt32(a);
114 : case MachineRepresentation::kFloat64:
115 224 : return m.RoundFloat64ToInt32(a);
116 : default:
117 0 : UNREACHABLE();
118 : }
119 : }
120 :
121 752 : std::shared_ptr<wasm::NativeModule> AllocateNativeModule(Isolate* isolate,
122 : size_t code_size) {
123 1504 : std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule());
124 752 : module->num_declared_functions = 1;
125 : // We have to add the code object to a NativeModule, because the
126 : // WasmCallDescriptor assumes that code is on the native heap and not
127 : // within a code object.
128 : return isolate->wasm_engine()->NewNativeModule(
129 1504 : isolate, wasm::kAllWasmFeatures, code_size, false, std::move(module));
130 : }
131 :
132 16 : void TestReturnMultipleValues(MachineType type) {
133 : const int kMaxCount = 20;
134 : const int kMaxParamCount = 9;
135 : // Use 9 parameters as a regression test or https://crbug.com/838098.
136 80 : for (int param_count : {2, kMaxParamCount}) {
137 1312 : for (int count = 0; count < kMaxCount; ++count) {
138 640 : printf("\n==== type = %s, count = %d ====\n\n\n",
139 : MachineReprToString(type.representation()), count);
140 1280 : v8::internal::AccountingAllocator allocator;
141 1280 : Zone zone(&allocator, ZONE_NAME);
142 : CallDescriptor* desc =
143 640 : CreateCallDescriptor(&zone, count, param_count, type);
144 1280 : HandleAndZoneScope handles;
145 : RawMachineAssembler m(
146 : handles.main_isolate(),
147 640 : new (handles.main_zone()) Graph(handles.main_zone()), desc,
148 : MachineType::PointerRepresentation(),
149 1280 : InstructionSelector::SupportedMachineOperatorFlags());
150 :
151 : // m.Parameter(0) is the WasmContext.
152 640 : Node* p0 = m.Parameter(1);
153 640 : Node* p1 = m.Parameter(2);
154 : typedef Node* Node_ptr;
155 640 : std::unique_ptr<Node_ptr[]> returns(new Node_ptr[count]);
156 12800 : for (int i = 0; i < count; ++i) {
157 8320 : if (i % 3 == 0) returns[i] = Add(m, type, p0, p1);
158 8096 : if (i % 3 == 1) returns[i] = Sub(m, type, p0, p1);
159 7904 : if (i % 3 == 2) returns[i] = Mul(m, type, p0, p1);
160 : }
161 640 : m.Return(count, returns.get());
162 :
163 : OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
164 1280 : Code::WASM_FUNCTION);
165 : Handle<Code> code =
166 1280 : Pipeline::GenerateCodeForTesting(
167 : &info, handles.main_isolate(), desc, m.graph(),
168 1280 : AssemblerOptions::Default(handles.main_isolate()), m.Export())
169 : .ToHandleChecked();
170 : #ifdef ENABLE_DISASSEMBLER
171 : if (FLAG_print_code) {
172 : StdoutStream os;
173 : code->Disassemble("multi_value", os);
174 : }
175 : #endif
176 :
177 : const int a = 47, b = 12;
178 : int expect = 0;
179 12800 : for (int i = 0, sign = +1; i < count; ++i) {
180 6080 : if (i % 3 == 0) expect += sign * (a + b);
181 6080 : if (i % 3 == 1) expect += sign * (a - b);
182 6080 : if (i % 3 == 2) expect += sign * (a * b);
183 6080 : if (i % 4 == 0) sign = -sign;
184 : }
185 :
186 : std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
187 640 : handles.main_isolate(), code->raw_instruction_size());
188 1280 : wasm::WasmCodeRefScope wasm_code_ref_scope;
189 : byte* code_start =
190 640 : module->AddCodeForTesting(code)->instructions().start();
191 :
192 640 : RawMachineAssemblerTester<int32_t> mt(Code::Kind::JS_TO_WASM_FUNCTION);
193 640 : const int input_count = 2 + param_count;
194 : Node* call_inputs[2 + kMaxParamCount];
195 640 : call_inputs[0] = mt.PointerConstant(code_start);
196 : // WasmContext dummy
197 640 : call_inputs[1] = mt.PointerConstant(nullptr);
198 : // Special inputs for the test.
199 640 : call_inputs[2] = MakeConstant(mt, type, a);
200 640 : call_inputs[3] = MakeConstant(mt, type, b);
201 5120 : for (int i = 2; i < param_count; i++) {
202 2240 : call_inputs[2 + i] = MakeConstant(mt, type, i);
203 : }
204 :
205 640 : Node* ret_multi = mt.AddNode(mt.common()->Call(desc),
206 640 : input_count, call_inputs);
207 640 : Node* ret = MakeConstant(mt, type, 0);
208 : bool sign = false;
209 12800 : for (int i = 0; i < count; ++i) {
210 : Node* x = (count == 1)
211 : ? ret_multi
212 6080 : : mt.AddNode(mt.common()->Projection(i), ret_multi);
213 6080 : ret = sign ? Sub(mt, type, ret, x) : Add(mt, type, ret, x);
214 6080 : if (i % 4 == 0) sign = !sign;
215 : }
216 640 : mt.Return(ToInt32(mt, type, ret));
217 : #ifdef ENABLE_DISASSEMBLER
218 : Handle<Code> code2 = mt.GetCode();
219 : if (FLAG_print_code) {
220 : StdoutStream os;
221 : code2->Disassemble("multi_value_call", os);
222 : }
223 : #endif
224 640 : CHECK_EQ(expect, mt.Call());
225 : }
226 : }
227 16 : }
228 :
229 : } // namespace
230 :
231 : #define TEST_MULTI(Type, type) \
232 : TEST(ReturnMultiple##Type) { TestReturnMultipleValues(type); }
233 :
234 26660 : TEST_MULTI(Int32, MachineType::Int32())
235 : #if (!V8_TARGET_ARCH_32_BIT)
236 26660 : TEST_MULTI(Int64, MachineType::Int64())
237 : #endif
238 26660 : TEST_MULTI(Float32, MachineType::Float32())
239 26660 : TEST_MULTI(Float64, MachineType::Float64())
240 :
241 : #undef TEST_MULTI
242 :
243 16 : void ReturnLastValue(MachineType type) {
244 16 : int slot_counts[] = {1, 2, 3, 600};
245 144 : for (auto slot_count : slot_counts) {
246 128 : v8::internal::AccountingAllocator allocator;
247 128 : Zone zone(&allocator, ZONE_NAME);
248 : // The wasm-linkage provides 2 return registers at the moment, on all
249 : // platforms.
250 64 : const int return_count = 2 + slot_count;
251 :
252 64 : CallDescriptor* desc = CreateCallDescriptor(&zone, return_count, 0, type);
253 :
254 128 : HandleAndZoneScope handles;
255 : RawMachineAssembler m(handles.main_isolate(),
256 64 : new (handles.main_zone()) Graph(handles.main_zone()),
257 : desc, MachineType::PointerRepresentation(),
258 128 : InstructionSelector::SupportedMachineOperatorFlags());
259 :
260 64 : std::unique_ptr<Node* []> returns(new Node*[return_count]);
261 :
262 19712 : for (int i = 0; i < return_count; ++i) {
263 19648 : returns[i] = MakeConstant(m, type, i);
264 : }
265 :
266 64 : m.Return(return_count, returns.get());
267 :
268 : OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
269 128 : Code::WASM_FUNCTION);
270 : Handle<Code> code =
271 128 : Pipeline::GenerateCodeForTesting(
272 : &info, handles.main_isolate(), desc, m.graph(),
273 128 : AssemblerOptions::Default(handles.main_isolate()), m.Export())
274 : .ToHandleChecked();
275 :
276 : std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
277 64 : handles.main_isolate(), code->raw_instruction_size());
278 128 : wasm::WasmCodeRefScope wasm_code_ref_scope;
279 64 : byte* code_start = module->AddCodeForTesting(code)->instructions().start();
280 :
281 : // Generate caller.
282 64 : int expect = return_count - 1;
283 64 : RawMachineAssemblerTester<int32_t> mt;
284 : Node* inputs[] = {mt.PointerConstant(code_start),
285 : // WasmContext dummy
286 128 : mt.PointerConstant(nullptr)};
287 :
288 64 : Node* call = mt.AddNode(mt.common()->Call(desc), 2, inputs);
289 :
290 128 : mt.Return(ToInt32(
291 64 : mt, type, mt.AddNode(mt.common()->Projection(return_count - 1), call)));
292 :
293 64 : CHECK_EQ(expect, mt.Call());
294 : }
295 16 : }
296 :
297 26660 : TEST(ReturnLastValueInt32) { ReturnLastValue(MachineType::Int32()); }
298 : #if (!V8_TARGET_ARCH_32_BIT)
299 26660 : TEST(ReturnLastValueInt64) { ReturnLastValue(MachineType::Int64()); }
300 : #endif
301 26660 : TEST(ReturnLastValueFloat32) { ReturnLastValue(MachineType::Float32()); }
302 26660 : TEST(ReturnLastValueFloat64) { ReturnLastValue(MachineType::Float64()); }
303 :
304 16 : void ReturnSumOfReturns(MachineType type) {
305 112 : for (int unused_stack_slots = 0; unused_stack_slots <= 2;
306 : ++unused_stack_slots) {
307 96 : v8::internal::AccountingAllocator allocator;
308 96 : Zone zone(&allocator, ZONE_NAME);
309 : // Let {unused_stack_slots + 1} returns be on the stack.
310 : // The wasm-linkage provides 2 return registers at the moment, on all
311 : // platforms.
312 48 : const int return_count = 2 + unused_stack_slots + 1;
313 :
314 48 : CallDescriptor* desc = CreateCallDescriptor(&zone, return_count, 0, type);
315 :
316 96 : HandleAndZoneScope handles;
317 : RawMachineAssembler m(handles.main_isolate(),
318 48 : new (handles.main_zone()) Graph(handles.main_zone()),
319 : desc, MachineType::PointerRepresentation(),
320 96 : InstructionSelector::SupportedMachineOperatorFlags());
321 :
322 48 : std::unique_ptr<Node* []> returns(new Node*[return_count]);
323 :
324 432 : for (int i = 0; i < return_count; ++i) {
325 384 : returns[i] = MakeConstant(m, type, i);
326 : }
327 :
328 48 : m.Return(return_count, returns.get());
329 :
330 : OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
331 96 : Code::WASM_FUNCTION);
332 : Handle<Code> code =
333 96 : Pipeline::GenerateCodeForTesting(
334 : &info, handles.main_isolate(), desc, m.graph(),
335 96 : AssemblerOptions::Default(handles.main_isolate()), m.Export())
336 : .ToHandleChecked();
337 :
338 : std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
339 48 : handles.main_isolate(), code->raw_instruction_size());
340 96 : wasm::WasmCodeRefScope wasm_code_ref_scope;
341 48 : byte* code_start = module->AddCodeForTesting(code)->instructions().start();
342 :
343 : // Generate caller.
344 48 : RawMachineAssemblerTester<int32_t> mt;
345 : Node* call_inputs[] = {mt.PointerConstant(code_start),
346 : // WasmContext dummy
347 96 : mt.PointerConstant(nullptr)};
348 :
349 48 : Node* call = mt.AddNode(mt.common()->Call(desc), 2, call_inputs);
350 :
351 : uint32_t expect = 0;
352 48 : Node* result = mt.Int32Constant(0);
353 :
354 432 : for (int i = 0; i < return_count; ++i) {
355 192 : expect += i;
356 384 : result = mt.Int32Add(
357 : result,
358 192 : ToInt32(mt, type, mt.AddNode(mt.common()->Projection(i), call)));
359 : }
360 :
361 48 : mt.Return(result);
362 :
363 48 : CHECK_EQ(expect, mt.Call());
364 : }
365 16 : }
366 :
367 26660 : TEST(ReturnSumOfReturnsInt32) { ReturnSumOfReturns(MachineType::Int32()); }
368 : #if (!V8_TARGET_ARCH_32_BIT)
369 26660 : TEST(ReturnSumOfReturnsInt64) { ReturnSumOfReturns(MachineType::Int64()); }
370 : #endif
371 26660 : TEST(ReturnSumOfReturnsFloat32) { ReturnSumOfReturns(MachineType::Float32()); }
372 26660 : TEST(ReturnSumOfReturnsFloat64) { ReturnSumOfReturns(MachineType::Float64()); }
373 :
374 : } // namespace compiler
375 : } // namespace internal
376 79968 : } // namespace v8
|