OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stdint.h> | 5 #include <stdint.h> |
6 #include <stdlib.h> | 6 #include <stdlib.h> |
7 #include <string.h> | 7 #include <string.h> |
8 | 8 |
9 #include "src/base/utils/random-number-generator.h" | |
10 | |
11 #include "src/compiler/graph-visualizer.h" | |
12 #include "src/compiler/js-graph.h" | |
13 #include "src/compiler/wasm-compiler.h" | |
14 | |
15 #include "src/wasm/ast-decoder.h" | |
16 #include "src/wasm/wasm-macro-gen.h" | 9 #include "src/wasm/wasm-macro-gen.h" |
17 #include "src/wasm/wasm-module.h" | |
18 #include "src/wasm/wasm-opcodes.h" | |
19 | 10 |
20 #include "test/cctest/cctest.h" | 11 #include "test/cctest/cctest.h" |
21 #include "test/cctest/compiler/codegen-tester.h" | |
22 #include "test/cctest/compiler/graph-builder-tester.h" | |
23 #include "test/cctest/compiler/value-helper.h" | 12 #include "test/cctest/compiler/value-helper.h" |
24 | |
25 #include "test/cctest/wasm/test-signatures.h" | 13 #include "test/cctest/wasm/test-signatures.h" |
26 | 14 #include "test/cctest/wasm/wasm-run-utils.h" |
27 // TODO(titzer): pull WASM_64 up to a common header. | |
28 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 | |
29 #define WASM_64 1 | |
30 #else | |
31 #define WASM_64 0 | |
32 #endif | |
33 | |
34 // TODO(titzer): check traps more robustly in tests. | |
35 // Currently, in tests, we just return 0xdeadbeef from the function in which | |
36 // the trap occurs if the runtime context is not available to throw a JavaScript | |
37 // exception. | |
38 #define CHECK_TRAP32(x) \ | |
39 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) | |
40 #define CHECK_TRAP64(x) \ | |
41 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) | |
42 #define CHECK_TRAP(x) CHECK_TRAP32(x) | |
43 | |
44 | 15 |
45 using namespace v8::base; | 16 using namespace v8::base; |
46 using namespace v8::internal; | 17 using namespace v8::internal; |
47 using namespace v8::internal::compiler; | 18 using namespace v8::internal::compiler; |
48 using namespace v8::internal::wasm; | 19 using namespace v8::internal::wasm; |
49 | 20 |
50 static void init_env(FunctionEnv* env, FunctionSig* sig) { | |
51 env->module = nullptr; | |
52 env->sig = sig; | |
53 env->local_int32_count = 0; | |
54 env->local_int64_count = 0; | |
55 env->local_float32_count = 0; | |
56 env->local_float64_count = 0; | |
57 env->SumLocals(); | |
58 } | |
59 | |
60 const uint32_t kMaxGlobalsSize = 128; | |
61 | |
62 // A helper for module environments that adds the ability to allocate memory | |
63 // and global variables. | |
64 class TestingModule : public ModuleEnv { | |
65 public: | |
66 TestingModule() : mem_size(0), global_offset(0) { | |
67 globals_area = 0; | |
68 mem_start = 0; | |
69 mem_end = 0; | |
70 module = nullptr; | |
71 linker = nullptr; | |
72 function_code = nullptr; | |
73 asm_js = false; | |
74 memset(global_data, 0, sizeof(global_data)); | |
75 } | |
76 | |
77 ~TestingModule() { | |
78 if (mem_start) { | |
79 free(raw_mem_start<byte>()); | |
80 } | |
81 if (function_code) delete function_code; | |
82 if (module) delete module; | |
83 } | |
84 | |
85 byte* AddMemory(size_t size) { | |
86 CHECK_EQ(0, mem_start); | |
87 CHECK_EQ(0, mem_size); | |
88 mem_start = reinterpret_cast<uintptr_t>(malloc(size)); | |
89 CHECK(mem_start); | |
90 byte* raw = raw_mem_start<byte>(); | |
91 memset(raw, 0, size); | |
92 mem_end = mem_start + size; | |
93 mem_size = size; | |
94 return raw_mem_start<byte>(); | |
95 } | |
96 | |
97 template <typename T> | |
98 T* AddMemoryElems(size_t count) { | |
99 AddMemory(count * sizeof(T)); | |
100 return raw_mem_start<T>(); | |
101 } | |
102 | |
103 template <typename T> | |
104 T* AddGlobal(MachineType mem_type) { | |
105 WasmGlobal* global = AddGlobal(mem_type); | |
106 return reinterpret_cast<T*>(globals_area + global->offset); | |
107 } | |
108 | |
109 byte AddSignature(FunctionSig* sig) { | |
110 AllocModule(); | |
111 if (!module->signatures) { | |
112 module->signatures = new std::vector<FunctionSig*>(); | |
113 } | |
114 module->signatures->push_back(sig); | |
115 size_t size = module->signatures->size(); | |
116 CHECK(size < 127); | |
117 return static_cast<byte>(size - 1); | |
118 } | |
119 | |
120 template <typename T> | |
121 T* raw_mem_start() { | |
122 DCHECK(mem_start); | |
123 return reinterpret_cast<T*>(mem_start); | |
124 } | |
125 | |
126 template <typename T> | |
127 T* raw_mem_end() { | |
128 DCHECK(mem_end); | |
129 return reinterpret_cast<T*>(mem_end); | |
130 } | |
131 | |
132 template <typename T> | |
133 T raw_mem_at(int i) { | |
134 DCHECK(mem_start); | |
135 return reinterpret_cast<T*>(mem_start)[i]; | |
136 } | |
137 | |
138 template <typename T> | |
139 T raw_val_at(int i) { | |
140 T val; | |
141 memcpy(&val, reinterpret_cast<void*>(mem_start + i), sizeof(T)); | |
142 return val; | |
143 } | |
144 | |
145 // Zero-initialize the memory. | |
146 void BlankMemory() { | |
147 byte* raw = raw_mem_start<byte>(); | |
148 memset(raw, 0, mem_size); | |
149 } | |
150 | |
151 // Pseudo-randomly intialize the memory. | |
152 void RandomizeMemory(unsigned int seed = 88) { | |
153 byte* raw = raw_mem_start<byte>(); | |
154 byte* end = raw_mem_end<byte>(); | |
155 v8::base::RandomNumberGenerator rng; | |
156 rng.SetSeed(seed); | |
157 rng.NextBytes(raw, end - raw); | |
158 } | |
159 | |
160 WasmFunction* AddFunction(FunctionSig* sig, Handle<Code> code) { | |
161 AllocModule(); | |
162 if (module->functions == nullptr) { | |
163 module->functions = new std::vector<WasmFunction>(); | |
164 function_code = new std::vector<Handle<Code>>(); | |
165 } | |
166 module->functions->push_back({sig, 0, 0, 0, 0, 0, 0, 0, false, false}); | |
167 function_code->push_back(code); | |
168 return &module->functions->back(); | |
169 } | |
170 | |
171 private: | |
172 size_t mem_size; | |
173 uint32_t global_offset; | |
174 byte global_data[kMaxGlobalsSize]; | |
175 | |
176 WasmGlobal* AddGlobal(MachineType mem_type) { | |
177 AllocModule(); | |
178 if (globals_area == 0) { | |
179 globals_area = reinterpret_cast<uintptr_t>(global_data); | |
180 module->globals = new std::vector<WasmGlobal>(); | |
181 } | |
182 byte size = WasmOpcodes::MemSize(mem_type); | |
183 global_offset = (global_offset + size - 1) & ~(size - 1); // align | |
184 module->globals->push_back({0, mem_type, global_offset, false}); | |
185 global_offset += size; | |
186 // limit number of globals. | |
187 CHECK_LT(global_offset, kMaxGlobalsSize); | |
188 return &module->globals->back(); | |
189 } | |
190 void AllocModule() { | |
191 if (module == nullptr) { | |
192 module = new WasmModule(); | |
193 module->globals = nullptr; | |
194 module->functions = nullptr; | |
195 module->data_segments = nullptr; | |
196 } | |
197 } | |
198 }; | |
199 | |
200 | |
201 static void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, FunctionEnv* env, | |
202 const byte* start, const byte* end) { | |
203 compiler::WasmGraphBuilder builder(zone, jsgraph, env->sig); | |
204 TreeResult result = BuildTFGraph(&builder, env, start, end); | |
205 if (result.failed()) { | |
206 ptrdiff_t pc = result.error_pc - result.start; | |
207 ptrdiff_t pt = result.error_pt - result.start; | |
208 std::ostringstream str; | |
209 str << "Verification failed: " << result.error_code << " pc = +" << pc; | |
210 if (result.error_pt) str << ", pt = +" << pt; | |
211 str << ", msg = " << result.error_msg.get(); | |
212 FATAL(str.str().c_str()); | |
213 } | |
214 if (FLAG_trace_turbo_graph) { | |
215 OFStream os(stdout); | |
216 os << AsRPO(*jsgraph->graph()); | |
217 } | |
218 } | |
219 | |
220 | |
221 // A helper for compiling functions that are only internally callable WASM code. | |
222 class WasmFunctionCompiler : public HandleAndZoneScope, | |
223 private GraphAndBuilders { | |
224 public: | |
225 explicit WasmFunctionCompiler(FunctionSig* sig) | |
226 : GraphAndBuilders(main_zone()), | |
227 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, | |
228 nullptr, this->machine()), | |
229 descriptor_(nullptr) { | |
230 init_env(&env, sig); | |
231 } | |
232 | |
233 JSGraph jsgraph; | |
234 FunctionEnv env; | |
235 // The call descriptor is initialized when the function is compiled. | |
236 CallDescriptor* descriptor_; | |
237 | |
238 Isolate* isolate() { return main_isolate(); } | |
239 Graph* graph() const { return main_graph_; } | |
240 Zone* zone() const { return graph()->zone(); } | |
241 CommonOperatorBuilder* common() { return &main_common_; } | |
242 MachineOperatorBuilder* machine() { return &main_machine_; } | |
243 CallDescriptor* descriptor() { return descriptor_; } | |
244 | |
245 void Build(const byte* start, const byte* end) { | |
246 TestBuildingGraph(main_zone(), &jsgraph, &env, start, end); | |
247 } | |
248 | |
249 byte AllocateLocal(LocalType type) { | |
250 int result = static_cast<int>(env.total_locals); | |
251 env.AddLocals(type, 1); | |
252 byte b = static_cast<byte>(result); | |
253 CHECK_EQ(result, b); | |
254 return b; | |
255 } | |
256 | |
257 Handle<Code> Compile(ModuleEnv* module) { | |
258 descriptor_ = module->GetWasmCallDescriptor(this->zone(), env.sig); | |
259 CompilationInfo info("wasm compile", this->isolate(), this->zone()); | |
260 Handle<Code> result = | |
261 Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph()); | |
262 #ifdef ENABLE_DISASSEMBLER | |
263 if (!result.is_null() && FLAG_print_opt_code) { | |
264 OFStream os(stdout); | |
265 result->Disassemble("wasm code", os); | |
266 } | |
267 #endif | |
268 | |
269 return result; | |
270 } | |
271 | |
272 uint32_t CompileAndAdd(TestingModule* module) { | |
273 uint32_t index = 0; | |
274 if (module->module && module->module->functions) { | |
275 index = static_cast<uint32_t>(module->module->functions->size()); | |
276 } | |
277 module->AddFunction(env.sig, Compile(module)); | |
278 return index; | |
279 } | |
280 }; | |
281 | |
282 | |
283 // A helper class to build graphs from Wasm bytecode, generate machine | |
284 // code, and run that code. | |
285 template <typename ReturnType> | |
286 class WasmRunner { | |
287 public: | |
288 WasmRunner(MachineType p0 = MachineType::None(), | |
289 MachineType p1 = MachineType::None(), | |
290 MachineType p2 = MachineType::None(), | |
291 MachineType p3 = MachineType::None()) | |
292 : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, | |
293 GetParameterCount(p0, p1, p2, p3), storage_), | |
294 compiler_(&signature_), | |
295 call_wrapper_(p0, p1, p2, p3), | |
296 compilation_done_(false) { | |
297 int index = 0; | |
298 MachineType ret = MachineTypeForC<ReturnType>(); | |
299 if (ret != MachineType::None()) { | |
300 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); | |
301 } | |
302 if (p0 != MachineType::None()) | |
303 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); | |
304 if (p1 != MachineType::None()) | |
305 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); | |
306 if (p2 != MachineType::None()) | |
307 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); | |
308 if (p3 != MachineType::None()) | |
309 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); | |
310 } | |
311 | |
312 | |
313 FunctionEnv* env() { return &compiler_.env; } | |
314 | |
315 | |
316 // Builds a graph from the given Wasm code, and generates the machine | |
317 // code and call wrapper for that graph. This method must not be called | |
318 // more than once. | |
319 void Build(const byte* start, const byte* end) { | |
320 DCHECK(!compilation_done_); | |
321 compilation_done_ = true; | |
322 // Build the TF graph. | |
323 compiler_.Build(start, end); | |
324 // Generate code. | |
325 Handle<Code> code = compiler_.Compile(env()->module); | |
326 | |
327 // Construct the call wrapper. | |
328 Node* inputs[5]; | |
329 int input_count = 0; | |
330 inputs[input_count++] = call_wrapper_.HeapConstant(code); | |
331 for (size_t i = 0; i < signature_.parameter_count(); i++) { | |
332 inputs[input_count++] = call_wrapper_.Parameter(i); | |
333 } | |
334 | |
335 call_wrapper_.Return(call_wrapper_.AddNode( | |
336 call_wrapper_.common()->Call(compiler_.descriptor()), input_count, | |
337 inputs)); | |
338 } | |
339 | |
340 | |
341 ReturnType Call() { return call_wrapper_.Call(); } | |
342 | |
343 | |
344 template <typename P0> | |
345 ReturnType Call(P0 p0) { | |
346 return call_wrapper_.Call(p0); | |
347 } | |
348 | |
349 | |
350 template <typename P0, typename P1> | |
351 ReturnType Call(P0 p0, P1 p1) { | |
352 return call_wrapper_.Call(p0, p1); | |
353 } | |
354 | |
355 | |
356 template <typename P0, typename P1, typename P2> | |
357 ReturnType Call(P0 p0, P1 p1, P2 p2) { | |
358 return call_wrapper_.Call(p0, p1, p2); | |
359 } | |
360 | |
361 | |
362 template <typename P0, typename P1, typename P2, typename P3> | |
363 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { | |
364 return call_wrapper_.Call(p0, p1, p2, p3); | |
365 } | |
366 | |
367 | |
368 byte AllocateLocal(LocalType type) { | |
369 int result = static_cast<int>(env()->total_locals); | |
370 env()->AddLocals(type, 1); | |
371 byte b = static_cast<byte>(result); | |
372 CHECK_EQ(result, b); | |
373 return b; | |
374 } | |
375 | |
376 | |
377 private: | |
378 LocalType storage_[5]; | |
379 FunctionSig signature_; | |
380 WasmFunctionCompiler compiler_; | |
381 BufferedRawMachineAssemblerTester<ReturnType> call_wrapper_; | |
382 bool compilation_done_; | |
383 | |
384 static size_t GetParameterCount(MachineType p0, MachineType p1, | |
385 MachineType p2, MachineType p3) { | |
386 if (p0 == MachineType::None()) return 0; | |
387 if (p1 == MachineType::None()) return 1; | |
388 if (p2 == MachineType::None()) return 2; | |
389 if (p3 == MachineType::None()) return 3; | |
390 return 4; | |
391 } | |
392 }; | |
393 | |
394 #define BUILD(r, ...) \ | 21 #define BUILD(r, ...) \ |
395 do { \ | 22 do { \ |
396 byte code[] = {__VA_ARGS__}; \ | 23 byte code[] = {__VA_ARGS__}; \ |
397 r.Build(code, code + arraysize(code)); \ | 24 r.Build(code, code + arraysize(code)); \ |
398 } while (false) | 25 } while (false) |
399 | 26 |
400 | 27 |
401 TEST(Run_WasmInt8Const) { | 28 TEST(Run_WasmInt8Const) { |
402 WasmRunner<int8_t> r; | 29 WasmRunner<int8_t> r; |
403 const byte kExpectedValue = 121; | 30 const byte kExpectedValue = 121; |
(...skipping 2485 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2889 } | 2516 } |
2890 } | 2517 } |
2891 | 2518 |
2892 #define ADD_CODE(vec, ...) \ | 2519 #define ADD_CODE(vec, ...) \ |
2893 do { \ | 2520 do { \ |
2894 byte __buf[] = {__VA_ARGS__}; \ | 2521 byte __buf[] = {__VA_ARGS__}; \ |
2895 for (size_t i = 0; i < sizeof(__buf); i++) vec.push_back(__buf[i]); \ | 2522 for (size_t i = 0; i < sizeof(__buf); i++) vec.push_back(__buf[i]); \ |
2896 } while (false) | 2523 } while (false) |
2897 | 2524 |
2898 | 2525 |
2899 void Run_WasmMixedCall_N(int start) { | 2526 static void Run_WasmMixedCall_N(int start) { |
2900 const int kExpected = 6333; | 2527 const int kExpected = 6333; |
2901 const int kElemSize = 8; | 2528 const int kElemSize = 8; |
2902 TestSignatures sigs; | 2529 TestSignatures sigs; |
2903 | 2530 |
2904 #if WASM_64 | 2531 #if WASM_64 |
2905 static MachineType mixed[] = { | 2532 static MachineType mixed[] = { |
2906 MachineType::Int32(), MachineType::Float32(), MachineType::Int64(), | 2533 MachineType::Int32(), MachineType::Float32(), MachineType::Int64(), |
2907 MachineType::Float64(), MachineType::Float32(), MachineType::Int64(), | 2534 MachineType::Float64(), MachineType::Float32(), MachineType::Int64(), |
2908 MachineType::Int32(), MachineType::Float64(), MachineType::Float32(), | 2535 MachineType::Int32(), MachineType::Float64(), MachineType::Float32(), |
2909 MachineType::Float64(), MachineType::Int32(), MachineType::Int64(), | 2536 MachineType::Float64(), MachineType::Int32(), MachineType::Int64(), |
(...skipping 708 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3618 TEST(Run_Wasm_F32CopySign) { | 3245 TEST(Run_Wasm_F32CopySign) { |
3619 WasmRunner<float> r(MachineType::Float32(), MachineType::Float32()); | 3246 WasmRunner<float> r(MachineType::Float32(), MachineType::Float32()); |
3620 BUILD(r, WASM_F32_COPYSIGN(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); | 3247 BUILD(r, WASM_F32_COPYSIGN(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); |
3621 | 3248 |
3622 FOR_FLOAT32_INPUTS(i) { | 3249 FOR_FLOAT32_INPUTS(i) { |
3623 FOR_FLOAT32_INPUTS(j) { CheckFloatEq(copysign(*i, *j), r.Call(*i, *j)); } | 3250 FOR_FLOAT32_INPUTS(j) { CheckFloatEq(copysign(*i, *j), r.Call(*i, *j)); } |
3624 } | 3251 } |
3625 } | 3252 } |
3626 | 3253 |
3627 #endif | 3254 #endif |
OLD | NEW |