OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 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 #ifndef WASM_RUN_UTILS_H | 5 #ifndef WASM_RUN_UTILS_H |
6 #define WASM_RUN_UTILS_H | 6 #define WASM_RUN_UTILS_H |
7 | 7 |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 #include <stdlib.h> | 9 #include <stdlib.h> |
10 #include <string.h> | 10 #include <string.h> |
11 | 11 |
12 #include "src/base/utils/random-number-generator.h" | 12 #include "src/base/utils/random-number-generator.h" |
13 | 13 |
14 #include "src/compiler/graph-visualizer.h" | 14 #include "src/compiler/graph-visualizer.h" |
15 #include "src/compiler/int64-lowering.h" | 15 #include "src/compiler/int64-lowering.h" |
16 #include "src/compiler/js-graph.h" | 16 #include "src/compiler/js-graph.h" |
| 17 #include "src/compiler/node.h" |
| 18 #include "src/compiler/pipeline.h" |
17 #include "src/compiler/wasm-compiler.h" | 19 #include "src/compiler/wasm-compiler.h" |
18 | 20 |
19 #include "src/wasm/ast-decoder.h" | 21 #include "src/wasm/ast-decoder.h" |
20 #include "src/wasm/wasm-js.h" | 22 #include "src/wasm/wasm-js.h" |
21 #include "src/wasm/wasm-module.h" | 23 #include "src/wasm/wasm-module.h" |
22 #include "src/wasm/wasm-opcodes.h" | 24 #include "src/wasm/wasm-opcodes.h" |
23 | 25 |
| 26 #include "src/zone.h" |
| 27 |
24 #include "test/cctest/cctest.h" | 28 #include "test/cctest/cctest.h" |
25 #include "test/cctest/compiler/codegen-tester.h" | 29 #include "test/cctest/compiler/call-tester.h" |
26 #include "test/cctest/compiler/graph-builder-tester.h" | 30 #include "test/cctest/compiler/graph-builder-tester.h" |
27 | 31 |
28 // TODO(titzer): pull WASM_64 up to a common header. | 32 // TODO(titzer): pull WASM_64 up to a common header. |
29 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 | 33 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 |
30 #define WASM_64 1 | 34 #define WASM_64 1 |
31 #else | 35 #else |
32 #define WASM_64 0 | 36 #define WASM_64 0 |
33 #endif | 37 #endif |
34 | 38 |
35 // TODO(titzer): check traps more robustly in tests. | 39 // TODO(titzer): check traps more robustly in tests. |
36 // Currently, in tests, we just return 0xdeadbeef from the function in which | 40 // Currently, in tests, we just return 0xdeadbeef from the function in which |
37 // the trap occurs if the runtime context is not available to throw a JavaScript | 41 // the trap occurs if the runtime context is not available to throw a JavaScript |
38 // exception. | 42 // exception. |
39 #define CHECK_TRAP32(x) \ | 43 #define CHECK_TRAP32(x) \ |
40 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) | 44 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) |
41 #define CHECK_TRAP64(x) \ | 45 #define CHECK_TRAP64(x) \ |
42 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) | 46 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) |
43 #define CHECK_TRAP(x) CHECK_TRAP32(x) | 47 #define CHECK_TRAP(x) CHECK_TRAP32(x) |
44 | 48 |
| 49 #define WASM_WRAPPER_RETURN_VALUE 8754 |
| 50 |
45 namespace { | 51 namespace { |
46 using namespace v8::base; | 52 using namespace v8::base; |
47 using namespace v8::internal; | 53 using namespace v8::internal; |
48 using namespace v8::internal::compiler; | 54 using namespace v8::internal::compiler; |
49 using namespace v8::internal::wasm; | 55 using namespace v8::internal::wasm; |
50 | 56 |
51 inline void init_env(FunctionEnv* env, FunctionSig* sig) { | 57 inline void init_env(FunctionEnv* env, FunctionSig* sig) { |
52 env->module = nullptr; | 58 env->module = nullptr; |
53 env->sig = sig; | 59 env->sig = sig; |
54 env->local_i32_count = 0; | 60 env->local_i32_count = 0; |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 str << ", msg = " << result.error_msg.get(); | 245 str << ", msg = " << result.error_msg.get(); |
240 FATAL(str.str().c_str()); | 246 FATAL(str.str().c_str()); |
241 } | 247 } |
242 builder.Int64LoweringForTesting(); | 248 builder.Int64LoweringForTesting(); |
243 if (FLAG_trace_turbo_graph) { | 249 if (FLAG_trace_turbo_graph) { |
244 OFStream os(stdout); | 250 OFStream os(stdout); |
245 os << AsRPO(*jsgraph->graph()); | 251 os << AsRPO(*jsgraph->graph()); |
246 } | 252 } |
247 } | 253 } |
248 | 254 |
| 255 template <typename ReturnType> |
| 256 class WasmFunctionWrapper : public HandleAndZoneScope, |
| 257 private GraphAndBuilders { |
| 258 public: |
| 259 WasmFunctionWrapper() |
| 260 : GraphAndBuilders(main_zone()), |
| 261 inner_code_node_(nullptr), |
| 262 signature_(nullptr) { |
| 263 Signature<MachineType>::Builder sig_builder(zone(), 1, 5); |
| 264 |
| 265 sig_builder.AddReturn(MachineType::Int32()); |
| 266 for (int i = 0; i < 5; i++) { |
| 267 sig_builder.AddParam(MachineType::Pointer()); |
| 268 } |
| 269 signature_ = sig_builder.Build(); |
| 270 } |
| 271 |
| 272 void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(), |
| 273 MachineType p1 = MachineType::None(), |
| 274 MachineType p2 = MachineType::None(), |
| 275 MachineType p3 = MachineType::None()) { |
| 276 // Create the TF graph for the wrapper. The wrapper always takes four |
| 277 // pointers as parameters, but may not pass the values of all pointers to |
| 278 // the actual test function. |
| 279 |
| 280 // Function, effect, and control. |
| 281 Node** parameters = zone()->template NewArray<Node*>(4 + 3); |
| 282 graph()->SetStart(graph()->NewNode(common()->Start(6))); |
| 283 Node* effect = graph()->start(); |
| 284 int parameter_count = 0; |
| 285 |
| 286 // Dummy node which gets replaced in SetInnerCode. |
| 287 inner_code_node_ = graph()->NewNode(common()->Int32Constant(0)); |
| 288 parameters[parameter_count++] = inner_code_node_; |
| 289 |
| 290 if (p0 != MachineType::None()) { |
| 291 parameters[parameter_count] = graph()->NewNode( |
| 292 machine()->Load(p0), |
| 293 graph()->NewNode(common()->Parameter(0), graph()->start()), |
| 294 graph()->NewNode(common()->Int32Constant(0)), effect, |
| 295 graph()->start()); |
| 296 effect = parameters[parameter_count++]; |
| 297 } |
| 298 if (p1 != MachineType::None()) { |
| 299 parameters[parameter_count] = graph()->NewNode( |
| 300 machine()->Load(p0), |
| 301 graph()->NewNode(common()->Parameter(1), graph()->start()), |
| 302 graph()->NewNode(common()->Int32Constant(0)), effect, |
| 303 graph()->start()); |
| 304 effect = parameters[parameter_count++]; |
| 305 } |
| 306 if (p2 != MachineType::None()) { |
| 307 parameters[parameter_count] = graph()->NewNode( |
| 308 machine()->Load(p0), |
| 309 graph()->NewNode(common()->Parameter(2), graph()->start()), |
| 310 graph()->NewNode(common()->Int32Constant(0)), effect, |
| 311 graph()->start()); |
| 312 effect = parameters[parameter_count++]; |
| 313 } |
| 314 if (p3 != MachineType::None()) { |
| 315 parameters[parameter_count] = graph()->NewNode( |
| 316 machine()->Load(p0), |
| 317 graph()->NewNode(common()->Parameter(3), graph()->start()), |
| 318 graph()->NewNode(common()->Int32Constant(0)), effect, |
| 319 graph()->start()); |
| 320 effect = parameters[parameter_count++]; |
| 321 } |
| 322 |
| 323 parameters[parameter_count++] = effect; |
| 324 parameters[parameter_count++] = graph()->start(); |
| 325 Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count, |
| 326 parameters); |
| 327 |
| 328 effect = graph()->NewNode( |
| 329 machine()->Store( |
| 330 StoreRepresentation(MachineTypeForC<ReturnType>().representation(), |
| 331 WriteBarrierKind::kNoWriteBarrier)), |
| 332 graph()->NewNode(common()->Parameter(4), graph()->start()), |
| 333 graph()->NewNode(common()->Int32Constant(0)), call, effect, |
| 334 graph()->start()); |
| 335 Node* r = graph()->NewNode( |
| 336 common()->Return(), |
| 337 graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)), |
| 338 effect, graph()->start()); |
| 339 graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start())); |
| 340 } |
| 341 |
| 342 void SetInnerCode(Handle<Code> code_handle) { |
| 343 NodeProperties::ChangeOp(inner_code_node_, |
| 344 common()->HeapConstant(code_handle)); |
| 345 } |
| 346 |
| 347 Handle<Code> GetWrapperCode() { |
| 348 if (code_.is_null()) { |
| 349 Isolate* isolate = CcTest::InitIsolateOnce(); |
| 350 |
| 351 CallDescriptor* descriptor = |
| 352 Linkage::GetSimplifiedCDescriptor(zone(), signature_, true); |
| 353 |
| 354 CompilationInfo info("testing", isolate, graph()->zone()); |
| 355 code_ = |
| 356 Pipeline::GenerateCodeForTesting(&info, descriptor, graph(), nullptr); |
| 357 CHECK(!code_.is_null()); |
| 358 #ifdef ENABLE_DISASSEMBLER |
| 359 if (FLAG_print_opt_code) { |
| 360 OFStream os(stdout); |
| 361 code_->Disassemble("wasm wrapper", os); |
| 362 } |
| 363 #endif |
| 364 } |
| 365 |
| 366 return code_; |
| 367 } |
| 368 |
| 369 Signature<MachineType>* signature() const { return signature_; } |
| 370 |
| 371 private: |
| 372 Node* inner_code_node_; |
| 373 Handle<Code> code_; |
| 374 Signature<MachineType>* signature_; |
| 375 }; |
249 | 376 |
250 // A helper for compiling functions that are only internally callable WASM code. | 377 // A helper for compiling functions that are only internally callable WASM code. |
251 class WasmFunctionCompiler : public HandleAndZoneScope, | 378 class WasmFunctionCompiler : public HandleAndZoneScope, |
252 private GraphAndBuilders { | 379 private GraphAndBuilders { |
253 public: | 380 public: |
254 explicit WasmFunctionCompiler(FunctionSig* sig, ModuleEnv* module = nullptr) | 381 explicit WasmFunctionCompiler(FunctionSig* sig, ModuleEnv* module = nullptr) |
255 : GraphAndBuilders(main_zone()), | 382 : GraphAndBuilders(main_zone()), |
256 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, | 383 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, |
257 nullptr, this->machine()), | 384 nullptr, this->machine()), |
258 descriptor_(nullptr) { | 385 descriptor_(nullptr) { |
259 init_env(&env, sig); | 386 init_env(&env, sig); |
260 env.module = module; | 387 env.module = module; |
261 } | 388 } |
262 | 389 |
263 JSGraph jsgraph; | 390 JSGraph jsgraph; |
264 FunctionEnv env; | 391 FunctionEnv env; |
265 // The call descriptor is initialized when the function is compiled. | 392 // The call descriptor is initialized when the function is compiled. |
266 CallDescriptor* descriptor_; | 393 CallDescriptor* descriptor_; |
267 | 394 |
268 Isolate* isolate() { return main_isolate(); } | 395 Isolate* isolate() { return main_isolate(); } |
269 Graph* graph() const { return main_graph_; } | 396 Graph* graph() const { return main_graph_; } |
270 Zone* zone() const { return graph()->zone(); } | 397 Zone* zone() const { return graph()->zone(); } |
271 CommonOperatorBuilder* common() { return &main_common_; } | 398 CommonOperatorBuilder* common() { return &main_common_; } |
272 MachineOperatorBuilder* machine() { return &main_machine_; } | 399 MachineOperatorBuilder* machine() { return &main_machine_; } |
| 400 void InitializeDescriptor() { |
| 401 if (descriptor_ == nullptr) { |
| 402 descriptor_ = env.module->GetWasmCallDescriptor(main_zone(), env.sig); |
| 403 } |
| 404 } |
273 CallDescriptor* descriptor() { return descriptor_; } | 405 CallDescriptor* descriptor() { return descriptor_; } |
274 | 406 |
275 void Build(const byte* start, const byte* end) { | 407 void Build(const byte* start, const byte* end) { |
276 TestBuildingGraph(main_zone(), &jsgraph, &env, start, end); | 408 TestBuildingGraph(main_zone(), &jsgraph, &env, start, end); |
277 } | 409 } |
278 | 410 |
279 byte AllocateLocal(LocalType type) { | 411 byte AllocateLocal(LocalType type) { |
280 int result = static_cast<int>(env.total_locals); | 412 int result = static_cast<int>(env.total_locals); |
281 env.AddLocals(type, 1); | 413 env.AddLocals(type, 1); |
282 byte b = static_cast<byte>(result); | 414 byte b = static_cast<byte>(result); |
283 CHECK_EQ(result, b); | 415 CHECK_EQ(result, b); |
284 return b; | 416 return b; |
285 } | 417 } |
286 | 418 |
287 Handle<Code> Compile(ModuleEnv* module) { | 419 Handle<Code> Compile(ModuleEnv* module) { |
288 descriptor_ = module->GetWasmCallDescriptor(this->zone(), env.sig); | 420 InitializeDescriptor(); |
289 CompilationInfo info("wasm compile", this->isolate(), this->zone()); | 421 CompilationInfo info("wasm compile", this->isolate(), this->zone()); |
290 Handle<Code> result = | 422 Handle<Code> result = |
291 Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph()); | 423 Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph()); |
292 #ifdef ENABLE_DISASSEMBLER | 424 #ifdef ENABLE_DISASSEMBLER |
293 if (!result.is_null() && FLAG_print_opt_code) { | 425 if (!result.is_null() && FLAG_print_opt_code) { |
294 OFStream os(stdout); | 426 OFStream os(stdout); |
295 result->Disassemble("wasm code", os); | 427 result->Disassemble("wasm code", os); |
296 } | 428 } |
297 #endif | 429 #endif |
298 | 430 |
(...skipping 17 matching lines...) Expand all Loading... |
316 template <typename ReturnType> | 448 template <typename ReturnType> |
317 class WasmRunner { | 449 class WasmRunner { |
318 public: | 450 public: |
319 WasmRunner(MachineType p0 = MachineType::None(), | 451 WasmRunner(MachineType p0 = MachineType::None(), |
320 MachineType p1 = MachineType::None(), | 452 MachineType p1 = MachineType::None(), |
321 MachineType p2 = MachineType::None(), | 453 MachineType p2 = MachineType::None(), |
322 MachineType p3 = MachineType::None()) | 454 MachineType p3 = MachineType::None()) |
323 : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, | 455 : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, |
324 GetParameterCount(p0, p1, p2, p3), storage_), | 456 GetParameterCount(p0, p1, p2, p3), storage_), |
325 compiler_(&signature_), | 457 compiler_(&signature_), |
326 call_wrapper_(p0, p1, p2, p3), | |
327 compilation_done_(false) { | 458 compilation_done_(false) { |
328 int index = 0; | 459 int index = 0; |
329 MachineType ret = MachineTypeForC<ReturnType>(); | 460 MachineType ret = MachineTypeForC<ReturnType>(); |
330 if (ret != MachineType::None()) { | 461 if (ret != MachineType::None()) { |
331 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); | 462 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); |
332 } | 463 } |
333 if (p0 != MachineType::None()) | 464 if (p0 != MachineType::None()) |
334 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); | 465 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); |
335 if (p1 != MachineType::None()) | 466 if (p1 != MachineType::None()) |
336 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); | 467 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); |
337 if (p2 != MachineType::None()) | 468 if (p2 != MachineType::None()) |
338 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); | 469 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); |
339 if (p3 != MachineType::None()) | 470 if (p3 != MachineType::None()) |
340 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); | 471 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); |
| 472 |
| 473 compiler_.InitializeDescriptor(); |
| 474 wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3); |
341 } | 475 } |
342 | 476 |
343 | 477 |
344 FunctionEnv* env() { return &compiler_.env; } | 478 FunctionEnv* env() { return &compiler_.env; } |
345 | 479 |
346 | 480 |
347 // Builds a graph from the given Wasm code, and generates the machine | 481 // Builds a graph from the given Wasm code, and generates the machine |
348 // code and call wrapper for that graph. This method must not be called | 482 // code and call wrapper for that graph. This method must not be called |
349 // more than once. | 483 // more than once. |
350 void Build(const byte* start, const byte* end) { | 484 void Build(const byte* start, const byte* end) { |
351 DCHECK(!compilation_done_); | 485 DCHECK(!compilation_done_); |
352 compilation_done_ = true; | 486 compilation_done_ = true; |
353 // Build the TF graph. | 487 // Build the TF graph. |
354 compiler_.Build(start, end); | 488 compiler_.Build(start, end); |
355 // Generate code. | 489 // Generate code. |
356 Handle<Code> code = compiler_.Compile(env()->module); | 490 Handle<Code> code = compiler_.Compile(env()->module); |
357 | 491 |
358 // Construct the call wrapper. | 492 wrapper_.SetInnerCode(code); |
359 Node* inputs[5]; | |
360 int input_count = 0; | |
361 inputs[input_count++] = call_wrapper_.HeapConstant(code); | |
362 for (size_t i = 0; i < signature_.parameter_count(); i++) { | |
363 inputs[input_count++] = call_wrapper_.Parameter(i); | |
364 } | |
365 | |
366 call_wrapper_.Return(call_wrapper_.AddNode( | |
367 call_wrapper_.common()->Call(compiler_.descriptor()), input_count, | |
368 inputs)); | |
369 } | 493 } |
370 | 494 |
371 ReturnType Call() { return call_wrapper_.Call(); } | 495 ReturnType Call() { return Call(nullptr, nullptr, nullptr, nullptr); } |
372 | 496 |
373 template <typename P0> | 497 template <typename P0> |
374 ReturnType Call(P0 p0) { | 498 ReturnType Call(P0 p0) { |
375 return call_wrapper_.Call(p0); | 499 return Call(p0, nullptr, nullptr, nullptr); |
376 } | 500 } |
377 | 501 |
378 template <typename P0, typename P1> | 502 template <typename P0, typename P1> |
379 ReturnType Call(P0 p0, P1 p1) { | 503 ReturnType Call(P0 p0, P1 p1) { |
380 return call_wrapper_.Call(p0, p1); | 504 return Call(p0, p1, nullptr, nullptr); |
381 } | 505 } |
382 | 506 |
383 template <typename P0, typename P1, typename P2> | 507 template <typename P0, typename P1, typename P2> |
384 ReturnType Call(P0 p0, P1 p1, P2 p2) { | 508 ReturnType Call(P0 p0, P1 p1, P2 p2) { |
385 return call_wrapper_.Call(p0, p1, p2); | 509 return Call(p0, p1, p2, nullptr); |
386 } | 510 } |
387 | 511 |
388 template <typename P0, typename P1, typename P2, typename P3> | 512 template <typename P0, typename P1, typename P2, typename P3> |
389 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { | 513 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { |
390 return call_wrapper_.Call(p0, p1, p2, p3); | 514 CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(), |
| 515 wrapper_.GetWrapperCode(), wrapper_.signature()); |
| 516 ReturnType return_value; |
| 517 int32_t result = runner.Call<void*, void*, void*, void*, void*>( |
| 518 &p0, &p1, &p2, &p3, &return_value); |
| 519 CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result); |
| 520 return return_value; |
391 } | 521 } |
392 | 522 |
393 byte AllocateLocal(LocalType type) { | 523 byte AllocateLocal(LocalType type) { |
394 int result = static_cast<int>(env()->total_locals); | 524 int result = static_cast<int>(env()->total_locals); |
395 env()->AddLocals(type, 1); | 525 env()->AddLocals(type, 1); |
396 byte b = static_cast<byte>(result); | 526 byte b = static_cast<byte>(result); |
397 CHECK_EQ(result, b); | 527 CHECK_EQ(result, b); |
398 return b; | 528 return b; |
399 } | 529 } |
400 | 530 |
401 private: | 531 private: |
402 LocalType storage_[5]; | 532 LocalType storage_[5]; |
403 FunctionSig signature_; | 533 FunctionSig signature_; |
404 WasmFunctionCompiler compiler_; | 534 WasmFunctionCompiler compiler_; |
405 BufferedRawMachineAssemblerTester<ReturnType> call_wrapper_; | 535 WasmFunctionWrapper<ReturnType> wrapper_; |
406 bool compilation_done_; | 536 bool compilation_done_; |
407 | 537 |
408 static size_t GetParameterCount(MachineType p0, MachineType p1, | 538 static size_t GetParameterCount(MachineType p0, MachineType p1, |
409 MachineType p2, MachineType p3) { | 539 MachineType p2, MachineType p3) { |
410 if (p0 == MachineType::None()) return 0; | 540 if (p0 == MachineType::None()) return 0; |
411 if (p1 == MachineType::None()) return 1; | 541 if (p1 == MachineType::None()) return 1; |
412 if (p2 == MachineType::None()) return 2; | 542 if (p2 == MachineType::None()) return 2; |
413 if (p3 == MachineType::None()) return 3; | 543 if (p3 == MachineType::None()) return 3; |
414 return 4; | 544 return 4; |
415 } | 545 } |
416 }; | 546 }; |
417 | 547 |
418 } // namespace | 548 } // namespace |
419 | 549 |
420 #endif | 550 #endif |
OLD | NEW |