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 3 // found in the LICENSE file. 4 5 #include "src/compiler/wasm-compiler.h" 6 7 #include <memory> 8 9 #include "src/isolate-inl.h" 10 11 #include "src/base/platform/elapsed-timer.h" 12 #include "src/base/platform/platform.h" 13 14 #include "src/compiler/access-builder.h" 15 #include "src/compiler/common-operator.h" 16 #include "src/compiler/compiler-source-position-table.h" 17 #include "src/compiler/diamond.h" 18 #include "src/compiler/graph-visualizer.h" 19 #include "src/compiler/graph.h" 20 #include "src/compiler/instruction-selector.h" 21 #include "src/compiler/int64-lowering.h" 22 #include "src/compiler/js-graph.h" 23 #include "src/compiler/js-operator.h" 24 #include "src/compiler/linkage.h" 25 #include "src/compiler/machine-operator.h" 26 #include "src/compiler/node-matchers.h" 27 #include "src/compiler/pipeline.h" 28 #include "src/compiler/simd-scalar-lowering.h" 29 #include "src/compiler/zone-stats.h" 30 31 #include "src/code-factory.h" 32 #include "src/code-stubs.h" 33 #include "src/factory.h" 34 #include "src/log-inl.h" 35 36 #include "src/wasm/ast-decoder.h" 37 #include "src/wasm/wasm-module.h" 38 #include "src/wasm/wasm-opcodes.h" 39 40 // TODO(titzer): pull WASM_64 up to a common header. 41 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 42 #define WASM_64 1 43 #else 44 #define WASM_64 0 45 #endif 46 47 namespace v8 { 48 namespace internal { 49 namespace compiler { 50 51 namespace { 52 const Operator* UnsupportedOpcode(wasm::WasmOpcode opcode) { 53 V8_Fatal(__FILE__, __LINE__, "Unsupported opcode #%d:%s", opcode, 54 wasm::WasmOpcodes::OpcodeName(opcode)); 55 return nullptr; 56 } 57 58 void MergeControlToEnd(JSGraph* jsgraph, Node* node) { 59 Graph* g = jsgraph->graph(); 60 if (g->end()) { 61 NodeProperties::MergeControlToEnd(g, jsgraph->common(), node); 62 } else { 63 g->SetEnd(g->NewNode(jsgraph->common()->End(1), node)); 64 } 65 } 66 67 Node* BuildCallToRuntime(Runtime::FunctionId f, JSGraph* jsgraph, 68 Handle<Context> context, Node** parameters, 69 int parameter_count, Node** effect_ptr, 70 Node* control) { 71 // At the moment we only allow 2 parameters. If more parameters are needed, 72 // then the size of {inputs} below has to be increased accordingly. 73 DCHECK(parameter_count <= 2); 74 const Runtime::Function* fun = Runtime::FunctionForId(f); 75 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( 76 jsgraph->zone(), f, fun->nargs, Operator::kNoProperties, 77 CallDescriptor::kNoFlags); 78 // CEntryStubConstant nodes have to be created and cached in the main 79 // thread. At the moment this is only done for CEntryStubConstant(1). 80 DCHECK_EQ(1, fun->result_size); 81 Node* inputs[8]; 82 int count = 0; 83 inputs[count++] = jsgraph->CEntryStubConstant(fun->result_size); 84 for (int i = 0; i < parameter_count; i++) { 85 inputs[count++] = parameters[i]; 86 } 87 inputs[count++] = jsgraph->ExternalConstant( 88 ExternalReference(f, jsgraph->isolate())); // ref 89 inputs[count++] = jsgraph->Int32Constant(fun->nargs); // arity 90 inputs[count++] = jsgraph->HeapConstant(context); // context 91 inputs[count++] = *effect_ptr; 92 inputs[count++] = control; 93 94 Node* node = 95 jsgraph->graph()->NewNode(jsgraph->common()->Call(desc), count, inputs); 96 *effect_ptr = node; 97 return node; 98 } 99 100 } // namespace 101 102 // A helper that handles building graph fragments for trapping. 103 // To avoid generating a ton of redundant code that just calls the runtime 104 // to trap, we generate a per-trap-reason block of code that all trap sites 105 // in this function will branch to. 106 class WasmTrapHelper : public ZoneObject { 107 public: 108 explicit WasmTrapHelper(WasmGraphBuilder* builder) 109 : builder_(builder), 110 jsgraph_(builder->jsgraph()), 111 graph_(builder->jsgraph() ? builder->jsgraph()->graph() : nullptr) {} 112 113 // Make the current control path trap to unreachable. 114 void Unreachable(wasm::WasmCodePosition position) { 115 ConnectTrap(wasm::kTrapUnreachable, position); 116 } 117 118 // Always trap with the given reason. 119 void TrapAlways(wasm::TrapReason reason, wasm::WasmCodePosition position) { 120 ConnectTrap(reason, position); 121 } 122 123 // Add a check that traps if {node} is equal to {val}. 124 Node* TrapIfEq32(wasm::TrapReason reason, Node* node, int32_t val, 125 wasm::WasmCodePosition position) { 126 Int32Matcher m(node); 127 if (m.HasValue() && !m.Is(val)) return graph()->start(); 128 if (val == 0) { 129 AddTrapIfFalse(reason, node, position); 130 } else { 131 AddTrapIfTrue(reason, 132 graph()->NewNode(jsgraph()->machine()->Word32Equal(), node, 133 jsgraph()->Int32Constant(val)), 134 position); 135 } 136 return builder_->Control(); 137 } 138 139 // Add a check that traps if {node} is zero. 140 Node* ZeroCheck32(wasm::TrapReason reason, Node* node, 141 wasm::WasmCodePosition position) { 142 return TrapIfEq32(reason, node, 0, position); 143 } 144 145 // Add a check that traps if {node} is equal to {val}. 146 Node* TrapIfEq64(wasm::TrapReason reason, Node* node, int64_t val, 147 wasm::WasmCodePosition position) { 148 Int64Matcher m(node); 149 if (m.HasValue() && !m.Is(val)) return graph()->start(); 150 AddTrapIfTrue(reason, graph()->NewNode(jsgraph()->machine()->Word64Equal(), 151 node, jsgraph()->Int64Constant(val)), 152 position); 153 return builder_->Control(); 154 } 155 156 // Add a check that traps if {node} is zero. 157 Node* ZeroCheck64(wasm::TrapReason reason, Node* node, 158 wasm::WasmCodePosition position) { 159 return TrapIfEq64(reason, node, 0, position); 160 } 161 162 // Add a trap if {cond} is true. 163 void AddTrapIfTrue(wasm::TrapReason reason, Node* cond, 164 wasm::WasmCodePosition position) { 165 AddTrapIf(reason, cond, true, position); 166 } 167 168 // Add a trap if {cond} is false. 169 void AddTrapIfFalse(wasm::TrapReason reason, Node* cond, 170 wasm::WasmCodePosition position) { 171 AddTrapIf(reason, cond, false, position); 172 } 173 174 // Add a trap if {cond} is true or false according to {iftrue}. 175 void AddTrapIf(wasm::TrapReason reason, Node* cond, bool iftrue, 176 wasm::WasmCodePosition position) { 177 Node** effect_ptr = builder_->effect_; 178 Node** control_ptr = builder_->control_; 179 Node* before = *effect_ptr; 180 BranchHint hint = iftrue ? BranchHint::kFalse : BranchHint::kTrue; 181 Node* branch = graph()->NewNode(common()->Branch(hint), cond, *control_ptr); 182 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 183 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 184 185 *control_ptr = iftrue ? if_true : if_false; 186 ConnectTrap(reason, position); 187 *control_ptr = iftrue ? if_false : if_true; 188 *effect_ptr = before; 189 } 190 191 Node* GetTrapValue(wasm::FunctionSig* sig) { 192 if (sig->return_count() > 0) { 193 return GetTrapValue(sig->GetReturn()); 194 } else { 195 return jsgraph()->Int32Constant(0xdeadbeef); 196 } 197 } 198 199 Node* GetTrapValue(wasm::LocalType type) { 200 switch (type) { 201 case wasm::kAstI32: 202 return jsgraph()->Int32Constant(0xdeadbeef); 203 case wasm::kAstI64: 204 return jsgraph()->Int64Constant(0xdeadbeefdeadbeef); 205 case wasm::kAstF32: 206 return jsgraph()->Float32Constant(bit_cast<float>(0xdeadbeef)); 207 case wasm::kAstF64: 208 return jsgraph()->Float64Constant(bit_cast<double>(0xdeadbeefdeadbeef)); 209 break; 210 case wasm::kAstS128: 211 return builder_->CreateS128Value(0xdeadbeef); 212 break; 213 default: 214 UNREACHABLE(); 215 return nullptr; 216 } 217 } 218 219 private: 220 WasmGraphBuilder* builder_; 221 JSGraph* jsgraph_; 222 Graph* graph_; 223 Node* trap_merge_ = nullptr; 224 Node* trap_effect_; 225 Node* trap_reason_; 226 Node* trap_position_; 227 228 JSGraph* jsgraph() { return jsgraph_; } 229 Graph* graph() { return jsgraph_->graph(); } 230 CommonOperatorBuilder* common() { return jsgraph()->common(); } 231 232 void ConnectTrap(wasm::TrapReason reason, wasm::WasmCodePosition position) { 233 DCHECK(position != wasm::kNoCodePosition); 234 Node* reason_node = builder_->Int32Constant( 235 wasm::WasmOpcodes::TrapReasonToMessageId(reason)); 236 Node* position_node = builder_->Int32Constant(position); 237 if (trap_merge_ == nullptr) { 238 // Create trap code for the first time. 239 return BuildTrapCode(reason_node, position_node); 240 } 241 // Connect the current control and effect to the existing trap code. 242 builder_->AppendToMerge(trap_merge_, builder_->Control()); 243 builder_->AppendToPhi(trap_effect_, builder_->Effect()); 244 builder_->AppendToPhi(trap_reason_, reason_node); 245 builder_->AppendToPhi(trap_position_, position_node); 246 } 247 248 void BuildTrapCode(Node* reason_node, Node* position_node) { 249 Node* end; 250 Node** control_ptr = builder_->control_; 251 Node** effect_ptr = builder_->effect_; 252 wasm::ModuleEnv* module = builder_->module_; 253 DCHECK(trap_merge_ == NULL); 254 *control_ptr = trap_merge_ = 255 graph()->NewNode(common()->Merge(1), *control_ptr); 256 *effect_ptr = trap_effect_ = 257 graph()->NewNode(common()->EffectPhi(1), *effect_ptr, *control_ptr); 258 trap_reason_ = 259 graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 1), 260 reason_node, *control_ptr); 261 trap_position_ = 262 graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 1), 263 position_node, *control_ptr); 264 265 Node* trap_reason_smi = builder_->BuildChangeInt32ToSmi(trap_reason_); 266 Node* trap_position_smi = builder_->BuildChangeInt32ToSmi(trap_position_); 267 268 if (module && !module->instance->context.is_null()) { 269 Node* parameters[] = {trap_reason_smi, // message id 270 trap_position_smi}; // byte position 271 BuildCallToRuntime(Runtime::kThrowWasmError, jsgraph(), 272 module->instance->context, parameters, 273 arraysize(parameters), effect_ptr, *control_ptr); 274 } 275 if (false) { 276 // End the control flow with a throw 277 Node* thrw = 278 graph()->NewNode(common()->Throw(), jsgraph()->ZeroConstant(), 279 *effect_ptr, *control_ptr); 280 end = thrw; 281 } else { 282 // End the control flow with returning 0xdeadbeef 283 Node* ret_value = GetTrapValue(builder_->GetFunctionSignature()); 284 end = graph()->NewNode(jsgraph()->common()->Return(), 285 jsgraph()->Int32Constant(0), ret_value, 286 *effect_ptr, *control_ptr); 287 } 288 289 MergeControlToEnd(jsgraph(), end); 290 } 291 }; 292 293 WasmGraphBuilder::WasmGraphBuilder( 294 Zone* zone, JSGraph* jsgraph, wasm::FunctionSig* function_signature, 295 compiler::SourcePositionTable* source_position_table) 296 : zone_(zone), 297 jsgraph_(jsgraph), 298 module_(nullptr), 299 mem_buffer_(nullptr), 300 mem_size_(nullptr), 301 function_tables_(zone), 302 function_table_sizes_(zone), 303 control_(nullptr), 304 effect_(nullptr), 305 cur_buffer_(def_buffer_), 306 cur_bufsize_(kDefaultBufferSize), 307 trap_(new (zone) WasmTrapHelper(this)), 308 function_signature_(function_signature), 309 source_position_table_(source_position_table) { 310 DCHECK_NOT_NULL(jsgraph_); 311 } 312 313 Node* WasmGraphBuilder::Error() { return jsgraph()->Dead(); } 314 315 Node* WasmGraphBuilder::Start(unsigned params) { 316 Node* start = graph()->NewNode(jsgraph()->common()->Start(params)); 317 graph()->SetStart(start); 318 return start; 319 } 320 321 Node* WasmGraphBuilder::Param(unsigned index, wasm::LocalType type) { 322 return graph()->NewNode(jsgraph()->common()->Parameter(index), 323 graph()->start()); 324 } 325 326 Node* WasmGraphBuilder::Loop(Node* entry) { 327 return graph()->NewNode(jsgraph()->common()->Loop(1), entry); 328 } 329 330 Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) { 331 Node* terminate = 332 graph()->NewNode(jsgraph()->common()->Terminate(), effect, control); 333 MergeControlToEnd(jsgraph(), terminate); 334 return terminate; 335 } 336 337 unsigned WasmGraphBuilder::InputCount(Node* node) { 338 return static_cast<unsigned>(node->InputCount()); 339 } 340 341 bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) { 342 return phi && IrOpcode::IsPhiOpcode(phi->opcode()) && 343 NodeProperties::GetControlInput(phi) == merge; 344 } 345 346 bool WasmGraphBuilder::ThrowsException(Node* node, Node** if_success, 347 Node** if_exception) { 348 if (node->op()->HasProperty(compiler::Operator::kNoThrow)) { 349 return false; 350 } 351 352 *if_success = graph()->NewNode(jsgraph()->common()->IfSuccess(), node); 353 *if_exception = 354 graph()->NewNode(jsgraph()->common()->IfException(), node, node); 355 356 return true; 357 } 358 359 void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) { 360 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode())); 361 merge->AppendInput(jsgraph()->zone(), from); 362 int new_size = merge->InputCount(); 363 NodeProperties::ChangeOp( 364 merge, jsgraph()->common()->ResizeMergeOrPhi(merge->op(), new_size)); 365 } 366 367 void WasmGraphBuilder::AppendToPhi(Node* phi, Node* from) { 368 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode())); 369 int new_size = phi->InputCount(); 370 phi->InsertInput(jsgraph()->zone(), phi->InputCount() - 1, from); 371 NodeProperties::ChangeOp( 372 phi, jsgraph()->common()->ResizeMergeOrPhi(phi->op(), new_size)); 373 } 374 375 Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) { 376 return graph()->NewNode(jsgraph()->common()->Merge(count), count, controls); 377 } 378 379 Node* WasmGraphBuilder::Phi(wasm::LocalType type, unsigned count, Node** vals, 380 Node* control) { 381 DCHECK(IrOpcode::IsMergeOpcode(control->opcode())); 382 Node** buf = Realloc(vals, count, count + 1); 383 buf[count] = control; 384 return graph()->NewNode(jsgraph()->common()->Phi(type, count), count + 1, 385 buf); 386 } 387 388 Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects, 389 Node* control) { 390 DCHECK(IrOpcode::IsMergeOpcode(control->opcode())); 391 Node** buf = Realloc(effects, count, count + 1); 392 buf[count] = control; 393 return graph()->NewNode(jsgraph()->common()->EffectPhi(count), count + 1, 394 buf); 395 } 396 397 Node* WasmGraphBuilder::NumberConstant(int32_t value) { 398 return jsgraph()->Constant(value); 399 } 400 401 Node* WasmGraphBuilder::Uint32Constant(uint32_t value) { 402 return jsgraph()->Uint32Constant(value); 403 } 404 405 Node* WasmGraphBuilder::Int32Constant(int32_t value) { 406 return jsgraph()->Int32Constant(value); 407 } 408 409 Node* WasmGraphBuilder::Int64Constant(int64_t value) { 410 return jsgraph()->Int64Constant(value); 411 } 412 413 void WasmGraphBuilder::StackCheck(wasm::WasmCodePosition position, 414 Node** effect, Node** control) { 415 if (effect == nullptr) { 416 effect = effect_; 417 } 418 if (control == nullptr) { 419 control = control_; 420 } 421 // We do not generate stack checks for cctests. 422 if (module_ && !module_->instance->context.is_null()) { 423 Node* limit = graph()->NewNode( 424 jsgraph()->machine()->Load(MachineType::Pointer()), 425 jsgraph()->ExternalConstant( 426 ExternalReference::address_of_stack_limit(jsgraph()->isolate())), 427 jsgraph()->IntPtrConstant(0), *effect, *control); 428 Node* pointer = graph()->NewNode(jsgraph()->machine()->LoadStackPointer()); 429 430 Node* check = 431 graph()->NewNode(jsgraph()->machine()->UintLessThan(), limit, pointer); 432 433 Diamond stack_check(graph(), jsgraph()->common(), check, BranchHint::kTrue); 434 stack_check.Chain(*control); 435 Node* effect_true = *effect; 436 437 Node* effect_false; 438 // Generate a call to the runtime if there is a stack check failure. 439 { 440 Node* node = BuildCallToRuntime(Runtime::kStackGuard, jsgraph(), 441 module_->instance->context, nullptr, 0, 442 effect, stack_check.if_false); 443 effect_false = node; 444 } 445 446 Node* ephi = graph()->NewNode(jsgraph()->common()->EffectPhi(2), 447 effect_true, effect_false, stack_check.merge); 448 449 *control = stack_check.merge; 450 *effect = ephi; 451 } 452 } 453 454 Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right, 455 wasm::WasmCodePosition position) { 456 const Operator* op; 457 MachineOperatorBuilder* m = jsgraph()->machine(); 458 switch (opcode) { 459 case wasm::kExprI32Add: 460 op = m->Int32Add(); 461 break; 462 case wasm::kExprI32Sub: 463 op = m->Int32Sub(); 464 break; 465 case wasm::kExprI32Mul: 466 op = m->Int32Mul(); 467 break; 468 case wasm::kExprI32DivS: 469 return BuildI32DivS(left, right, position); 470 case wasm::kExprI32DivU: 471 return BuildI32DivU(left, right, position); 472 case wasm::kExprI32RemS: 473 return BuildI32RemS(left, right, position); 474 case wasm::kExprI32RemU: 475 return BuildI32RemU(left, right, position); 476 case wasm::kExprI32And: 477 op = m->Word32And(); 478 break; 479 case wasm::kExprI32Ior: 480 op = m->Word32Or(); 481 break; 482 case wasm::kExprI32Xor: 483 op = m->Word32Xor(); 484 break; 485 case wasm::kExprI32Shl: 486 op = m->Word32Shl(); 487 right = MaskShiftCount32(right); 488 break; 489 case wasm::kExprI32ShrU: 490 op = m->Word32Shr(); 491 right = MaskShiftCount32(right); 492 break; 493 case wasm::kExprI32ShrS: 494 op = m->Word32Sar(); 495 right = MaskShiftCount32(right); 496 break; 497 case wasm::kExprI32Ror: 498 op = m->Word32Ror(); 499 right = MaskShiftCount32(right); 500 break; 501 case wasm::kExprI32Rol: 502 right = MaskShiftCount32(right); 503 return BuildI32Rol(left, right); 504 case wasm::kExprI32Eq: 505 op = m->Word32Equal(); 506 break; 507 case wasm::kExprI32Ne: 508 return Invert(Binop(wasm::kExprI32Eq, left, right)); 509 case wasm::kExprI32LtS: 510 op = m->Int32LessThan(); 511 break; 512 case wasm::kExprI32LeS: 513 op = m->Int32LessThanOrEqual(); 514 break; 515 case wasm::kExprI32LtU: 516 op = m->Uint32LessThan(); 517 break; 518 case wasm::kExprI32LeU: 519 op = m->Uint32LessThanOrEqual(); 520 break; 521 case wasm::kExprI32GtS: 522 op = m->Int32LessThan(); 523 std::swap(left, right); 524 break; 525 case wasm::kExprI32GeS: 526 op = m->Int32LessThanOrEqual(); 527 std::swap(left, right); 528 break; 529 case wasm::kExprI32GtU: 530 op = m->Uint32LessThan(); 531 std::swap(left, right); 532 break; 533 case wasm::kExprI32GeU: 534 op = m->Uint32LessThanOrEqual(); 535 std::swap(left, right); 536 break; 537 case wasm::kExprI64And: 538 op = m->Word64And(); 539 break; 540 case wasm::kExprI64Add: 541 op = m->Int64Add(); 542 break; 543 case wasm::kExprI64Sub: 544 op = m->Int64Sub(); 545 break; 546 case wasm::kExprI64Mul: 547 op = m->Int64Mul(); 548 break; 549 case wasm::kExprI64DivS: 550 return BuildI64DivS(left, right, position); 551 case wasm::kExprI64DivU: 552 return BuildI64DivU(left, right, position); 553 case wasm::kExprI64RemS: 554 return BuildI64RemS(left, right, position); 555 case wasm::kExprI64RemU: 556 return BuildI64RemU(left, right, position); 557 case wasm::kExprI64Ior: 558 op = m->Word64Or(); 559 break; 560 case wasm::kExprI64Xor: 561 op = m->Word64Xor(); 562 break; 563 case wasm::kExprI64Shl: 564 op = m->Word64Shl(); 565 right = MaskShiftCount64(right); 566 break; 567 case wasm::kExprI64ShrU: 568 op = m->Word64Shr(); 569 right = MaskShiftCount64(right); 570 break; 571 case wasm::kExprI64ShrS: 572 op = m->Word64Sar(); 573 right = MaskShiftCount64(right); 574 break; 575 case wasm::kExprI64Eq: 576 op = m->Word64Equal(); 577 break; 578 case wasm::kExprI64Ne: 579 return Invert(Binop(wasm::kExprI64Eq, left, right)); 580 case wasm::kExprI64LtS: 581 op = m->Int64LessThan(); 582 break; 583 case wasm::kExprI64LeS: 584 op = m->Int64LessThanOrEqual(); 585 break; 586 case wasm::kExprI64LtU: 587 op = m->Uint64LessThan(); 588 break; 589 case wasm::kExprI64LeU: 590 op = m->Uint64LessThanOrEqual(); 591 break; 592 case wasm::kExprI64GtS: 593 op = m->Int64LessThan(); 594 std::swap(left, right); 595 break; 596 case wasm::kExprI64GeS: 597 op = m->Int64LessThanOrEqual(); 598 std::swap(left, right); 599 break; 600 case wasm::kExprI64GtU: 601 op = m->Uint64LessThan(); 602 std::swap(left, right); 603 break; 604 case wasm::kExprI64GeU: 605 op = m->Uint64LessThanOrEqual(); 606 std::swap(left, right); 607 break; 608 case wasm::kExprI64Ror: 609 op = m->Word64Ror(); 610 right = MaskShiftCount64(right); 611 break; 612 case wasm::kExprI64Rol: 613 return BuildI64Rol(left, right); 614 case wasm::kExprF32CopySign: 615 return BuildF32CopySign(left, right); 616 case wasm::kExprF64CopySign: 617 return BuildF64CopySign(left, right); 618 case wasm::kExprF32Add: 619 op = m->Float32Add(); 620 break; 621 case wasm::kExprF32Sub: 622 op = m->Float32Sub(); 623 break; 624 case wasm::kExprF32Mul: 625 op = m->Float32Mul(); 626 break; 627 case wasm::kExprF32Div: 628 op = m->Float32Div(); 629 break; 630 case wasm::kExprF32Eq: 631 op = m->Float32Equal(); 632 break; 633 case wasm::kExprF32Ne: 634 return Invert(Binop(wasm::kExprF32Eq, left, right)); 635 case wasm::kExprF32Lt: 636 op = m->Float32LessThan(); 637 break; 638 case wasm::kExprF32Ge: 639 op = m->Float32LessThanOrEqual(); 640 std::swap(left, right); 641 break; 642 case wasm::kExprF32Gt: 643 op = m->Float32LessThan(); 644 std::swap(left, right); 645 break; 646 case wasm::kExprF32Le: 647 op = m->Float32LessThanOrEqual(); 648 break; 649 case wasm::kExprF64Add: 650 op = m->Float64Add(); 651 break; 652 case wasm::kExprF64Sub: 653 op = m->Float64Sub(); 654 break; 655 case wasm::kExprF64Mul: 656 op = m->Float64Mul(); 657 break; 658 case wasm::kExprF64Div: 659 op = m->Float64Div(); 660 break; 661 case wasm::kExprF64Eq: 662 op = m->Float64Equal(); 663 break; 664 case wasm::kExprF64Ne: 665 return Invert(Binop(wasm::kExprF64Eq, left, right)); 666 case wasm::kExprF64Lt: 667 op = m->Float64LessThan(); 668 break; 669 case wasm::kExprF64Le: 670 op = m->Float64LessThanOrEqual(); 671 break; 672 case wasm::kExprF64Gt: 673 op = m->Float64LessThan(); 674 std::swap(left, right); 675 break; 676 case wasm::kExprF64Ge: 677 op = m->Float64LessThanOrEqual(); 678 std::swap(left, right); 679 break; 680 case wasm::kExprF32Min: 681 op = m->Float32Min(); 682 break; 683 case wasm::kExprF64Min: 684 op = m->Float64Min(); 685 break; 686 case wasm::kExprF32Max: 687 op = m->Float32Max(); 688 break; 689 case wasm::kExprF64Max: 690 op = m->Float64Max(); 691 break; 692 case wasm::kExprF64Pow: 693 return BuildF64Pow(left, right); 694 case wasm::kExprF64Atan2: 695 op = m->Float64Atan2(); 696 break; 697 case wasm::kExprF64Mod: 698 return BuildF64Mod(left, right); 699 case wasm::kExprI32AsmjsDivS: 700 return BuildI32AsmjsDivS(left, right); 701 case wasm::kExprI32AsmjsDivU: 702 return BuildI32AsmjsDivU(left, right); 703 case wasm::kExprI32AsmjsRemS: 704 return BuildI32AsmjsRemS(left, right); 705 case wasm::kExprI32AsmjsRemU: 706 return BuildI32AsmjsRemU(left, right); 707 case wasm::kExprI32AsmjsStoreMem8: 708 return BuildAsmjsStoreMem(MachineType::Int8(), left, right); 709 case wasm::kExprI32AsmjsStoreMem16: 710 return BuildAsmjsStoreMem(MachineType::Int16(), left, right); 711 case wasm::kExprI32AsmjsStoreMem: 712 return BuildAsmjsStoreMem(MachineType::Int32(), left, right); 713 case wasm::kExprF32AsmjsStoreMem: 714 return BuildAsmjsStoreMem(MachineType::Float32(), left, right); 715 case wasm::kExprF64AsmjsStoreMem: 716 return BuildAsmjsStoreMem(MachineType::Float64(), left, right); 717 default: 718 op = UnsupportedOpcode(opcode); 719 } 720 return graph()->NewNode(op, left, right); 721 } 722 723 Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input, 724 wasm::WasmCodePosition position) { 725 const Operator* op; 726 MachineOperatorBuilder* m = jsgraph()->machine(); 727 switch (opcode) { 728 case wasm::kExprI32Eqz: 729 op = m->Word32Equal(); 730 return graph()->NewNode(op, input, jsgraph()->Int32Constant(0)); 731 case wasm::kExprF32Abs: 732 op = m->Float32Abs(); 733 break; 734 case wasm::kExprF32Neg: { 735 op = m->Float32Neg(); 736 break; 737 } 738 case wasm::kExprF32Sqrt: 739 op = m->Float32Sqrt(); 740 break; 741 case wasm::kExprF64Abs: 742 op = m->Float64Abs(); 743 break; 744 case wasm::kExprF64Neg: { 745 op = m->Float64Neg(); 746 break; 747 } 748 case wasm::kExprF64Sqrt: 749 op = m->Float64Sqrt(); 750 break; 751 case wasm::kExprI32SConvertF64: 752 return BuildI32SConvertF64(input, position); 753 case wasm::kExprI32UConvertF64: 754 return BuildI32UConvertF64(input, position); 755 case wasm::kExprI32AsmjsSConvertF64: 756 return BuildI32AsmjsSConvertF64(input); 757 case wasm::kExprI32AsmjsUConvertF64: 758 return BuildI32AsmjsUConvertF64(input); 759 case wasm::kExprF32ConvertF64: 760 op = m->TruncateFloat64ToFloat32(); 761 break; 762 case wasm::kExprF64SConvertI32: 763 op = m->ChangeInt32ToFloat64(); 764 break; 765 case wasm::kExprF64UConvertI32: 766 op = m->ChangeUint32ToFloat64(); 767 break; 768 case wasm::kExprF32SConvertI32: 769 op = m->RoundInt32ToFloat32(); 770 break; 771 case wasm::kExprF32UConvertI32: 772 op = m->RoundUint32ToFloat32(); 773 break; 774 case wasm::kExprI32SConvertF32: 775 return BuildI32SConvertF32(input, position); 776 case wasm::kExprI32UConvertF32: 777 return BuildI32UConvertF32(input, position); 778 case wasm::kExprI32AsmjsSConvertF32: 779 return BuildI32AsmjsSConvertF32(input); 780 case wasm::kExprI32AsmjsUConvertF32: 781 return BuildI32AsmjsUConvertF32(input); 782 case wasm::kExprF64ConvertF32: 783 op = m->ChangeFloat32ToFloat64(); 784 break; 785 case wasm::kExprF32ReinterpretI32: 786 op = m->BitcastInt32ToFloat32(); 787 break; 788 case wasm::kExprI32ReinterpretF32: 789 op = m->BitcastFloat32ToInt32(); 790 break; 791 case wasm::kExprI32Clz: 792 op = m->Word32Clz(); 793 break; 794 case wasm::kExprI32Ctz: { 795 if (m->Word32Ctz().IsSupported()) { 796 op = m->Word32Ctz().op(); 797 break; 798 } else if (m->Word32ReverseBits().IsSupported()) { 799 Node* reversed = graph()->NewNode(m->Word32ReverseBits().op(), input); 800 Node* result = graph()->NewNode(m->Word32Clz(), reversed); 801 return result; 802 } else { 803 return BuildI32Ctz(input); 804 } 805 } 806 case wasm::kExprI32Popcnt: { 807 if (m->Word32Popcnt().IsSupported()) { 808 op = m->Word32Popcnt().op(); 809 break; 810 } else { 811 return BuildI32Popcnt(input); 812 } 813 } 814 case wasm::kExprF32Floor: { 815 if (!m->Float32RoundDown().IsSupported()) return BuildF32Floor(input); 816 op = m->Float32RoundDown().op(); 817 break; 818 } 819 case wasm::kExprF32Ceil: { 820 if (!m->Float32RoundUp().IsSupported()) return BuildF32Ceil(input); 821 op = m->Float32RoundUp().op(); 822 break; 823 } 824 case wasm::kExprF32Trunc: { 825 if (!m->Float32RoundTruncate().IsSupported()) return BuildF32Trunc(input); 826 op = m->Float32RoundTruncate().op(); 827 break; 828 } 829 case wasm::kExprF32NearestInt: { 830 if (!m->Float32RoundTiesEven().IsSupported()) 831 return BuildF32NearestInt(input); 832 op = m->Float32RoundTiesEven().op(); 833 break; 834 } 835 case wasm::kExprF64Floor: { 836 if (!m->Float64RoundDown().IsSupported()) return BuildF64Floor(input); 837 op = m->Float64RoundDown().op(); 838 break; 839 } 840 case wasm::kExprF64Ceil: { 841 if (!m->Float64RoundUp().IsSupported()) return BuildF64Ceil(input); 842 op = m->Float64RoundUp().op(); 843 break; 844 } 845 case wasm::kExprF64Trunc: { 846 if (!m->Float64RoundTruncate().IsSupported()) return BuildF64Trunc(input); 847 op = m->Float64RoundTruncate().op(); 848 break; 849 } 850 case wasm::kExprF64NearestInt: { 851 if (!m->Float64RoundTiesEven().IsSupported()) 852 return BuildF64NearestInt(input); 853 op = m->Float64RoundTiesEven().op(); 854 break; 855 } 856 case wasm::kExprF64Acos: { 857 return BuildF64Acos(input); 858 } 859 case wasm::kExprF64Asin: { 860 return BuildF64Asin(input); 861 } 862 case wasm::kExprF64Atan: 863 op = m->Float64Atan(); 864 break; 865 case wasm::kExprF64Cos: { 866 op = m->Float64Cos(); 867 break; 868 } 869 case wasm::kExprF64Sin: { 870 op = m->Float64Sin(); 871 break; 872 } 873 case wasm::kExprF64Tan: { 874 op = m->Float64Tan(); 875 break; 876 } 877 case wasm::kExprF64Exp: { 878 op = m->Float64Exp(); 879 break; 880 } 881 case wasm::kExprF64Log: 882 op = m->Float64Log(); 883 break; 884 case wasm::kExprI32ConvertI64: 885 op = m->TruncateInt64ToInt32(); 886 break; 887 case wasm::kExprI64SConvertI32: 888 op = m->ChangeInt32ToInt64(); 889 break; 890 case wasm::kExprI64UConvertI32: 891 op = m->ChangeUint32ToUint64(); 892 break; 893 case wasm::kExprF64ReinterpretI64: 894 op = m->BitcastInt64ToFloat64(); 895 break; 896 case wasm::kExprI64ReinterpretF64: 897 op = m->BitcastFloat64ToInt64(); 898 break; 899 case wasm::kExprI64Clz: 900 op = m->Word64Clz(); 901 break; 902 case wasm::kExprI64Ctz: { 903 OptionalOperator ctz64 = m->Word64Ctz(); 904 if (ctz64.IsSupported()) { 905 op = ctz64.op(); 906 break; 907 } else if (m->Is32() && m->Word32Ctz().IsSupported()) { 908 op = ctz64.placeholder(); 909 break; 910 } else if (m->Word64ReverseBits().IsSupported()) { 911 Node* reversed = graph()->NewNode(m->Word64ReverseBits().op(), input); 912 Node* result = graph()->NewNode(m->Word64Clz(), reversed); 913 return result; 914 } else { 915 return BuildI64Ctz(input); 916 } 917 } 918 case wasm::kExprI64Popcnt: { 919 OptionalOperator popcnt64 = m->Word64Popcnt(); 920 if (popcnt64.IsSupported()) { 921 op = popcnt64.op(); 922 } else if (m->Is32() && m->Word32Popcnt().IsSupported()) { 923 op = popcnt64.placeholder(); 924 } else { 925 return BuildI64Popcnt(input); 926 } 927 break; 928 } 929 case wasm::kExprI64Eqz: 930 op = m->Word64Equal(); 931 return graph()->NewNode(op, input, jsgraph()->Int64Constant(0)); 932 case wasm::kExprF32SConvertI64: 933 if (m->Is32()) { 934 return BuildF32SConvertI64(input); 935 } 936 op = m->RoundInt64ToFloat32(); 937 break; 938 case wasm::kExprF32UConvertI64: 939 if (m->Is32()) { 940 return BuildF32UConvertI64(input); 941 } 942 op = m->RoundUint64ToFloat32(); 943 break; 944 case wasm::kExprF64SConvertI64: 945 if (m->Is32()) { 946 return BuildF64SConvertI64(input); 947 } 948 op = m->RoundInt64ToFloat64(); 949 break; 950 case wasm::kExprF64UConvertI64: 951 if (m->Is32()) { 952 return BuildF64UConvertI64(input); 953 } 954 op = m->RoundUint64ToFloat64(); 955 break; 956 case wasm::kExprI64SConvertF32: 957 return BuildI64SConvertF32(input, position); 958 case wasm::kExprI64SConvertF64: 959 return BuildI64SConvertF64(input, position); 960 case wasm::kExprI64UConvertF32: 961 return BuildI64UConvertF32(input, position); 962 case wasm::kExprI64UConvertF64: 963 return BuildI64UConvertF64(input, position); 964 case wasm::kExprI32AsmjsLoadMem8S: 965 return BuildAsmjsLoadMem(MachineType::Int8(), input); 966 case wasm::kExprI32AsmjsLoadMem8U: 967 return BuildAsmjsLoadMem(MachineType::Uint8(), input); 968 case wasm::kExprI32AsmjsLoadMem16S: 969 return BuildAsmjsLoadMem(MachineType::Int16(), input); 970 case wasm::kExprI32AsmjsLoadMem16U: 971 return BuildAsmjsLoadMem(MachineType::Uint16(), input); 972 case wasm::kExprI32AsmjsLoadMem: 973 return BuildAsmjsLoadMem(MachineType::Int32(), input); 974 case wasm::kExprF32AsmjsLoadMem: 975 return BuildAsmjsLoadMem(MachineType::Float32(), input); 976 case wasm::kExprF64AsmjsLoadMem: 977 return BuildAsmjsLoadMem(MachineType::Float64(), input); 978 default: 979 op = UnsupportedOpcode(opcode); 980 } 981 return graph()->NewNode(op, input); 982 } 983 984 Node* WasmGraphBuilder::Float32Constant(float value) { 985 return jsgraph()->Float32Constant(value); 986 } 987 988 Node* WasmGraphBuilder::Float64Constant(double value) { 989 return jsgraph()->Float64Constant(value); 990 } 991 992 Node* WasmGraphBuilder::HeapConstant(Handle<HeapObject> value) { 993 return jsgraph()->HeapConstant(value); 994 } 995 996 namespace { 997 Node* Branch(JSGraph* jsgraph, Node* cond, Node** true_node, Node** false_node, 998 Node* control, BranchHint hint) { 999 DCHECK_NOT_NULL(cond); 1000 DCHECK_NOT_NULL(control); 1001 Node* branch = 1002 jsgraph->graph()->NewNode(jsgraph->common()->Branch(hint), cond, control); 1003 *true_node = jsgraph->graph()->NewNode(jsgraph->common()->IfTrue(), branch); 1004 *false_node = jsgraph->graph()->NewNode(jsgraph->common()->IfFalse(), branch); 1005 return branch; 1006 } 1007 } // namespace 1008 1009 Node* WasmGraphBuilder::BranchNoHint(Node* cond, Node** true_node, 1010 Node** false_node) { 1011 return Branch(jsgraph(), cond, true_node, false_node, *control_, 1012 BranchHint::kNone); 1013 } 1014 1015 Node* WasmGraphBuilder::BranchExpectTrue(Node* cond, Node** true_node, 1016 Node** false_node) { 1017 return Branch(jsgraph(), cond, true_node, false_node, *control_, 1018 BranchHint::kTrue); 1019 } 1020 1021 Node* WasmGraphBuilder::BranchExpectFalse(Node* cond, Node** true_node, 1022 Node** false_node) { 1023 return Branch(jsgraph(), cond, true_node, false_node, *control_, 1024 BranchHint::kFalse); 1025 } 1026 1027 Node* WasmGraphBuilder::Switch(unsigned count, Node* key) { 1028 return graph()->NewNode(jsgraph()->common()->Switch(count), key, *control_); 1029 } 1030 1031 Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) { 1032 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode()); 1033 return graph()->NewNode(jsgraph()->common()->IfValue(value), sw); 1034 } 1035 1036 Node* WasmGraphBuilder::IfDefault(Node* sw) { 1037 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode()); 1038 return graph()->NewNode(jsgraph()->common()->IfDefault(), sw); 1039 } 1040 1041 Node* WasmGraphBuilder::Return(unsigned count, Node** vals) { 1042 DCHECK_NOT_NULL(*control_); 1043 DCHECK_NOT_NULL(*effect_); 1044 1045 Node** buf = Realloc(vals, count, count + 3); 1046 memmove(buf + 1, buf, sizeof(void*) * count); 1047 buf[0] = jsgraph()->Int32Constant(0); 1048 buf[count + 1] = *effect_; 1049 buf[count + 2] = *control_; 1050 Node* ret = 1051 graph()->NewNode(jsgraph()->common()->Return(count), count + 3, buf); 1052 1053 MergeControlToEnd(jsgraph(), ret); 1054 return ret; 1055 } 1056 1057 Node* WasmGraphBuilder::ReturnVoid() { return Return(0, Buffer(0)); } 1058 1059 Node* WasmGraphBuilder::Unreachable(wasm::WasmCodePosition position) { 1060 trap_->Unreachable(position); 1061 return nullptr; 1062 } 1063 1064 Node* WasmGraphBuilder::MaskShiftCount32(Node* node) { 1065 static const int32_t kMask32 = 0x1f; 1066 if (!jsgraph()->machine()->Word32ShiftIsSafe()) { 1067 // Shifts by constants are so common we pattern-match them here. 1068 Int32Matcher match(node); 1069 if (match.HasValue()) { 1070 int32_t masked = (match.Value() & kMask32); 1071 if (match.Value() != masked) node = jsgraph()->Int32Constant(masked); 1072 } else { 1073 node = graph()->NewNode(jsgraph()->machine()->Word32And(), node, 1074 jsgraph()->Int32Constant(kMask32)); 1075 } 1076 } 1077 return node; 1078 } 1079 1080 Node* WasmGraphBuilder::MaskShiftCount64(Node* node) { 1081 static const int64_t kMask64 = 0x3f; 1082 if (!jsgraph()->machine()->Word32ShiftIsSafe()) { 1083 // Shifts by constants are so common we pattern-match them here. 1084 Int64Matcher match(node); 1085 if (match.HasValue()) { 1086 int64_t masked = (match.Value() & kMask64); 1087 if (match.Value() != masked) node = jsgraph()->Int64Constant(masked); 1088 } else { 1089 node = graph()->NewNode(jsgraph()->machine()->Word64And(), node, 1090 jsgraph()->Int64Constant(kMask64)); 1091 } 1092 } 1093 return node; 1094 } 1095 1096 static bool ReverseBytesSupported(MachineOperatorBuilder* m, 1097 size_t size_in_bytes) { 1098 switch (size_in_bytes) { 1099 case 4: 1100 return m->Word32ReverseBytes().IsSupported(); 1101 case 8: 1102 return m->Word64ReverseBytes().IsSupported(); 1103 default: 1104 break; 1105 } 1106 return false; 1107 } 1108 1109 Node* WasmGraphBuilder::BuildChangeEndianness(Node* node, MachineType memtype, 1110 wasm::LocalType wasmtype) { 1111 Node* result; 1112 Node* value = node; 1113 MachineOperatorBuilder* m = jsgraph()->machine(); 1114 int valueSizeInBytes = 1 << ElementSizeLog2Of(memtype.representation()); 1115 int valueSizeInBits = 8 * valueSizeInBytes; 1116 bool isFloat = false; 1117 1118 switch (memtype.representation()) { 1119 case MachineRepresentation::kFloat64: 1120 value = graph()->NewNode(m->BitcastFloat64ToInt64(), node); 1121 isFloat = true; 1122 case MachineRepresentation::kWord64: 1123 result = jsgraph()->Int64Constant(0); 1124 break; 1125 case MachineRepresentation::kFloat32: 1126 value = graph()->NewNode(m->BitcastFloat32ToInt32(), node); 1127 isFloat = true; 1128 case MachineRepresentation::kWord32: 1129 case MachineRepresentation::kWord16: 1130 result = jsgraph()->Int32Constant(0); 1131 break; 1132 case MachineRepresentation::kWord8: 1133 // No need to change endianness for byte size, return original node 1134 return node; 1135 break; 1136 default: 1137 UNREACHABLE(); 1138 break; 1139 } 1140 1141 int i; 1142 uint32_t shiftCount; 1143 1144 if (ReverseBytesSupported(m, valueSizeInBytes < 4 ? 4 : valueSizeInBytes)) { 1145 switch (valueSizeInBytes) { 1146 case 2: 1147 result = 1148 graph()->NewNode(m->Word32ReverseBytes().op(), 1149 graph()->NewNode(m->Word32Shl(), value, 1150 jsgraph()->Int32Constant(16))); 1151 break; 1152 case 4: 1153 result = graph()->NewNode(m->Word32ReverseBytes().op(), value); 1154 break; 1155 case 8: 1156 result = graph()->NewNode(m->Word64ReverseBytes().op(), value); 1157 break; 1158 default: 1159 UNREACHABLE(); 1160 } 1161 } else { 1162 for (i = 0, shiftCount = valueSizeInBits - 8; i < valueSizeInBits / 2; 1163 i += 8, shiftCount -= 16) { 1164 Node* shiftLower; 1165 Node* shiftHigher; 1166 Node* lowerByte; 1167 Node* higherByte; 1168 1169 DCHECK(shiftCount > 0); 1170 DCHECK((shiftCount + 8) % 16 == 0); 1171 1172 if (valueSizeInBits > 32) { 1173 shiftLower = graph()->NewNode(m->Word64Shl(), value, 1174 jsgraph()->Int64Constant(shiftCount)); 1175 shiftHigher = graph()->NewNode(m->Word64Shr(), value, 1176 jsgraph()->Int64Constant(shiftCount)); 1177 lowerByte = graph()->NewNode( 1178 m->Word64And(), shiftLower, 1179 jsgraph()->Int64Constant(static_cast<uint64_t>(0xFF) 1180 << (valueSizeInBits - 8 - i))); 1181 higherByte = graph()->NewNode( 1182 m->Word64And(), shiftHigher, 1183 jsgraph()->Int64Constant(static_cast<uint64_t>(0xFF) << i)); 1184 result = graph()->NewNode(m->Word64Or(), result, lowerByte); 1185 result = graph()->NewNode(m->Word64Or(), result, higherByte); 1186 } else { 1187 shiftLower = graph()->NewNode(m->Word32Shl(), value, 1188 jsgraph()->Int32Constant(shiftCount)); 1189 shiftHigher = graph()->NewNode(m->Word32Shr(), value, 1190 jsgraph()->Int32Constant(shiftCount)); 1191 lowerByte = graph()->NewNode( 1192 m->Word32And(), shiftLower, 1193 jsgraph()->Int32Constant(static_cast<uint32_t>(0xFF) 1194 << (valueSizeInBits - 8 - i))); 1195 higherByte = graph()->NewNode( 1196 m->Word32And(), shiftHigher, 1197 jsgraph()->Int32Constant(static_cast<uint32_t>(0xFF) << i)); 1198 result = graph()->NewNode(m->Word32Or(), result, lowerByte); 1199 result = graph()->NewNode(m->Word32Or(), result, higherByte); 1200 } 1201 } 1202 } 1203 1204 if (isFloat) { 1205 switch (memtype.representation()) { 1206 case MachineRepresentation::kFloat64: 1207 result = graph()->NewNode(m->BitcastInt64ToFloat64(), result); 1208 break; 1209 case MachineRepresentation::kFloat32: 1210 result = graph()->NewNode(m->BitcastInt32ToFloat32(), result); 1211 break; 1212 default: 1213 UNREACHABLE(); 1214 break; 1215 } 1216 } 1217 1218 // We need to sign extend the value 1219 if (memtype.IsSigned()) { 1220 DCHECK(!isFloat); 1221 if (valueSizeInBits < 32) { 1222 Node* shiftBitCount; 1223 // Perform sign extension using following trick 1224 // result = (x << machine_width - type_width) >> (machine_width - 1225 // type_width) 1226 if (wasmtype == wasm::kAstI64) { 1227 shiftBitCount = jsgraph()->Int32Constant(64 - valueSizeInBits); 1228 result = graph()->NewNode( 1229 m->Word64Sar(), 1230 graph()->NewNode(m->Word64Shl(), 1231 graph()->NewNode(m->ChangeInt32ToInt64(), result), 1232 shiftBitCount), 1233 shiftBitCount); 1234 } else if (wasmtype == wasm::kAstI32) { 1235 shiftBitCount = jsgraph()->Int32Constant(32 - valueSizeInBits); 1236 result = graph()->NewNode( 1237 m->Word32Sar(), 1238 graph()->NewNode(m->Word32Shl(), result, shiftBitCount), 1239 shiftBitCount); 1240 } 1241 } 1242 } 1243 1244 return result; 1245 } 1246 1247 Node* WasmGraphBuilder::BuildF32CopySign(Node* left, Node* right) { 1248 Node* result = Unop( 1249 wasm::kExprF32ReinterpretI32, 1250 Binop(wasm::kExprI32Ior, 1251 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, left), 1252 jsgraph()->Int32Constant(0x7fffffff)), 1253 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, right), 1254 jsgraph()->Int32Constant(0x80000000)))); 1255 1256 return result; 1257 } 1258 1259 Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) { 1260 #if WASM_64 1261 Node* result = Unop( 1262 wasm::kExprF64ReinterpretI64, 1263 Binop(wasm::kExprI64Ior, 1264 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, left), 1265 jsgraph()->Int64Constant(0x7fffffffffffffff)), 1266 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, right), 1267 jsgraph()->Int64Constant(0x8000000000000000)))); 1268 1269 return result; 1270 #else 1271 MachineOperatorBuilder* m = jsgraph()->machine(); 1272 1273 Node* high_word_left = graph()->NewNode(m->Float64ExtractHighWord32(), left); 1274 Node* high_word_right = 1275 graph()->NewNode(m->Float64ExtractHighWord32(), right); 1276 1277 Node* new_high_word = 1278 Binop(wasm::kExprI32Ior, Binop(wasm::kExprI32And, high_word_left, 1279 jsgraph()->Int32Constant(0x7fffffff)), 1280 Binop(wasm::kExprI32And, high_word_right, 1281 jsgraph()->Int32Constant(0x80000000))); 1282 1283 return graph()->NewNode(m->Float64InsertHighWord32(), left, new_high_word); 1284 #endif 1285 } 1286 1287 Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input, 1288 wasm::WasmCodePosition position) { 1289 MachineOperatorBuilder* m = jsgraph()->machine(); 1290 // Truncation of the input value is needed for the overflow check later. 1291 Node* trunc = Unop(wasm::kExprF32Trunc, input); 1292 Node* result = graph()->NewNode(m->TruncateFloat32ToInt32(), trunc); 1293 1294 // Convert the result back to f64. If we end up at a different value than the 1295 // truncated input value, then there has been an overflow and we trap. 1296 Node* check = Unop(wasm::kExprF32SConvertI32, result); 1297 Node* overflow = Binop(wasm::kExprF32Ne, trunc, check); 1298 trap_->AddTrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position); 1299 1300 return result; 1301 } 1302 1303 Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input, 1304 wasm::WasmCodePosition position) { 1305 MachineOperatorBuilder* m = jsgraph()->machine(); 1306 // Truncation of the input value is needed for the overflow check later. 1307 Node* trunc = Unop(wasm::kExprF64Trunc, input); 1308 Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), trunc); 1309 1310 // Convert the result back to f64. If we end up at a different value than the 1311 // truncated input value, then there has been an overflow and we trap. 1312 Node* check = Unop(wasm::kExprF64SConvertI32, result); 1313 Node* overflow = Binop(wasm::kExprF64Ne, trunc, check); 1314 trap_->AddTrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position); 1315 1316 return result; 1317 } 1318 1319 Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input, 1320 wasm::WasmCodePosition position) { 1321 MachineOperatorBuilder* m = jsgraph()->machine(); 1322 // Truncation of the input value is needed for the overflow check later. 1323 Node* trunc = Unop(wasm::kExprF32Trunc, input); 1324 Node* result = graph()->NewNode(m->TruncateFloat32ToUint32(), trunc); 1325 1326 // Convert the result back to f32. If we end up at a different value than the 1327 // truncated input value, then there has been an overflow and we trap. 1328 Node* check = Unop(wasm::kExprF32UConvertI32, result); 1329 Node* overflow = Binop(wasm::kExprF32Ne, trunc, check); 1330 trap_->AddTrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position); 1331 1332 return result; 1333 } 1334 1335 Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input, 1336 wasm::WasmCodePosition position) { 1337 MachineOperatorBuilder* m = jsgraph()->machine(); 1338 // Truncation of the input value is needed for the overflow check later. 1339 Node* trunc = Unop(wasm::kExprF64Trunc, input); 1340 Node* result = graph()->NewNode(m->TruncateFloat64ToUint32(), trunc); 1341 1342 // Convert the result back to f64. If we end up at a different value than the 1343 // truncated input value, then there has been an overflow and we trap. 1344 Node* check = Unop(wasm::kExprF64UConvertI32, result); 1345 Node* overflow = Binop(wasm::kExprF64Ne, trunc, check); 1346 trap_->AddTrapIfTrue(wasm::kTrapFloatUnrepresentable, overflow, position); 1347 1348 return result; 1349 } 1350 1351 Node* WasmGraphBuilder::BuildI32AsmjsSConvertF32(Node* input) { 1352 MachineOperatorBuilder* m = jsgraph()->machine(); 1353 // asm.js must use the wacky JS semantics. 1354 input = graph()->NewNode(m->ChangeFloat32ToFloat64(), input); 1355 return graph()->NewNode(m->TruncateFloat64ToWord32(), input); 1356 } 1357 1358 Node* WasmGraphBuilder::BuildI32AsmjsSConvertF64(Node* input) { 1359 MachineOperatorBuilder* m = jsgraph()->machine(); 1360 // asm.js must use the wacky JS semantics. 1361 return graph()->NewNode(m->TruncateFloat64ToWord32(), input); 1362 } 1363 1364 Node* WasmGraphBuilder::BuildI32AsmjsUConvertF32(Node* input) { 1365 MachineOperatorBuilder* m = jsgraph()->machine(); 1366 // asm.js must use the wacky JS semantics. 1367 input = graph()->NewNode(m->ChangeFloat32ToFloat64(), input); 1368 return graph()->NewNode(m->TruncateFloat64ToWord32(), input); 1369 } 1370 1371 Node* WasmGraphBuilder::BuildI32AsmjsUConvertF64(Node* input) { 1372 MachineOperatorBuilder* m = jsgraph()->machine(); 1373 // asm.js must use the wacky JS semantics. 1374 return graph()->NewNode(m->TruncateFloat64ToWord32(), input); 1375 } 1376 1377 Node* WasmGraphBuilder::BuildBitCountingCall(Node* input, ExternalReference ref, 1378 MachineRepresentation input_type) { 1379 Node* stack_slot_param = 1380 graph()->NewNode(jsgraph()->machine()->StackSlot(input_type)); 1381 1382 const Operator* store_op = jsgraph()->machine()->Store( 1383 StoreRepresentation(input_type, kNoWriteBarrier)); 1384 *effect_ = 1385 graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0), 1386 input, *effect_, *control_); 1387 1388 MachineSignature::Builder sig_builder(jsgraph()->zone(), 1, 1); 1389 sig_builder.AddReturn(MachineType::Int32()); 1390 sig_builder.AddParam(MachineType::Pointer()); 1391 1392 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); 1393 Node* args[] = {function, stack_slot_param}; 1394 1395 return BuildCCall(sig_builder.Build(), args); 1396 } 1397 1398 Node* WasmGraphBuilder::BuildI32Ctz(Node* input) { 1399 return BuildBitCountingCall( 1400 input, ExternalReference::wasm_word32_ctz(jsgraph()->isolate()), 1401 MachineRepresentation::kWord32); 1402 } 1403 1404 Node* WasmGraphBuilder::BuildI64Ctz(Node* input) { 1405 return Unop(wasm::kExprI64UConvertI32, 1406 BuildBitCountingCall(input, ExternalReference::wasm_word64_ctz( 1407 jsgraph()->isolate()), 1408 MachineRepresentation::kWord64)); 1409 } 1410 1411 Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) { 1412 return BuildBitCountingCall( 1413 input, ExternalReference::wasm_word32_popcnt(jsgraph()->isolate()), 1414 MachineRepresentation::kWord32); 1415 } 1416 1417 Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) { 1418 return Unop(wasm::kExprI64UConvertI32, 1419 BuildBitCountingCall(input, ExternalReference::wasm_word64_popcnt( 1420 jsgraph()->isolate()), 1421 MachineRepresentation::kWord64)); 1422 } 1423 1424 Node* WasmGraphBuilder::BuildF32Trunc(Node* input) { 1425 MachineType type = MachineType::Float32(); 1426 ExternalReference ref = 1427 ExternalReference::wasm_f32_trunc(jsgraph()->isolate()); 1428 1429 return BuildCFuncInstruction(ref, type, input); 1430 } 1431 1432 Node* WasmGraphBuilder::BuildF32Floor(Node* input) { 1433 MachineType type = MachineType::Float32(); 1434 ExternalReference ref = 1435 ExternalReference::wasm_f32_floor(jsgraph()->isolate()); 1436 return BuildCFuncInstruction(ref, type, input); 1437 } 1438 1439 Node* WasmGraphBuilder::BuildF32Ceil(Node* input) { 1440 MachineType type = MachineType::Float32(); 1441 ExternalReference ref = 1442 ExternalReference::wasm_f32_ceil(jsgraph()->isolate()); 1443 return BuildCFuncInstruction(ref, type, input); 1444 } 1445 1446 Node* WasmGraphBuilder::BuildF32NearestInt(Node* input) { 1447 MachineType type = MachineType::Float32(); 1448 ExternalReference ref = 1449 ExternalReference::wasm_f32_nearest_int(jsgraph()->isolate()); 1450 return BuildCFuncInstruction(ref, type, input); 1451 } 1452 1453 Node* WasmGraphBuilder::BuildF64Trunc(Node* input) { 1454 MachineType type = MachineType::Float64(); 1455 ExternalReference ref = 1456 ExternalReference::wasm_f64_trunc(jsgraph()->isolate()); 1457 return BuildCFuncInstruction(ref, type, input); 1458 } 1459 1460 Node* WasmGraphBuilder::BuildF64Floor(Node* input) { 1461 MachineType type = MachineType::Float64(); 1462 ExternalReference ref = 1463 ExternalReference::wasm_f64_floor(jsgraph()->isolate()); 1464 return BuildCFuncInstruction(ref, type, input); 1465 } 1466 1467 Node* WasmGraphBuilder::BuildF64Ceil(Node* input) { 1468 MachineType type = MachineType::Float64(); 1469 ExternalReference ref = 1470 ExternalReference::wasm_f64_ceil(jsgraph()->isolate()); 1471 return BuildCFuncInstruction(ref, type, input); 1472 } 1473 1474 Node* WasmGraphBuilder::BuildF64NearestInt(Node* input) { 1475 MachineType type = MachineType::Float64(); 1476 ExternalReference ref = 1477 ExternalReference::wasm_f64_nearest_int(jsgraph()->isolate()); 1478 return BuildCFuncInstruction(ref, type, input); 1479 } 1480 1481 Node* WasmGraphBuilder::BuildF64Acos(Node* input) { 1482 MachineType type = MachineType::Float64(); 1483 ExternalReference ref = 1484 ExternalReference::f64_acos_wrapper_function(jsgraph()->isolate()); 1485 return BuildCFuncInstruction(ref, type, input); 1486 } 1487 1488 Node* WasmGraphBuilder::BuildF64Asin(Node* input) { 1489 MachineType type = MachineType::Float64(); 1490 ExternalReference ref = 1491 ExternalReference::f64_asin_wrapper_function(jsgraph()->isolate()); 1492 return BuildCFuncInstruction(ref, type, input); 1493 } 1494 1495 Node* WasmGraphBuilder::BuildF64Pow(Node* left, Node* right) { 1496 MachineType type = MachineType::Float64(); 1497 ExternalReference ref = 1498 ExternalReference::wasm_float64_pow(jsgraph()->isolate()); 1499 return BuildCFuncInstruction(ref, type, left, right); 1500 } 1501 1502 Node* WasmGraphBuilder::BuildF64Mod(Node* left, Node* right) { 1503 MachineType type = MachineType::Float64(); 1504 ExternalReference ref = 1505 ExternalReference::f64_mod_wrapper_function(jsgraph()->isolate()); 1506 return BuildCFuncInstruction(ref, type, left, right); 1507 } 1508 1509 Node* WasmGraphBuilder::BuildCFuncInstruction(ExternalReference ref, 1510 MachineType type, Node* input0, 1511 Node* input1) { 1512 // We do truncation by calling a C function which calculates the result. 1513 // The input is passed to the C function as a double*'s to avoid double 1514 // parameters. For this we reserve slots on the stack, store the parameters 1515 // in those slots, pass pointers to the slot to the C function, 1516 // and after calling the C function we collect the return value from 1517 // the stack slot. 1518 1519 Node* stack_slot_param0 = 1520 graph()->NewNode(jsgraph()->machine()->StackSlot(type.representation())); 1521 1522 const Operator* store_op0 = jsgraph()->machine()->Store( 1523 StoreRepresentation(type.representation(), kNoWriteBarrier)); 1524 *effect_ = graph()->NewNode(store_op0, stack_slot_param0, 1525 jsgraph()->Int32Constant(0), input0, *effect_, 1526 *control_); 1527 1528 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); 1529 Node** args = Buffer(5); 1530 args[0] = function; 1531 args[1] = stack_slot_param0; 1532 int input_count = 1; 1533 1534 if (input1 != nullptr) { 1535 Node* stack_slot_param1 = graph()->NewNode( 1536 jsgraph()->machine()->StackSlot(type.representation())); 1537 const Operator* store_op1 = jsgraph()->machine()->Store( 1538 StoreRepresentation(type.representation(), kNoWriteBarrier)); 1539 *effect_ = graph()->NewNode(store_op1, stack_slot_param1, 1540 jsgraph()->Int32Constant(0), input1, *effect_, 1541 *control_); 1542 args[2] = stack_slot_param1; 1543 ++input_count; 1544 } 1545 1546 Signature<MachineType>::Builder sig_builder(jsgraph()->zone(), 0, 1547 input_count); 1548 sig_builder.AddParam(MachineType::Pointer()); 1549 if (input1 != nullptr) { 1550 sig_builder.AddParam(MachineType::Pointer()); 1551 } 1552 BuildCCall(sig_builder.Build(), args); 1553 1554 const Operator* load_op = jsgraph()->machine()->Load(type); 1555 1556 Node* load = 1557 graph()->NewNode(load_op, stack_slot_param0, jsgraph()->Int32Constant(0), 1558 *effect_, *control_); 1559 *effect_ = load; 1560 return load; 1561 } 1562 1563 Node* WasmGraphBuilder::BuildF32SConvertI64(Node* input) { 1564 // TODO(titzer/bradnelson): Check handlng of asm.js case. 1565 return BuildIntToFloatConversionInstruction( 1566 input, ExternalReference::wasm_int64_to_float32(jsgraph()->isolate()), 1567 MachineRepresentation::kWord64, MachineType::Float32()); 1568 } 1569 Node* WasmGraphBuilder::BuildF32UConvertI64(Node* input) { 1570 // TODO(titzer/bradnelson): Check handlng of asm.js case. 1571 return BuildIntToFloatConversionInstruction( 1572 input, ExternalReference::wasm_uint64_to_float32(jsgraph()->isolate()), 1573 MachineRepresentation::kWord64, MachineType::Float32()); 1574 } 1575 Node* WasmGraphBuilder::BuildF64SConvertI64(Node* input) { 1576 return BuildIntToFloatConversionInstruction( 1577 input, ExternalReference::wasm_int64_to_float64(jsgraph()->isolate()), 1578 MachineRepresentation::kWord64, MachineType::Float64()); 1579 } 1580 Node* WasmGraphBuilder::BuildF64UConvertI64(Node* input) { 1581 return BuildIntToFloatConversionInstruction( 1582 input, ExternalReference::wasm_uint64_to_float64(jsgraph()->isolate()), 1583 MachineRepresentation::kWord64, MachineType::Float64()); 1584 } 1585 1586 Node* WasmGraphBuilder::BuildIntToFloatConversionInstruction( 1587 Node* input, ExternalReference ref, 1588 MachineRepresentation parameter_representation, 1589 const MachineType result_type) { 1590 Node* stack_slot_param = graph()->NewNode( 1591 jsgraph()->machine()->StackSlot(parameter_representation)); 1592 Node* stack_slot_result = graph()->NewNode( 1593 jsgraph()->machine()->StackSlot(result_type.representation())); 1594 const Operator* store_op = jsgraph()->machine()->Store( 1595 StoreRepresentation(parameter_representation, kNoWriteBarrier)); 1596 *effect_ = 1597 graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0), 1598 input, *effect_, *control_); 1599 MachineSignature::Builder sig_builder(jsgraph()->zone(), 0, 2); 1600 sig_builder.AddParam(MachineType::Pointer()); 1601 sig_builder.AddParam(MachineType::Pointer()); 1602 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); 1603 Node* args[] = {function, stack_slot_param, stack_slot_result}; 1604 BuildCCall(sig_builder.Build(), args); 1605 const Operator* load_op = jsgraph()->machine()->Load(result_type); 1606 Node* load = 1607 graph()->NewNode(load_op, stack_slot_result, jsgraph()->Int32Constant(0), 1608 *effect_, *control_); 1609 *effect_ = load; 1610 return load; 1611 } 1612 1613 Node* WasmGraphBuilder::BuildI64SConvertF32(Node* input, 1614 wasm::WasmCodePosition position) { 1615 if (jsgraph()->machine()->Is32()) { 1616 return BuildFloatToIntConversionInstruction( 1617 input, ExternalReference::wasm_float32_to_int64(jsgraph()->isolate()), 1618 MachineRepresentation::kFloat32, MachineType::Int64(), position); 1619 } else { 1620 Node* trunc = graph()->NewNode( 1621 jsgraph()->machine()->TryTruncateFloat32ToInt64(), input); 1622 Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc, 1623 graph()->start()); 1624 Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc, 1625 graph()->start()); 1626 trap_->ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position); 1627 return result; 1628 } 1629 } 1630 1631 Node* WasmGraphBuilder::BuildI64UConvertF32(Node* input, 1632 wasm::WasmCodePosition position) { 1633 if (jsgraph()->machine()->Is32()) { 1634 return BuildFloatToIntConversionInstruction( 1635 input, ExternalReference::wasm_float32_to_uint64(jsgraph()->isolate()), 1636 MachineRepresentation::kFloat32, MachineType::Int64(), position); 1637 } else { 1638 Node* trunc = graph()->NewNode( 1639 jsgraph()->machine()->TryTruncateFloat32ToUint64(), input); 1640 Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc, 1641 graph()->start()); 1642 Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc, 1643 graph()->start()); 1644 trap_->ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position); 1645 return result; 1646 } 1647 } 1648 1649 Node* WasmGraphBuilder::BuildI64SConvertF64(Node* input, 1650 wasm::WasmCodePosition position) { 1651 if (jsgraph()->machine()->Is32()) { 1652 return BuildFloatToIntConversionInstruction( 1653 input, ExternalReference::wasm_float64_to_int64(jsgraph()->isolate()), 1654 MachineRepresentation::kFloat64, MachineType::Int64(), position); 1655 } else { 1656 Node* trunc = graph()->NewNode( 1657 jsgraph()->machine()->TryTruncateFloat64ToInt64(), input); 1658 Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc, 1659 graph()->start()); 1660 Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc, 1661 graph()->start()); 1662 trap_->ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position); 1663 return result; 1664 } 1665 } 1666 1667 Node* WasmGraphBuilder::BuildI64UConvertF64(Node* input, 1668 wasm::WasmCodePosition position) { 1669 if (jsgraph()->machine()->Is32()) { 1670 return BuildFloatToIntConversionInstruction( 1671 input, ExternalReference::wasm_float64_to_uint64(jsgraph()->isolate()), 1672 MachineRepresentation::kFloat64, MachineType::Int64(), position); 1673 } else { 1674 Node* trunc = graph()->NewNode( 1675 jsgraph()->machine()->TryTruncateFloat64ToUint64(), input); 1676 Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc, 1677 graph()->start()); 1678 Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc, 1679 graph()->start()); 1680 trap_->ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position); 1681 return result; 1682 } 1683 } 1684 1685 Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction( 1686 Node* input, ExternalReference ref, 1687 MachineRepresentation parameter_representation, 1688 const MachineType result_type, wasm::WasmCodePosition position) { 1689 Node* stack_slot_param = graph()->NewNode( 1690 jsgraph()->machine()->StackSlot(parameter_representation)); 1691 Node* stack_slot_result = graph()->NewNode( 1692 jsgraph()->machine()->StackSlot(result_type.representation())); 1693 const Operator* store_op = jsgraph()->machine()->Store( 1694 StoreRepresentation(parameter_representation, kNoWriteBarrier)); 1695 *effect_ = 1696 graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0), 1697 input, *effect_, *control_); 1698 MachineSignature::Builder sig_builder(jsgraph()->zone(), 1, 2); 1699 sig_builder.AddReturn(MachineType::Int32()); 1700 sig_builder.AddParam(MachineType::Pointer()); 1701 sig_builder.AddParam(MachineType::Pointer()); 1702 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); 1703 Node* args[] = {function, stack_slot_param, stack_slot_result}; 1704 trap_->ZeroCheck32(wasm::kTrapFloatUnrepresentable, 1705 BuildCCall(sig_builder.Build(), args), position); 1706 const Operator* load_op = jsgraph()->machine()->Load(result_type); 1707 Node* load = 1708 graph()->NewNode(load_op, stack_slot_result, jsgraph()->Int32Constant(0), 1709 *effect_, *control_); 1710 *effect_ = load; 1711 return load; 1712 } 1713 1714 Node* WasmGraphBuilder::GrowMemory(Node* input) { 1715 Diamond check_input_range( 1716 graph(), jsgraph()->common(), 1717 graph()->NewNode( 1718 jsgraph()->machine()->Uint32LessThanOrEqual(), input, 1719 jsgraph()->Uint32Constant(wasm::WasmModule::kV8MaxPages)), 1720 BranchHint::kTrue); 1721 1722 check_input_range.Chain(*control_); 1723 1724 Runtime::FunctionId function_id = Runtime::kWasmGrowMemory; 1725 const Runtime::Function* function = Runtime::FunctionForId(function_id); 1726 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( 1727 jsgraph()->zone(), function_id, function->nargs, Operator::kNoThrow, 1728 CallDescriptor::kNoFlags); 1729 wasm::ModuleEnv* module = module_; 1730 input = BuildChangeUint32ToSmi(input); 1731 Node* inputs[] = { 1732 jsgraph()->CEntryStubConstant(function->result_size), input, // C entry 1733 jsgraph()->ExternalConstant( 1734 ExternalReference(function_id, jsgraph()->isolate())), // ref 1735 jsgraph()->Int32Constant(function->nargs), // arity 1736 jsgraph()->HeapConstant(module->instance->context), // context 1737 *effect_, 1738 check_input_range.if_true}; 1739 Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), 1740 static_cast<int>(arraysize(inputs)), inputs); 1741 1742 Node* result = BuildChangeSmiToInt32(call); 1743 1744 result = check_input_range.Phi(MachineRepresentation::kWord32, result, 1745 jsgraph()->Int32Constant(-1)); 1746 *effect_ = graph()->NewNode(jsgraph()->common()->EffectPhi(2), call, *effect_, 1747 check_input_range.merge); 1748 *control_ = check_input_range.merge; 1749 return result; 1750 } 1751 1752 Node* WasmGraphBuilder::Throw(Node* input) { 1753 MachineOperatorBuilder* machine = jsgraph()->machine(); 1754 1755 // Pass the thrown value as two SMIs: 1756 // 1757 // upper = static_cast<uint32_t>(input) >> 16; 1758 // lower = input & 0xFFFF; 1759 // 1760 // This is needed because we can't safely call BuildChangeInt32ToTagged from 1761 // this method. 1762 // 1763 // TODO(wasm): figure out how to properly pass this to the runtime function. 1764 Node* upper = BuildChangeInt32ToSmi( 1765 graph()->NewNode(machine->Word32Shr(), input, Int32Constant(16))); 1766 Node* lower = BuildChangeInt32ToSmi( 1767 graph()->NewNode(machine->Word32And(), input, Int32Constant(0xFFFFu))); 1768 1769 Node* parameters[] = {lower, upper}; // thrown value 1770 return BuildCallToRuntime(Runtime::kWasmThrow, jsgraph(), 1771 module_->instance->context, parameters, 1772 arraysize(parameters), effect_, *control_); 1773 } 1774 1775 Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) { 1776 CommonOperatorBuilder* common = jsgraph()->common(); 1777 1778 Node* parameters[] = {input}; // caught value 1779 Node* value = 1780 BuildCallToRuntime(Runtime::kWasmGetCaughtExceptionValue, jsgraph(), 1781 module_->instance->context, parameters, 1782 arraysize(parameters), effect_, *control_); 1783 1784 Node* is_smi; 1785 Node* is_heap; 1786 BranchExpectFalse(BuildTestNotSmi(value), &is_heap, &is_smi); 1787 1788 // is_smi 1789 Node* smi_i32 = BuildChangeSmiToInt32(value); 1790 Node* is_smi_effect = *effect_; 1791 1792 // is_heap 1793 *control_ = is_heap; 1794 Node* heap_f64 = BuildLoadHeapNumberValue(value, is_heap); 1795 1796 // *control_ needs to point to the current control dependency (is_heap) in 1797 // case BuildI32SConvertF64 needs to insert nodes that depend on the "current" 1798 // control node. 1799 Node* heap_i32 = BuildI32SConvertF64(heap_f64, position); 1800 // *control_ contains the control node that should be used when merging the 1801 // result for the catch clause. It may be different than *control_ because 1802 // BuildI32SConvertF64 may introduce a new control node (used for trapping if 1803 // heap_f64 cannot be converted to an i32. 1804 is_heap = *control_; 1805 Node* is_heap_effect = *effect_; 1806 1807 Node* merge = graph()->NewNode(common->Merge(2), is_heap, is_smi); 1808 Node* effect_merge = graph()->NewNode(common->EffectPhi(2), is_heap_effect, 1809 is_smi_effect, merge); 1810 1811 Node* value_i32 = graph()->NewNode( 1812 common->Phi(MachineRepresentation::kWord32, 2), heap_i32, smi_i32, merge); 1813 1814 *control_ = merge; 1815 *effect_ = effect_merge; 1816 return value_i32; 1817 } 1818 1819 Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right, 1820 wasm::WasmCodePosition position) { 1821 MachineOperatorBuilder* m = jsgraph()->machine(); 1822 trap_->ZeroCheck32(wasm::kTrapDivByZero, right, position); 1823 Node* before = *control_; 1824 Node* denom_is_m1; 1825 Node* denom_is_not_m1; 1826 BranchExpectFalse( 1827 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)), 1828 &denom_is_m1, &denom_is_not_m1); 1829 *control_ = denom_is_m1; 1830 trap_->TrapIfEq32(wasm::kTrapDivUnrepresentable, left, kMinInt, position); 1831 if (*control_ != denom_is_m1) { 1832 *control_ = graph()->NewNode(jsgraph()->common()->Merge(2), denom_is_not_m1, 1833 *control_); 1834 } else { 1835 *control_ = before; 1836 } 1837 return graph()->NewNode(m->Int32Div(), left, right, *control_); 1838 } 1839 1840 Node* WasmGraphBuilder::BuildI32RemS(Node* left, Node* right, 1841 wasm::WasmCodePosition position) { 1842 MachineOperatorBuilder* m = jsgraph()->machine(); 1843 1844 trap_->ZeroCheck32(wasm::kTrapRemByZero, right, position); 1845 1846 Diamond d( 1847 graph(), jsgraph()->common(), 1848 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)), 1849 BranchHint::kFalse); 1850 d.Chain(*control_); 1851 1852 return d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), 1853 graph()->NewNode(m->Int32Mod(), left, right, d.if_false)); 1854 } 1855 1856 Node* WasmGraphBuilder::BuildI32DivU(Node* left, Node* right, 1857 wasm::WasmCodePosition position) { 1858 MachineOperatorBuilder* m = jsgraph()->machine(); 1859 return graph()->NewNode( 1860 m->Uint32Div(), left, right, 1861 trap_->ZeroCheck32(wasm::kTrapDivByZero, right, position)); 1862 } 1863 1864 Node* WasmGraphBuilder::BuildI32RemU(Node* left, Node* right, 1865 wasm::WasmCodePosition position) { 1866 MachineOperatorBuilder* m = jsgraph()->machine(); 1867 return graph()->NewNode( 1868 m->Uint32Mod(), left, right, 1869 trap_->ZeroCheck32(wasm::kTrapRemByZero, right, position)); 1870 } 1871 1872 Node* WasmGraphBuilder::BuildI32AsmjsDivS(Node* left, Node* right) { 1873 MachineOperatorBuilder* m = jsgraph()->machine(); 1874 1875 Int32Matcher mr(right); 1876 if (mr.HasValue()) { 1877 if (mr.Value() == 0) { 1878 return jsgraph()->Int32Constant(0); 1879 } else if (mr.Value() == -1) { 1880 // The result is the negation of the left input. 1881 return graph()->NewNode(m->Int32Sub(), jsgraph()->Int32Constant(0), left); 1882 } 1883 return graph()->NewNode(m->Int32Div(), left, right, *control_); 1884 } 1885 1886 // asm.js semantics return 0 on divide or mod by zero. 1887 if (m->Int32DivIsSafe()) { 1888 // The hardware instruction does the right thing (e.g. arm). 1889 return graph()->NewNode(m->Int32Div(), left, right, graph()->start()); 1890 } 1891 1892 // Check denominator for zero. 1893 Diamond z( 1894 graph(), jsgraph()->common(), 1895 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)), 1896 BranchHint::kFalse); 1897 1898 // Check numerator for -1. (avoid minint / -1 case). 1899 Diamond n( 1900 graph(), jsgraph()->common(), 1901 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)), 1902 BranchHint::kFalse); 1903 1904 Node* div = graph()->NewNode(m->Int32Div(), left, right, z.if_false); 1905 Node* neg = 1906 graph()->NewNode(m->Int32Sub(), jsgraph()->Int32Constant(0), left); 1907 1908 return n.Phi( 1909 MachineRepresentation::kWord32, neg, 1910 z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), div)); 1911 } 1912 1913 Node* WasmGraphBuilder::BuildI32AsmjsRemS(Node* left, Node* right) { 1914 MachineOperatorBuilder* m = jsgraph()->machine(); 1915 1916 Int32Matcher mr(right); 1917 if (mr.HasValue()) { 1918 if (mr.Value() == 0) { 1919 return jsgraph()->Int32Constant(0); 1920 } else if (mr.Value() == -1) { 1921 return jsgraph()->Int32Constant(0); 1922 } 1923 return graph()->NewNode(m->Int32Mod(), left, right, *control_); 1924 } 1925 1926 // asm.js semantics return 0 on divide or mod by zero. 1927 // Explicit check for x % 0. 1928 Diamond z( 1929 graph(), jsgraph()->common(), 1930 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)), 1931 BranchHint::kFalse); 1932 1933 // Explicit check for x % -1. 1934 Diamond d( 1935 graph(), jsgraph()->common(), 1936 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)), 1937 BranchHint::kFalse); 1938 d.Chain(z.if_false); 1939 1940 return z.Phi( 1941 MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), 1942 d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), 1943 graph()->NewNode(m->Int32Mod(), left, right, d.if_false))); 1944 } 1945 1946 Node* WasmGraphBuilder::BuildI32AsmjsDivU(Node* left, Node* right) { 1947 MachineOperatorBuilder* m = jsgraph()->machine(); 1948 // asm.js semantics return 0 on divide or mod by zero. 1949 if (m->Uint32DivIsSafe()) { 1950 // The hardware instruction does the right thing (e.g. arm). 1951 return graph()->NewNode(m->Uint32Div(), left, right, graph()->start()); 1952 } 1953 1954 // Explicit check for x % 0. 1955 Diamond z( 1956 graph(), jsgraph()->common(), 1957 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)), 1958 BranchHint::kFalse); 1959 1960 return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), 1961 graph()->NewNode(jsgraph()->machine()->Uint32Div(), left, right, 1962 z.if_false)); 1963 } 1964 1965 Node* WasmGraphBuilder::BuildI32AsmjsRemU(Node* left, Node* right) { 1966 MachineOperatorBuilder* m = jsgraph()->machine(); 1967 // asm.js semantics return 0 on divide or mod by zero. 1968 // Explicit check for x % 0. 1969 Diamond z( 1970 graph(), jsgraph()->common(), 1971 graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)), 1972 BranchHint::kFalse); 1973 1974 Node* rem = graph()->NewNode(jsgraph()->machine()->Uint32Mod(), left, right, 1975 z.if_false); 1976 return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), 1977 rem); 1978 } 1979 1980 Node* WasmGraphBuilder::BuildI64DivS(Node* left, Node* right, 1981 wasm::WasmCodePosition position) { 1982 if (jsgraph()->machine()->Is32()) { 1983 return BuildDiv64Call( 1984 left, right, ExternalReference::wasm_int64_div(jsgraph()->isolate()), 1985 MachineType::Int64(), wasm::kTrapDivByZero, position); 1986 } 1987 trap_->ZeroCheck64(wasm::kTrapDivByZero, right, position); 1988 Node* before = *control_; 1989 Node* denom_is_m1; 1990 Node* denom_is_not_m1; 1991 BranchExpectFalse(graph()->NewNode(jsgraph()->machine()->Word64Equal(), right, 1992 jsgraph()->Int64Constant(-1)), 1993 &denom_is_m1, &denom_is_not_m1); 1994 *control_ = denom_is_m1; 1995 trap_->TrapIfEq64(wasm::kTrapDivUnrepresentable, left, 1996 std::numeric_limits<int64_t>::min(), position); 1997 if (*control_ != denom_is_m1) { 1998 *control_ = graph()->NewNode(jsgraph()->common()->Merge(2), denom_is_not_m1, 1999 *control_); 2000 } else { 2001 *control_ = before; 2002 } 2003 return graph()->NewNode(jsgraph()->machine()->Int64Div(), left, right, 2004 *control_); 2005 } 2006 2007 Node* WasmGraphBuilder::BuildI64RemS(Node* left, Node* right, 2008 wasm::WasmCodePosition position) { 2009 if (jsgraph()->machine()->Is32()) { 2010 return BuildDiv64Call( 2011 left, right, ExternalReference::wasm_int64_mod(jsgraph()->isolate()), 2012 MachineType::Int64(), wasm::kTrapRemByZero, position); 2013 } 2014 trap_->ZeroCheck64(wasm::kTrapRemByZero, right, position); 2015 Diamond d(jsgraph()->graph(), jsgraph()->common(), 2016 graph()->NewNode(jsgraph()->machine()->Word64Equal(), right, 2017 jsgraph()->Int64Constant(-1))); 2018 2019 Node* rem = graph()->NewNode(jsgraph()->machine()->Int64Mod(), left, right, 2020 d.if_false); 2021 2022 return d.Phi(MachineRepresentation::kWord64, jsgraph()->Int64Constant(0), 2023 rem); 2024 } 2025 2026 Node* WasmGraphBuilder::BuildI64DivU(Node* left, Node* right, 2027 wasm::WasmCodePosition position) { 2028 if (jsgraph()->machine()->Is32()) { 2029 return BuildDiv64Call( 2030 left, right, ExternalReference::wasm_uint64_div(jsgraph()->isolate()), 2031 MachineType::Int64(), wasm::kTrapDivByZero, position); 2032 } 2033 return graph()->NewNode( 2034 jsgraph()->machine()->Uint64Div(), left, right, 2035 trap_->ZeroCheck64(wasm::kTrapDivByZero, right, position)); 2036 } 2037 Node* WasmGraphBuilder::BuildI64RemU(Node* left, Node* right, 2038 wasm::WasmCodePosition position) { 2039 if (jsgraph()->machine()->Is32()) { 2040 return BuildDiv64Call( 2041 left, right, ExternalReference::wasm_uint64_mod(jsgraph()->isolate()), 2042 MachineType::Int64(), wasm::kTrapRemByZero, position); 2043 } 2044 return graph()->NewNode( 2045 jsgraph()->machine()->Uint64Mod(), left, right, 2046 trap_->ZeroCheck64(wasm::kTrapRemByZero, right, position)); 2047 } 2048 2049 Node* WasmGraphBuilder::BuildDiv64Call(Node* left, Node* right, 2050 ExternalReference ref, 2051 MachineType result_type, int trap_zero, 2052 wasm::WasmCodePosition position) { 2053 Node* stack_slot_dst = graph()->NewNode( 2054 jsgraph()->machine()->StackSlot(MachineRepresentation::kWord64)); 2055 Node* stack_slot_src = graph()->NewNode( 2056 jsgraph()->machine()->StackSlot(MachineRepresentation::kWord64)); 2057 2058 const Operator* store_op = jsgraph()->machine()->Store( 2059 StoreRepresentation(MachineRepresentation::kWord64, kNoWriteBarrier)); 2060 *effect_ = 2061 graph()->NewNode(store_op, stack_slot_dst, jsgraph()->Int32Constant(0), 2062 left, *effect_, *control_); 2063 *effect_ = 2064 graph()->NewNode(store_op, stack_slot_src, jsgraph()->Int32Constant(0), 2065 right, *effect_, *control_); 2066 2067 MachineSignature::Builder sig_builder(jsgraph()->zone(), 1, 2); 2068 sig_builder.AddReturn(MachineType::Int32()); 2069 sig_builder.AddParam(MachineType::Pointer()); 2070 sig_builder.AddParam(MachineType::Pointer()); 2071 2072 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); 2073 Node* args[] = {function, stack_slot_dst, stack_slot_src}; 2074 2075 Node* call = BuildCCall(sig_builder.Build(), args); 2076 2077 // TODO(wasm): This can get simpler if we have a specialized runtime call to 2078 // throw WASM exceptions by trap code instead of by string. 2079 trap_->ZeroCheck32(static_cast<wasm::TrapReason>(trap_zero), call, position); 2080 trap_->TrapIfEq32(wasm::kTrapDivUnrepresentable, call, -1, position); 2081 const Operator* load_op = jsgraph()->machine()->Load(result_type); 2082 Node* load = 2083 graph()->NewNode(load_op, stack_slot_dst, jsgraph()->Int32Constant(0), 2084 *effect_, *control_); 2085 *effect_ = load; 2086 return load; 2087 } 2088 2089 Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) { 2090 const size_t params = sig->parameter_count(); 2091 const size_t extra = 2; // effect and control inputs. 2092 const size_t count = 1 + params + extra; 2093 2094 // Reallocate the buffer to make space for extra inputs. 2095 args = Realloc(args, 1 + params, count); 2096 2097 // Add effect and control inputs. 2098 args[params + 1] = *effect_; 2099 args[params + 2] = *control_; 2100 2101 CallDescriptor* desc = 2102 Linkage::GetSimplifiedCDescriptor(jsgraph()->zone(), sig); 2103 2104 const Operator* op = jsgraph()->common()->Call(desc); 2105 Node* call = graph()->NewNode(op, static_cast<int>(count), args); 2106 *effect_ = call; 2107 return call; 2108 } 2109 2110 Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args, 2111 Node*** rets, 2112 wasm::WasmCodePosition position) { 2113 const size_t params = sig->parameter_count(); 2114 const size_t extra = 2; // effect and control inputs. 2115 const size_t count = 1 + params + extra; 2116 2117 // Reallocate the buffer to make space for extra inputs. 2118 args = Realloc(args, 1 + params, count); 2119 2120 // Add effect and control inputs. 2121 args[params + 1] = *effect_; 2122 args[params + 2] = *control_; 2123 2124 CallDescriptor* descriptor = 2125 wasm::ModuleEnv::GetWasmCallDescriptor(jsgraph()->zone(), sig); 2126 const Operator* op = jsgraph()->common()->Call(descriptor); 2127 Node* call = graph()->NewNode(op, static_cast<int>(count), args); 2128 SetSourcePosition(call, position); 2129 2130 *effect_ = call; 2131 size_t ret_count = sig->return_count(); 2132 if (ret_count == 0) return call; // No return value. 2133 2134 *rets = Buffer(ret_count); 2135 if (ret_count == 1) { 2136 // Only a single return value. 2137 (*rets)[0] = call; 2138 } else { 2139 // Create projections for all return values. 2140 for (size_t i = 0; i < ret_count; i++) { 2141 (*rets)[i] = graph()->NewNode(jsgraph()->common()->Projection(i), call, 2142 graph()->start()); 2143 } 2144 } 2145 return call; 2146 } 2147 2148 Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets, 2149 wasm::WasmCodePosition position) { 2150 DCHECK_NULL(args[0]); 2151 2152 // Add code object as constant. 2153 Handle<Code> code = module_->GetFunctionCode(index); 2154 DCHECK(!code.is_null()); 2155 args[0] = HeapConstant(code); 2156 wasm::FunctionSig* sig = module_->GetFunctionSignature(index); 2157 2158 return BuildWasmCall(sig, args, rets, position); 2159 } 2160 2161 Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args, 2162 Node*** rets, 2163 wasm::WasmCodePosition position) { 2164 DCHECK_NOT_NULL(args[0]); 2165 DCHECK(module_ && module_->instance); 2166 2167 // Assume only one table for now. 2168 uint32_t table_index = 0; 2169 wasm::FunctionSig* sig = module_->GetSignature(sig_index); 2170 2171 DCHECK(module_->IsValidTable(table_index)); 2172 2173 EnsureFunctionTableNodes(); 2174 MachineOperatorBuilder* machine = jsgraph()->machine(); 2175 Node* key = args[0]; 2176 2177 // Bounds check against the table size. 2178 Node* size = function_table_sizes_[table_index]; 2179 Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size); 2180 trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position); 2181 Node* table = function_tables_[table_index]; 2182 2183 // Load signature from the table and check. 2184 // The table is a FixedArray; signatures are encoded as SMIs. 2185 // [sig1, sig2, sig3, ...., code1, code2, code3 ...] 2186 ElementAccess access = AccessBuilder::ForFixedArrayElement(); 2187 const int fixed_offset = access.header_size - access.tag(); 2188 { 2189 Node* load_sig = graph()->NewNode( 2190 machine->Load(MachineType::AnyTagged()), table, 2191 graph()->NewNode(machine->Int32Add(), 2192 graph()->NewNode(machine->Word32Shl(), key, 2193 Int32Constant(kPointerSizeLog2)), 2194 Int32Constant(fixed_offset)), 2195 *effect_, *control_); 2196 auto map = const_cast<wasm::SignatureMap&>( 2197 module_->module->function_tables[0].map); 2198 Node* sig_match = graph()->NewNode( 2199 machine->WordEqual(), load_sig, 2200 jsgraph()->SmiConstant(static_cast<int>(map.FindOrInsert(sig)))); 2201 trap_->AddTrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position); 2202 } 2203 2204 // Load code object from the table. 2205 uint32_t table_size = module_->module->function_tables[table_index].min_size; 2206 uint32_t offset = fixed_offset + kPointerSize * table_size; 2207 Node* load_code = graph()->NewNode( 2208 machine->Load(MachineType::AnyTagged()), table, 2209 graph()->NewNode(machine->Int32Add(), 2210 graph()->NewNode(machine->Word32Shl(), key, 2211 Int32Constant(kPointerSizeLog2)), 2212 Uint32Constant(offset)), 2213 *effect_, *control_); 2214 2215 args[0] = load_code; 2216 return BuildWasmCall(sig, args, rets, position); 2217 } 2218 2219 Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) { 2220 // Implement Rol by Ror since TurboFan does not have Rol opcode. 2221 // TODO(weiliang): support Word32Rol opcode in TurboFan. 2222 Int32Matcher m(right); 2223 if (m.HasValue()) { 2224 return Binop(wasm::kExprI32Ror, left, 2225 jsgraph()->Int32Constant(32 - m.Value())); 2226 } else { 2227 return Binop(wasm::kExprI32Ror, left, 2228 Binop(wasm::kExprI32Sub, jsgraph()->Int32Constant(32), right)); 2229 } 2230 } 2231 2232 Node* WasmGraphBuilder::BuildI64Rol(Node* left, Node* right) { 2233 // Implement Rol by Ror since TurboFan does not have Rol opcode. 2234 // TODO(weiliang): support Word64Rol opcode in TurboFan. 2235 Int64Matcher m(right); 2236 if (m.HasValue()) { 2237 return Binop(wasm::kExprI64Ror, left, 2238 jsgraph()->Int64Constant(64 - m.Value())); 2239 } else { 2240 return Binop(wasm::kExprI64Ror, left, 2241 Binop(wasm::kExprI64Sub, jsgraph()->Int64Constant(64), right)); 2242 } 2243 } 2244 2245 Node* WasmGraphBuilder::Invert(Node* node) { 2246 return Unop(wasm::kExprI32Eqz, node); 2247 } 2248 2249 Node* WasmGraphBuilder::BuildChangeInt32ToTagged(Node* value) { 2250 MachineOperatorBuilder* machine = jsgraph()->machine(); 2251 CommonOperatorBuilder* common = jsgraph()->common(); 2252 2253 if (machine->Is64()) { 2254 return BuildChangeInt32ToSmi(value); 2255 } 2256 2257 Node* add = graph()->NewNode(machine->Int32AddWithOverflow(), value, value, 2258 graph()->start()); 2259 2260 Node* ovf = graph()->NewNode(common->Projection(1), add, graph()->start()); 2261 Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), ovf, 2262 graph()->start()); 2263 2264 Node* if_true = graph()->NewNode(common->IfTrue(), branch); 2265 Node* vtrue = BuildAllocateHeapNumberWithValue( 2266 graph()->NewNode(machine->ChangeInt32ToFloat64(), value), if_true); 2267 2268 Node* if_false = graph()->NewNode(common->IfFalse(), branch); 2269 Node* vfalse = graph()->NewNode(common->Projection(0), add, if_false); 2270 2271 Node* merge = graph()->NewNode(common->Merge(2), if_true, if_false); 2272 Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), 2273 vtrue, vfalse, merge); 2274 return phi; 2275 } 2276 2277 Node* WasmGraphBuilder::BuildChangeFloat64ToTagged(Node* value) { 2278 MachineOperatorBuilder* machine = jsgraph()->machine(); 2279 CommonOperatorBuilder* common = jsgraph()->common(); 2280 2281 Node* value32 = graph()->NewNode(machine->RoundFloat64ToInt32(), value); 2282 Node* check_same = graph()->NewNode( 2283 machine->Float64Equal(), value, 2284 graph()->NewNode(machine->ChangeInt32ToFloat64(), value32)); 2285 Node* branch_same = 2286 graph()->NewNode(common->Branch(), check_same, graph()->start()); 2287 2288 Node* if_smi = graph()->NewNode(common->IfTrue(), branch_same); 2289 Node* vsmi; 2290 Node* if_box = graph()->NewNode(common->IfFalse(), branch_same); 2291 Node* vbox; 2292 2293 // We only need to check for -0 if the {value} can potentially contain -0. 2294 Node* check_zero = graph()->NewNode(machine->Word32Equal(), value32, 2295 jsgraph()->Int32Constant(0)); 2296 Node* branch_zero = 2297 graph()->NewNode(common->Branch(BranchHint::kFalse), check_zero, if_smi); 2298 2299 Node* if_zero = graph()->NewNode(common->IfTrue(), branch_zero); 2300 Node* if_notzero = graph()->NewNode(common->IfFalse(), branch_zero); 2301 2302 // In case of 0, we need to check the high bits for the IEEE -0 pattern. 2303 Node* check_negative = graph()->NewNode( 2304 machine->Int32LessThan(), 2305 graph()->NewNode(machine->Float64ExtractHighWord32(), value), 2306 jsgraph()->Int32Constant(0)); 2307 Node* branch_negative = graph()->NewNode(common->Branch(BranchHint::kFalse), 2308 check_negative, if_zero); 2309 2310 Node* if_negative = graph()->NewNode(common->IfTrue(), branch_negative); 2311 Node* if_notnegative = graph()->NewNode(common->IfFalse(), branch_negative); 2312 2313 // We need to create a box for negative 0. 2314 if_smi = graph()->NewNode(common->Merge(2), if_notzero, if_notnegative); 2315 if_box = graph()->NewNode(common->Merge(2), if_box, if_negative); 2316 2317 // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit 2318 // machines we need to deal with potential overflow and fallback to boxing. 2319 if (machine->Is64()) { 2320 vsmi = BuildChangeInt32ToSmi(value32); 2321 } else { 2322 Node* smi_tag = graph()->NewNode(machine->Int32AddWithOverflow(), value32, 2323 value32, if_smi); 2324 2325 Node* check_ovf = graph()->NewNode(common->Projection(1), smi_tag, if_smi); 2326 Node* branch_ovf = 2327 graph()->NewNode(common->Branch(BranchHint::kFalse), check_ovf, if_smi); 2328 2329 Node* if_ovf = graph()->NewNode(common->IfTrue(), branch_ovf); 2330 if_box = graph()->NewNode(common->Merge(2), if_ovf, if_box); 2331 2332 if_smi = graph()->NewNode(common->IfFalse(), branch_ovf); 2333 vsmi = graph()->NewNode(common->Projection(0), smi_tag, if_smi); 2334 } 2335 2336 // Allocate the box for the {value}. 2337 vbox = BuildAllocateHeapNumberWithValue(value, if_box); 2338 2339 Node* control = graph()->NewNode(common->Merge(2), if_smi, if_box); 2340 value = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), vsmi, 2341 vbox, control); 2342 return value; 2343 } 2344 2345 Node* WasmGraphBuilder::ToJS(Node* node, wasm::LocalType type) { 2346 switch (type) { 2347 case wasm::kAstI32: 2348 return BuildChangeInt32ToTagged(node); 2349 case wasm::kAstS128: 2350 case wasm::kAstI64: 2351 // Throw a TypeError. The native context is good enough here because we 2352 // only throw a TypeError. 2353 return BuildCallToRuntime(Runtime::kWasmThrowTypeError, jsgraph(), 2354 jsgraph()->isolate()->native_context(), nullptr, 2355 0, effect_, *control_); 2356 case wasm::kAstF32: 2357 node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(), 2358 node); 2359 return BuildChangeFloat64ToTagged(node); 2360 case wasm::kAstF64: 2361 return BuildChangeFloat64ToTagged(node); 2362 case wasm::kAstStmt: 2363 return jsgraph()->UndefinedConstant(); 2364 default: 2365 UNREACHABLE(); 2366 return nullptr; 2367 } 2368 } 2369 2370 Node* WasmGraphBuilder::BuildJavaScriptToNumber(Node* node, Node* context, 2371 Node* effect, Node* control) { 2372 Callable callable = CodeFactory::ToNumber(jsgraph()->isolate()); 2373 CallDescriptor* desc = Linkage::GetStubCallDescriptor( 2374 jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, 2375 CallDescriptor::kNoFlags, Operator::kNoProperties); 2376 Node* stub_code = jsgraph()->HeapConstant(callable.code()); 2377 2378 Node* result = graph()->NewNode(jsgraph()->common()->Call(desc), stub_code, 2379 node, context, effect, control); 2380 2381 *effect_ = result; 2382 2383 return result; 2384 } 2385 2386 bool CanCover(Node* value, IrOpcode::Value opcode) { 2387 if (value->opcode() != opcode) return false; 2388 bool first = true; 2389 for (Edge const edge : value->use_edges()) { 2390 if (NodeProperties::IsControlEdge(edge)) continue; 2391 if (NodeProperties::IsEffectEdge(edge)) continue; 2392 DCHECK(NodeProperties::IsValueEdge(edge)); 2393 if (!first) return false; 2394 first = false; 2395 } 2396 return true; 2397 } 2398 2399 Node* WasmGraphBuilder::BuildChangeTaggedToFloat64(Node* value) { 2400 MachineOperatorBuilder* machine = jsgraph()->machine(); 2401 CommonOperatorBuilder* common = jsgraph()->common(); 2402 2403 if (CanCover(value, IrOpcode::kJSToNumber)) { 2404 // ChangeTaggedToFloat64(JSToNumber(x)) => 2405 // if IsSmi(x) then ChangeSmiToFloat64(x) 2406 // else let y = JSToNumber(x) in 2407 // if IsSmi(y) then ChangeSmiToFloat64(y) 2408 // else BuildLoadHeapNumberValue(y) 2409 Node* object = NodeProperties::GetValueInput(value, 0); 2410 Node* context = NodeProperties::GetContextInput(value); 2411 Node* frame_state = NodeProperties::GetFrameStateInput(value); 2412 Node* effect = NodeProperties::GetEffectInput(value); 2413 Node* control = NodeProperties::GetControlInput(value); 2414 2415 const Operator* merge_op = common->Merge(2); 2416 const Operator* ephi_op = common->EffectPhi(2); 2417 const Operator* phi_op = common->Phi(MachineRepresentation::kFloat64, 2); 2418 2419 Node* check1 = BuildTestNotSmi(object); 2420 Node* branch1 = 2421 graph()->NewNode(common->Branch(BranchHint::kFalse), check1, control); 2422 2423 Node* if_true1 = graph()->NewNode(common->IfTrue(), branch1); 2424 Node* vtrue1 = graph()->NewNode(value->op(), object, context, frame_state, 2425 effect, if_true1); 2426 Node* etrue1 = vtrue1; 2427 2428 Node* check2 = BuildTestNotSmi(vtrue1); 2429 Node* branch2 = graph()->NewNode(common->Branch(), check2, if_true1); 2430 2431 Node* if_true2 = graph()->NewNode(common->IfTrue(), branch2); 2432 Node* vtrue2 = BuildLoadHeapNumberValue(vtrue1, if_true2); 2433 2434 Node* if_false2 = graph()->NewNode(common->IfFalse(), branch2); 2435 Node* vfalse2 = BuildChangeSmiToFloat64(vtrue1); 2436 2437 if_true1 = graph()->NewNode(merge_op, if_true2, if_false2); 2438 vtrue1 = graph()->NewNode(phi_op, vtrue2, vfalse2, if_true1); 2439 2440 Node* if_false1 = graph()->NewNode(common->IfFalse(), branch1); 2441 Node* vfalse1 = BuildChangeSmiToFloat64(object); 2442 Node* efalse1 = effect; 2443 2444 Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1); 2445 Node* ephi1 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1); 2446 Node* phi1 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1); 2447 2448 // Wire the new diamond into the graph, {JSToNumber} can still throw. 2449 NodeProperties::ReplaceUses(value, phi1, ephi1, etrue1, etrue1); 2450 2451 // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from 2452 // the node and places it inside the diamond. Come up with a helper method! 2453 for (Node* use : etrue1->uses()) { 2454 if (use->opcode() == IrOpcode::kIfSuccess) { 2455 use->ReplaceUses(merge1); 2456 NodeProperties::ReplaceControlInput(branch2, use); 2457 } 2458 } 2459 return phi1; 2460 } 2461 2462 Node* check = BuildTestNotSmi(value); 2463 Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check, 2464 graph()->start()); 2465 2466 Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch); 2467 2468 Node* vnot_smi; 2469 Node* check_undefined = graph()->NewNode(machine->WordEqual(), value, 2470 jsgraph()->UndefinedConstant()); 2471 Node* branch_undefined = graph()->NewNode(common->Branch(BranchHint::kFalse), 2472 check_undefined, if_not_smi); 2473 2474 Node* if_undefined = graph()->NewNode(common->IfTrue(), branch_undefined); 2475 Node* vundefined = 2476 jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN()); 2477 2478 Node* if_not_undefined = 2479 graph()->NewNode(common->IfFalse(), branch_undefined); 2480 Node* vheap_number = BuildLoadHeapNumberValue(value, if_not_undefined); 2481 2482 if_not_smi = 2483 graph()->NewNode(common->Merge(2), if_undefined, if_not_undefined); 2484 vnot_smi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), 2485 vundefined, vheap_number, if_not_smi); 2486 2487 Node* if_smi = graph()->NewNode(common->IfFalse(), branch); 2488 Node* vfrom_smi = BuildChangeSmiToFloat64(value); 2489 2490 Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi); 2491 Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), 2492 vnot_smi, vfrom_smi, merge); 2493 2494 return phi; 2495 } 2496 2497 Node* WasmGraphBuilder::FromJS(Node* node, Node* context, 2498 wasm::LocalType type) { 2499 // Do a JavaScript ToNumber. 2500 Node* num = BuildJavaScriptToNumber(node, context, *effect_, *control_); 2501 2502 // Change representation. 2503 SimplifiedOperatorBuilder simplified(jsgraph()->zone()); 2504 num = BuildChangeTaggedToFloat64(num); 2505 2506 switch (type) { 2507 case wasm::kAstI32: { 2508 num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(), 2509 num); 2510 break; 2511 } 2512 case wasm::kAstS128: 2513 case wasm::kAstI64: 2514 // Throw a TypeError. The native context is good enough here because we 2515 // only throw a TypeError. 2516 return BuildCallToRuntime(Runtime::kWasmThrowTypeError, jsgraph(), 2517 jsgraph()->isolate()->native_context(), nullptr, 2518 0, effect_, *control_); 2519 case wasm::kAstF32: 2520 num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToFloat32(), 2521 num); 2522 break; 2523 case wasm::kAstF64: 2524 break; 2525 case wasm::kAstStmt: 2526 num = jsgraph()->Int32Constant(0); 2527 break; 2528 default: 2529 UNREACHABLE(); 2530 return nullptr; 2531 } 2532 return num; 2533 } 2534 2535 Node* WasmGraphBuilder::BuildChangeInt32ToSmi(Node* value) { 2536 if (jsgraph()->machine()->Is64()) { 2537 value = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), value); 2538 } 2539 return graph()->NewNode(jsgraph()->machine()->WordShl(), value, 2540 BuildSmiShiftBitsConstant()); 2541 } 2542 2543 Node* WasmGraphBuilder::BuildChangeSmiToInt32(Node* value) { 2544 value = graph()->NewNode(jsgraph()->machine()->WordSar(), value, 2545 BuildSmiShiftBitsConstant()); 2546 if (jsgraph()->machine()->Is64()) { 2547 value = 2548 graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), value); 2549 } 2550 return value; 2551 } 2552 2553 Node* WasmGraphBuilder::BuildChangeUint32ToSmi(Node* value) { 2554 if (jsgraph()->machine()->Is64()) { 2555 value = 2556 graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), value); 2557 } 2558 return graph()->NewNode(jsgraph()->machine()->WordShl(), value, 2559 BuildSmiShiftBitsConstant()); 2560 } 2561 2562 Node* WasmGraphBuilder::BuildChangeSmiToFloat64(Node* value) { 2563 return graph()->NewNode(jsgraph()->machine()->ChangeInt32ToFloat64(), 2564 BuildChangeSmiToInt32(value)); 2565 } 2566 2567 Node* WasmGraphBuilder::BuildTestNotSmi(Node* value) { 2568 STATIC_ASSERT(kSmiTag == 0); 2569 STATIC_ASSERT(kSmiTagMask == 1); 2570 return graph()->NewNode(jsgraph()->machine()->WordAnd(), value, 2571 jsgraph()->IntPtrConstant(kSmiTagMask)); 2572 } 2573 2574 Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() { 2575 return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); 2576 } 2577 2578 Node* WasmGraphBuilder::BuildAllocateHeapNumberWithValue(Node* value, 2579 Node* control) { 2580 MachineOperatorBuilder* machine = jsgraph()->machine(); 2581 CommonOperatorBuilder* common = jsgraph()->common(); 2582 // The AllocateHeapNumberStub does not use the context, so we can safely pass 2583 // in Smi zero here. 2584 Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate()); 2585 Node* target = jsgraph()->HeapConstant(callable.code()); 2586 Node* context = jsgraph()->NoContextConstant(); 2587 Node* effect = 2588 graph()->NewNode(common->BeginRegion(RegionObservability::kNotObservable), 2589 graph()->start()); 2590 if (!allocate_heap_number_operator_.is_set()) { 2591 CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( 2592 jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, 2593 CallDescriptor::kNoFlags, Operator::kNoThrow); 2594 allocate_heap_number_operator_.set(common->Call(descriptor)); 2595 } 2596 Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(), 2597 target, context, effect, control); 2598 Node* store = 2599 graph()->NewNode(machine->Store(StoreRepresentation( 2600 MachineRepresentation::kFloat64, kNoWriteBarrier)), 2601 heap_number, BuildHeapNumberValueIndexConstant(), value, 2602 heap_number, control); 2603 return graph()->NewNode(common->FinishRegion(), heap_number, store); 2604 } 2605 2606 Node* WasmGraphBuilder::BuildLoadHeapNumberValue(Node* value, Node* control) { 2607 return graph()->NewNode(jsgraph()->machine()->Load(MachineType::Float64()), 2608 value, BuildHeapNumberValueIndexConstant(), 2609 graph()->start(), control); 2610 } 2611 2612 Node* WasmGraphBuilder::BuildHeapNumberValueIndexConstant() { 2613 return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag); 2614 } 2615 2616 void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code, 2617 wasm::FunctionSig* sig) { 2618 int wasm_count = static_cast<int>(sig->parameter_count()); 2619 int param_count; 2620 if (jsgraph()->machine()->Is64()) { 2621 param_count = static_cast<int>(sig->parameter_count()); 2622 } else { 2623 param_count = Int64Lowering::GetParameterCountAfterLowering(sig); 2624 } 2625 int count = param_count + 3; 2626 Node** args = Buffer(count); 2627 2628 // Build the start and the JS parameter nodes. 2629 Node* start = Start(param_count + 5); 2630 *control_ = start; 2631 *effect_ = start; 2632 // Create the context parameter 2633 Node* context = graph()->NewNode( 2634 jsgraph()->common()->Parameter( 2635 Linkage::GetJSCallContextParamIndex(wasm_count + 1), "%context"), 2636 graph()->start()); 2637 2638 int pos = 0; 2639 args[pos++] = HeapConstant(wasm_code); 2640 2641 // Convert JS parameters to WASM numbers. 2642 for (int i = 0; i < wasm_count; ++i) { 2643 Node* param = 2644 graph()->NewNode(jsgraph()->common()->Parameter(i + 1), start); 2645 Node* wasm_param = FromJS(param, context, sig->GetParam(i)); 2646 args[pos++] = wasm_param; 2647 if (jsgraph()->machine()->Is32() && sig->GetParam(i) == wasm::kAstI64) { 2648 // We make up the high word with SAR to get the proper sign extension. 2649 args[pos++] = graph()->NewNode(jsgraph()->machine()->Word32Sar(), 2650 wasm_param, jsgraph()->Int32Constant(31)); 2651 } 2652 } 2653 2654 args[pos++] = *effect_; 2655 args[pos++] = *control_; 2656 2657 // Call the WASM code. 2658 CallDescriptor* desc = 2659 wasm::ModuleEnv::GetWasmCallDescriptor(jsgraph()->zone(), sig); 2660 if (jsgraph()->machine()->Is32()) { 2661 desc = wasm::ModuleEnv::GetI32WasmCallDescriptor(jsgraph()->zone(), desc); 2662 } 2663 Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), count, args); 2664 Node* retval = call; 2665 if (jsgraph()->machine()->Is32() && sig->return_count() > 0 && 2666 sig->GetReturn(0) == wasm::kAstI64) { 2667 // The return values comes as two values, we pick the low word. 2668 retval = graph()->NewNode(jsgraph()->common()->Projection(0), retval, 2669 graph()->start()); 2670 } 2671 Node* jsval = ToJS( 2672 retval, sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn()); 2673 Node* ret = graph()->NewNode(jsgraph()->common()->Return(), 2674 jsgraph()->Int32Constant(0), jsval, call, start); 2675 2676 MergeControlToEnd(jsgraph(), ret); 2677 } 2678 2679 int WasmGraphBuilder::AddParameterNodes(Node** args, int pos, int param_count, 2680 wasm::FunctionSig* sig) { 2681 // Convert WASM numbers to JS values. 2682 int param_index = 0; 2683 for (int i = 0; i < param_count; ++i) { 2684 Node* param = graph()->NewNode( 2685 jsgraph()->common()->Parameter(param_index++), graph()->start()); 2686 args[pos++] = ToJS(param, sig->GetParam(i)); 2687 if (jsgraph()->machine()->Is32() && sig->GetParam(i) == wasm::kAstI64) { 2688 // On 32 bit platforms we have to skip the high word of int64 2689 // parameters. 2690 param_index++; 2691 } 2692 } 2693 return pos; 2694 } 2695 2696 void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target, 2697 wasm::FunctionSig* sig) { 2698 DCHECK(target->IsCallable()); 2699 2700 int wasm_count = static_cast<int>(sig->parameter_count()); 2701 int param_count; 2702 if (jsgraph()->machine()->Is64()) { 2703 param_count = wasm_count; 2704 } else { 2705 param_count = Int64Lowering::GetParameterCountAfterLowering(sig); 2706 } 2707 2708 // Build the start and the parameter nodes. 2709 Isolate* isolate = jsgraph()->isolate(); 2710 CallDescriptor* desc; 2711 Node* start = Start(param_count + 3); 2712 *effect_ = start; 2713 *control_ = start; 2714 Node** args = Buffer(wasm_count + 7); 2715 2716 Node* call; 2717 bool direct_call = false; 2718 2719 if (target->IsJSFunction()) { 2720 Handle<JSFunction> function = Handle<JSFunction>::cast(target); 2721 if (function->shared()->internal_formal_parameter_count() == wasm_count) { 2722 direct_call = true; 2723 int pos = 0; 2724 args[pos++] = jsgraph()->Constant(target); // target callable. 2725 // Receiver. 2726 if (is_sloppy(function->shared()->language_mode()) && 2727 !function->shared()->native()) { 2728 args[pos++] = 2729 HeapConstant(handle(function->context()->global_proxy(), isolate)); 2730 } else { 2731 args[pos++] = jsgraph()->Constant( 2732 handle(isolate->heap()->undefined_value(), isolate)); 2733 } 2734 2735 desc = Linkage::GetJSCallDescriptor( 2736 graph()->zone(), false, wasm_count + 1, CallDescriptor::kNoFlags); 2737 2738 // Convert WASM numbers to JS values. 2739 pos = AddParameterNodes(args, pos, wasm_count, sig); 2740 2741 args[pos++] = jsgraph()->UndefinedConstant(); // new target 2742 args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count 2743 args[pos++] = HeapConstant(handle(function->context())); 2744 args[pos++] = *effect_; 2745 args[pos++] = *control_; 2746 2747 call = graph()->NewNode(jsgraph()->common()->Call(desc), pos, args); 2748 } 2749 } 2750 2751 // We cannot call the target directly, we have to use the Call builtin. 2752 if (!direct_call) { 2753 int pos = 0; 2754 Callable callable = CodeFactory::Call(isolate); 2755 args[pos++] = jsgraph()->HeapConstant(callable.code()); 2756 args[pos++] = jsgraph()->Constant(target); // target callable 2757 args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count 2758 args[pos++] = jsgraph()->Constant( 2759 handle(isolate->heap()->undefined_value(), isolate)); // receiver 2760 2761 desc = Linkage::GetStubCallDescriptor(isolate, graph()->zone(), 2762 callable.descriptor(), wasm_count + 1, 2763 CallDescriptor::kNoFlags); 2764 2765 // Convert WASM numbers to JS values. 2766 pos = AddParameterNodes(args, pos, wasm_count, sig); 2767 2768 // The native_context is sufficient here, because all kind of callables 2769 // which depend on the context provide their own context. The context here 2770 // is only needed if the target is a constructor to throw a TypeError, if 2771 // the target is a native function, or if the target is a callable JSObject, 2772 // which can only be constructed by the runtime. 2773 args[pos++] = HeapConstant(isolate->native_context()); 2774 args[pos++] = *effect_; 2775 args[pos++] = *control_; 2776 2777 call = graph()->NewNode(jsgraph()->common()->Call(desc), pos, args); 2778 } 2779 2780 // Convert the return value back. 2781 Node* ret; 2782 Node* val = 2783 FromJS(call, HeapConstant(isolate->native_context()), 2784 sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn()); 2785 Node* pop_size = jsgraph()->Int32Constant(0); 2786 if (jsgraph()->machine()->Is32() && sig->return_count() > 0 && 2787 sig->GetReturn() == wasm::kAstI64) { 2788 ret = graph()->NewNode(jsgraph()->common()->Return(), pop_size, val, 2789 graph()->NewNode(jsgraph()->machine()->Word32Sar(), 2790 val, jsgraph()->Int32Constant(31)), 2791 call, start); 2792 } else { 2793 ret = graph()->NewNode(jsgraph()->common()->Return(), pop_size, val, call, 2794 start); 2795 } 2796 2797 MergeControlToEnd(jsgraph(), ret); 2798 } 2799 2800 Node* WasmGraphBuilder::MemBuffer(uint32_t offset) { 2801 DCHECK(module_ && module_->instance); 2802 if (offset == 0) { 2803 if (!mem_buffer_) { 2804 mem_buffer_ = jsgraph()->RelocatableIntPtrConstant( 2805 reinterpret_cast<uintptr_t>(module_->instance->mem_start), 2806 RelocInfo::WASM_MEMORY_REFERENCE); 2807 } 2808 return mem_buffer_; 2809 } else { 2810 return jsgraph()->RelocatableIntPtrConstant( 2811 reinterpret_cast<uintptr_t>(module_->instance->mem_start + offset), 2812 RelocInfo::WASM_MEMORY_REFERENCE); 2813 } 2814 } 2815 2816 Node* WasmGraphBuilder::CurrentMemoryPages() { 2817 Runtime::FunctionId function_id = Runtime::kWasmMemorySize; 2818 const Runtime::Function* function = Runtime::FunctionForId(function_id); 2819 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( 2820 jsgraph()->zone(), function_id, function->nargs, Operator::kNoThrow, 2821 CallDescriptor::kNoFlags); 2822 wasm::ModuleEnv* module = module_; 2823 Node* inputs[] = { 2824 jsgraph()->CEntryStubConstant(function->result_size), // C entry 2825 jsgraph()->ExternalConstant( 2826 ExternalReference(function_id, jsgraph()->isolate())), // ref 2827 jsgraph()->Int32Constant(function->nargs), // arity 2828 jsgraph()->HeapConstant(module->instance->context), // context 2829 *effect_, 2830 *control_}; 2831 Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), 2832 static_cast<int>(arraysize(inputs)), inputs); 2833 2834 Node* result = BuildChangeSmiToInt32(call); 2835 2836 *effect_ = call; 2837 return result; 2838 } 2839 2840 Node* WasmGraphBuilder::MemSize(uint32_t offset) { 2841 DCHECK(module_ && module_->instance); 2842 uint32_t size = static_cast<uint32_t>(module_->instance->mem_size); 2843 if (offset == 0) { 2844 if (!mem_size_) 2845 mem_size_ = jsgraph()->RelocatableInt32Constant( 2846 size, RelocInfo::WASM_MEMORY_SIZE_REFERENCE); 2847 return mem_size_; 2848 } else { 2849 return jsgraph()->RelocatableInt32Constant( 2850 size + offset, RelocInfo::WASM_MEMORY_SIZE_REFERENCE); 2851 } 2852 } 2853 2854 void WasmGraphBuilder::EnsureFunctionTableNodes() { 2855 if (function_tables_.size() > 0) return; 2856 for (size_t i = 0; i < module_->instance->function_tables.size(); ++i) { 2857 auto handle = module_->instance->function_tables[i]; 2858 DCHECK(!handle.is_null()); 2859 function_tables_.push_back(HeapConstant(handle)); 2860 uint32_t table_size = module_->module->function_tables[i].min_size; 2861 function_table_sizes_.push_back(Uint32Constant(table_size)); 2862 } 2863 } 2864 2865 Node* WasmGraphBuilder::GetGlobal(uint32_t index) { 2866 MachineType mem_type = 2867 wasm::WasmOpcodes::MachineTypeFor(module_->GetGlobalType(index)); 2868 Node* addr = jsgraph()->RelocatableIntPtrConstant( 2869 reinterpret_cast<uintptr_t>(module_->instance->globals_start + 2870 module_->module->globals[index].offset), 2871 RelocInfo::WASM_GLOBAL_REFERENCE); 2872 const Operator* op = jsgraph()->machine()->Load(mem_type); 2873 Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), *effect_, 2874 *control_); 2875 *effect_ = node; 2876 return node; 2877 } 2878 2879 Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) { 2880 MachineType mem_type = 2881 wasm::WasmOpcodes::MachineTypeFor(module_->GetGlobalType(index)); 2882 Node* addr = jsgraph()->RelocatableIntPtrConstant( 2883 reinterpret_cast<uintptr_t>(module_->instance->globals_start + 2884 module_->module->globals[index].offset), 2885 RelocInfo::WASM_GLOBAL_REFERENCE); 2886 const Operator* op = jsgraph()->machine()->Store( 2887 StoreRepresentation(mem_type.representation(), kNoWriteBarrier)); 2888 Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), val, 2889 *effect_, *control_); 2890 *effect_ = node; 2891 return node; 2892 } 2893 2894 void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index, 2895 uint32_t offset, 2896 wasm::WasmCodePosition position) { 2897 DCHECK(module_ && module_->instance); 2898 uint32_t size = module_->instance->mem_size; 2899 byte memsize = wasm::WasmOpcodes::MemSize(memtype); 2900 2901 size_t effective_size; 2902 if (size <= offset || size < (static_cast<uint64_t>(offset) + memsize)) { 2903 // Two checks are needed in the case where the offset is statically 2904 // out of bounds; one check for the offset being in bounds, and the next for 2905 // the offset + index being out of bounds for code to be patched correctly 2906 // on relocation. 2907 2908 // Check for overflows. 2909 if ((std::numeric_limits<uint32_t>::max() - memsize) + 1 < offset) { 2910 // Always trap. Do not use TrapAlways because it does not create a valid 2911 // graph here. 2912 trap_->TrapIfEq32(wasm::kTrapMemOutOfBounds, jsgraph()->Int32Constant(0), 2913 0, position); 2914 return; 2915 } 2916 size_t effective_offset = (offset - 1) + memsize; 2917 2918 Node* cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), 2919 jsgraph()->IntPtrConstant(effective_offset), 2920 jsgraph()->RelocatableInt32Constant( 2921 static_cast<uint32_t>(size), 2922 RelocInfo::WASM_MEMORY_SIZE_REFERENCE)); 2923 trap_->AddTrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); 2924 // For offset > effective size, this relies on check above to fail and 2925 // effective size can be negative, relies on wrap around. 2926 effective_size = size - offset - memsize + 1; 2927 } else { 2928 effective_size = size - offset - memsize + 1; 2929 CHECK(effective_size <= kMaxUInt32); 2930 2931 Uint32Matcher m(index); 2932 if (m.HasValue()) { 2933 uint32_t value = m.Value(); 2934 if (value < effective_size) { 2935 // The bounds check will always succeed. 2936 return; 2937 } 2938 } 2939 } 2940 2941 Node* cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), index, 2942 jsgraph()->RelocatableInt32Constant( 2943 static_cast<uint32_t>(effective_size), 2944 RelocInfo::WASM_MEMORY_SIZE_REFERENCE)); 2945 trap_->AddTrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); 2946 } 2947 2948 2949 Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype, 2950 Node* index, uint32_t offset, 2951 uint32_t alignment, 2952 wasm::WasmCodePosition position) { 2953 Node* load; 2954 2955 // WASM semantics throw on OOB. Introduce explicit bounds check. 2956 if (!FLAG_wasm_trap_handler) { 2957 BoundsCheckMem(memtype, index, offset, position); 2958 } 2959 bool aligned = static_cast<int>(alignment) >= 2960 ElementSizeLog2Of(memtype.representation()); 2961 2962 if (aligned || 2963 jsgraph()->machine()->UnalignedLoadSupported(memtype, alignment)) { 2964 if (FLAG_wasm_trap_handler) { 2965 Node* context = HeapConstant(module_->instance->context); 2966 Node* position_node = jsgraph()->Int32Constant(position); 2967 load = graph()->NewNode(jsgraph()->machine()->ProtectedLoad(memtype), 2968 MemBuffer(offset), index, context, position_node, 2969 *effect_, *control_); 2970 } else { 2971 load = graph()->NewNode(jsgraph()->machine()->Load(memtype), 2972 MemBuffer(offset), index, *effect_, *control_); 2973 } 2974 } else { 2975 DCHECK(!FLAG_wasm_trap_handler); 2976 load = graph()->NewNode(jsgraph()->machine()->UnalignedLoad(memtype), 2977 MemBuffer(offset), index, *effect_, *control_); 2978 } 2979 2980 *effect_ = load; 2981 2982 #if defined(V8_TARGET_BIG_ENDIAN) 2983 load = BuildChangeEndianness(load, memtype, type); 2984 #endif 2985 2986 if (type == wasm::kAstI64 && 2987 ElementSizeLog2Of(memtype.representation()) < 3) { 2988 // TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes. 2989 if (memtype.IsSigned()) { 2990 // sign extend 2991 load = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), load); 2992 } else { 2993 // zero extend 2994 load = 2995 graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), load); 2996 } 2997 } 2998 2999 return load; 3000 } 3001 3002 3003 Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index, 3004 uint32_t offset, uint32_t alignment, Node* val, 3005 wasm::WasmCodePosition position) { 3006 Node* store; 3007 3008 // WASM semantics throw on OOB. Introduce explicit bounds check. 3009 BoundsCheckMem(memtype, index, offset, position); 3010 StoreRepresentation rep(memtype.representation(), kNoWriteBarrier); 3011 3012 bool aligned = static_cast<int>(alignment) >= 3013 ElementSizeLog2Of(memtype.representation()); 3014 3015 #if defined(V8_TARGET_BIG_ENDIAN) 3016 val = BuildChangeEndianness(val, memtype); 3017 #endif 3018 3019 if (aligned || 3020 jsgraph()->machine()->UnalignedStoreSupported(memtype, alignment)) { 3021 StoreRepresentation rep(memtype.representation(), kNoWriteBarrier); 3022 store = 3023 graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset), 3024 index, val, *effect_, *control_); 3025 } else { 3026 UnalignedStoreRepresentation rep(memtype.representation()); 3027 store = 3028 graph()->NewNode(jsgraph()->machine()->UnalignedStore(rep), 3029 MemBuffer(offset), index, val, *effect_, *control_); 3030 } 3031 3032 *effect_ = store; 3033 3034 return store; 3035 } 3036 3037 Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) { 3038 // TODO(turbofan): fold bounds checks for constant asm.js loads. 3039 // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish). 3040 const Operator* op = jsgraph()->machine()->CheckedLoad(type); 3041 Node* load = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), *effect_, 3042 *control_); 3043 *effect_ = load; 3044 return load; 3045 } 3046 3047 Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index, 3048 Node* val) { 3049 // TODO(turbofan): fold bounds checks for constant asm.js stores. 3050 // asm.js semantics use CheckedStore (i.e. ignore OOB writes). 3051 const Operator* op = 3052 jsgraph()->machine()->CheckedStore(type.representation()); 3053 Node* store = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val, 3054 *effect_, *control_); 3055 *effect_ = store; 3056 return val; 3057 } 3058 3059 void WasmGraphBuilder::PrintDebugName(Node* node) { 3060 PrintF("#%d:%s", node->id(), node->op()->mnemonic()); 3061 } 3062 3063 Node* WasmGraphBuilder::String(const char* string) { 3064 return jsgraph()->Constant( 3065 jsgraph()->isolate()->factory()->NewStringFromAsciiChecked(string)); 3066 } 3067 3068 Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); } 3069 3070 void WasmGraphBuilder::Int64LoweringForTesting() { 3071 if (jsgraph()->machine()->Is32()) { 3072 Int64Lowering r(jsgraph()->graph(), jsgraph()->machine(), 3073 jsgraph()->common(), jsgraph()->zone(), 3074 function_signature_); 3075 r.LowerGraph(); 3076 } 3077 } 3078 3079 void WasmGraphBuilder::SimdScalarLoweringForTesting() { 3080 SimdScalarLowering(jsgraph()->graph(), jsgraph()->machine(), 3081 jsgraph()->common(), jsgraph()->zone(), 3082 function_signature_) 3083 .LowerGraph(); 3084 } 3085 3086 void WasmGraphBuilder::SetSourcePosition(Node* node, 3087 wasm::WasmCodePosition position) { 3088 DCHECK_NE(position, wasm::kNoCodePosition); 3089 if (source_position_table_) 3090 source_position_table_->SetSourcePosition(node, SourcePosition(position)); 3091 } 3092 3093 Node* WasmGraphBuilder::CreateS128Value(int32_t value) { 3094 // TODO(gdeepti): Introduce Simd128Constant to common-operator.h and use 3095 // instead of creating a SIMD Value. 3096 return graph()->NewNode(jsgraph()->machine()->CreateInt32x4(), 3097 Int32Constant(value), Int32Constant(value), 3098 Int32Constant(value), Int32Constant(value)); 3099 } 3100 3101 Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, 3102 const NodeVector& inputs) { 3103 switch (opcode) { 3104 case wasm::kExprI32x4Splat: 3105 return graph()->NewNode(jsgraph()->machine()->CreateInt32x4(), inputs[0], 3106 inputs[0], inputs[0], inputs[0]); 3107 case wasm::kExprI32x4Add: 3108 return graph()->NewNode(jsgraph()->machine()->Int32x4Add(), inputs[0], 3109 inputs[1]); 3110 case wasm::kExprF32x4ExtractLane: 3111 return graph()->NewNode(jsgraph()->machine()->Float32x4ExtractLane(), 3112 inputs[0], inputs[1]); 3113 case wasm::kExprF32x4Splat: 3114 return graph()->NewNode(jsgraph()->machine()->CreateFloat32x4(), 3115 inputs[0], inputs[0], inputs[0], inputs[0]); 3116 case wasm::kExprF32x4Add: 3117 return graph()->NewNode(jsgraph()->machine()->Float32x4Add(), inputs[0], 3118 inputs[1]); 3119 default: 3120 return graph()->NewNode(UnsupportedOpcode(opcode), nullptr); 3121 } 3122 } 3123 3124 Node* WasmGraphBuilder::SimdExtractLane(wasm::WasmOpcode opcode, uint8_t lane, 3125 Node* input) { 3126 switch (opcode) { 3127 case wasm::kExprI32x4ExtractLane: 3128 return graph()->NewNode(jsgraph()->machine()->Int32x4ExtractLane(), input, 3129 Int32Constant(lane)); 3130 case wasm::kExprF32x4ExtractLane: 3131 return graph()->NewNode(jsgraph()->machine()->Float32x4ExtractLane(), 3132 input, Int32Constant(lane)); 3133 default: 3134 return graph()->NewNode(UnsupportedOpcode(opcode), nullptr); 3135 } 3136 } 3137 3138 static void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag, 3139 Isolate* isolate, Handle<Code> code, 3140 const char* message, uint32_t index, 3141 const wasm::WasmName& module_name, 3142 const wasm::WasmName& func_name) { 3143 DCHECK(isolate->logger()->is_logging_code_events() || 3144 isolate->is_profiling()); 3145 3146 ScopedVector<char> buffer(128); 3147 SNPrintF(buffer, "%s#%d:%.*s:%.*s", message, index, module_name.length(), 3148 module_name.start(), func_name.length(), func_name.start()); 3149 Handle<String> name_str = 3150 isolate->factory()->NewStringFromAsciiChecked(buffer.start()); 3151 Handle<String> script_str = 3152 isolate->factory()->NewStringFromAsciiChecked("(WASM)"); 3153 Handle<SharedFunctionInfo> shared = 3154 isolate->factory()->NewSharedFunctionInfo(name_str, code, false); 3155 PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *shared, 3156 *script_str, 0, 0)); 3157 } 3158 3159 Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::ModuleEnv* module, 3160 Handle<Code> wasm_code, uint32_t index) { 3161 const wasm::WasmFunction* func = &module->module->functions[index]; 3162 3163 //---------------------------------------------------------------------------- 3164 // Create the Graph 3165 //---------------------------------------------------------------------------- 3166 Zone zone(isolate->allocator(), ZONE_NAME); 3167 Graph graph(&zone); 3168 CommonOperatorBuilder common(&zone); 3169 MachineOperatorBuilder machine(&zone); 3170 JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); 3171 3172 Node* control = nullptr; 3173 Node* effect = nullptr; 3174 3175 WasmGraphBuilder builder(&zone, &jsgraph, func->sig); 3176 builder.set_control_ptr(&control); 3177 builder.set_effect_ptr(&effect); 3178 builder.set_module(module); 3179 builder.BuildJSToWasmWrapper(wasm_code, func->sig); 3180 3181 //---------------------------------------------------------------------------- 3182 // Run the compilation pipeline. 3183 //---------------------------------------------------------------------------- 3184 if (FLAG_trace_turbo_graph) { // Simple textual RPO. 3185 OFStream os(stdout); 3186 os << "-- Graph after change lowering -- " << std::endl; 3187 os << AsRPO(graph); 3188 } 3189 3190 // Schedule and compile to machine code. 3191 int params = 3192 static_cast<int>(module->GetFunctionSignature(index)->parameter_count()); 3193 CallDescriptor* incoming = Linkage::GetJSCallDescriptor( 3194 &zone, false, params + 1, CallDescriptor::kNoFlags); 3195 Code::Flags flags = Code::ComputeFlags(Code::JS_TO_WASM_FUNCTION); 3196 bool debugging = 3197 #if DEBUG 3198 true; 3199 #else 3200 FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph; 3201 #endif 3202 Vector<const char> func_name = ArrayVector("js-to-wasm"); 3203 3204 static unsigned id = 0; 3205 Vector<char> buffer; 3206 if (debugging) { 3207 buffer = Vector<char>::New(128); 3208 int chars = SNPrintF(buffer, "js-to-wasm#%d", id); 3209 func_name = Vector<const char>::cast(buffer.SubVector(0, chars)); 3210 } 3211 3212 CompilationInfo info(func_name, isolate, &zone, flags); 3213 Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph); 3214 #ifdef ENABLE_DISASSEMBLER 3215 if (FLAG_print_opt_code && !code.is_null()) { 3216 OFStream os(stdout); 3217 code->Disassemble(buffer.start(), os); 3218 } 3219 #endif 3220 if (debugging) { 3221 buffer.Dispose(); 3222 } 3223 3224 if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) { 3225 RecordFunctionCompilation( 3226 CodeEventListener::FUNCTION_TAG, isolate, code, "js-to-wasm", index, 3227 wasm::WasmName("export"), 3228 module->module->GetName(func->name_offset, func->name_length)); 3229 } 3230 return code; 3231 } 3232 3233 Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target, 3234 wasm::FunctionSig* sig, uint32_t index, 3235 Handle<String> module_name, 3236 MaybeHandle<String> import_name) { 3237 //---------------------------------------------------------------------------- 3238 // Create the Graph 3239 //---------------------------------------------------------------------------- 3240 Zone zone(isolate->allocator(), ZONE_NAME); 3241 Graph graph(&zone); 3242 CommonOperatorBuilder common(&zone); 3243 MachineOperatorBuilder machine(&zone); 3244 JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); 3245 3246 Node* control = nullptr; 3247 Node* effect = nullptr; 3248 3249 WasmGraphBuilder builder(&zone, &jsgraph, sig); 3250 builder.set_control_ptr(&control); 3251 builder.set_effect_ptr(&effect); 3252 builder.BuildWasmToJSWrapper(target, sig); 3253 3254 Handle<Code> code = Handle<Code>::null(); 3255 { 3256 if (FLAG_trace_turbo_graph) { // Simple textual RPO. 3257 OFStream os(stdout); 3258 os << "-- Graph after change lowering -- " << std::endl; 3259 os << AsRPO(graph); 3260 } 3261 3262 // Schedule and compile to machine code. 3263 CallDescriptor* incoming = 3264 wasm::ModuleEnv::GetWasmCallDescriptor(&zone, sig); 3265 if (machine.Is32()) { 3266 incoming = wasm::ModuleEnv::GetI32WasmCallDescriptor(&zone, incoming); 3267 } 3268 Code::Flags flags = Code::ComputeFlags(Code::WASM_TO_JS_FUNCTION); 3269 bool debugging = 3270 #if DEBUG 3271 true; 3272 #else 3273 FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph; 3274 #endif 3275 Vector<const char> func_name = ArrayVector("wasm-to-js"); 3276 static unsigned id = 0; 3277 Vector<char> buffer; 3278 if (debugging) { 3279 buffer = Vector<char>::New(128); 3280 int chars = SNPrintF(buffer, "wasm-to-js#%d", id); 3281 func_name = Vector<const char>::cast(buffer.SubVector(0, chars)); 3282 } 3283 3284 CompilationInfo info(func_name, isolate, &zone, flags); 3285 code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr); 3286 #ifdef ENABLE_DISASSEMBLER 3287 if (FLAG_print_opt_code && !code.is_null()) { 3288 OFStream os(stdout); 3289 code->Disassemble(buffer.start(), os); 3290 } 3291 #endif 3292 if (debugging) { 3293 buffer.Dispose(); 3294 } 3295 } 3296 if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) { 3297 const char* function_name = nullptr; 3298 int function_name_size = 0; 3299 if (!import_name.is_null()) { 3300 Handle<String> handle = import_name.ToHandleChecked(); 3301 function_name = handle->ToCString().get(); 3302 function_name_size = handle->length(); 3303 } 3304 RecordFunctionCompilation( 3305 CodeEventListener::FUNCTION_TAG, isolate, code, "wasm-to-js", index, 3306 {module_name->ToCString().get(), module_name->length()}, 3307 {function_name, function_name_size}); 3308 } 3309 3310 return code; 3311 } 3312 3313 SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction( 3314 double* decode_ms) { 3315 base::ElapsedTimer decode_timer; 3316 if (FLAG_trace_wasm_decode_time) { 3317 decode_timer.Start(); 3318 } 3319 // Create a TF graph during decoding. 3320 3321 Graph* graph = jsgraph_->graph(); 3322 CommonOperatorBuilder* common = jsgraph_->common(); 3323 MachineOperatorBuilder* machine = jsgraph_->machine(); 3324 SourcePositionTable* source_position_table = 3325 new (jsgraph_->zone()) SourcePositionTable(graph); 3326 WasmGraphBuilder builder(jsgraph_->zone(), jsgraph_, function_->sig, 3327 source_position_table); 3328 wasm::FunctionBody body = { 3329 module_env_, function_->sig, module_env_->module->module_start, 3330 module_env_->module->module_start + function_->code_start_offset, 3331 module_env_->module->module_start + function_->code_end_offset}; 3332 graph_construction_result_ = 3333 wasm::BuildTFGraph(isolate_->allocator(), &builder, body); 3334 3335 if (graph_construction_result_.failed()) { 3336 if (FLAG_trace_wasm_compiler) { 3337 OFStream os(stdout); 3338 os << "Compilation failed: " << graph_construction_result_ << std::endl; 3339 } 3340 return nullptr; 3341 } 3342 3343 if (machine->Is32()) { 3344 Int64Lowering r(graph, machine, common, jsgraph_->zone(), function_->sig); 3345 r.LowerGraph(); 3346 } 3347 3348 SimdScalarLowering(graph, machine, common, jsgraph_->zone(), function_->sig) 3349 .LowerGraph(); 3350 3351 int index = static_cast<int>(function_->func_index); 3352 3353 if (index >= FLAG_trace_wasm_ast_start && index < FLAG_trace_wasm_ast_end) { 3354 OFStream os(stdout); 3355 PrintAst(isolate_->allocator(), body, os, nullptr); 3356 } 3357 if (FLAG_trace_wasm_decode_time) { 3358 *decode_ms = decode_timer.Elapsed().InMillisecondsF(); 3359 } 3360 return source_position_table; 3361 } 3362 3363 WasmCompilationUnit::WasmCompilationUnit(wasm::ErrorThrower* thrower, 3364 Isolate* isolate, 3365 wasm::ModuleEnv* module_env, 3366 const wasm::WasmFunction* function, 3367 uint32_t index) 3368 : thrower_(thrower), 3369 isolate_(isolate), 3370 module_env_(module_env), 3371 function_(function), 3372 graph_zone_(new Zone(isolate->allocator(), ZONE_NAME)), 3373 jsgraph_(new (graph_zone()) JSGraph( 3374 isolate, new (graph_zone()) Graph(graph_zone()), 3375 new (graph_zone()) CommonOperatorBuilder(graph_zone()), nullptr, 3376 nullptr, new (graph_zone()) MachineOperatorBuilder( 3377 graph_zone(), MachineType::PointerRepresentation(), 3378 InstructionSelector::SupportedMachineOperatorFlags(), 3379 InstructionSelector::AlignmentRequirements()))), 3380 compilation_zone_(isolate->allocator(), ZONE_NAME), 3381 info_(function->name_length != 0 3382 ? module_env->module->GetNameOrNull(function->name_offset, 3383 function->name_length) 3384 : ArrayVector("wasm"), 3385 isolate, &compilation_zone_, 3386 Code::ComputeFlags(Code::WASM_FUNCTION)), 3387 job_(), 3388 index_(index), 3389 ok_(true) { 3390 // Create and cache this node in the main thread. 3391 jsgraph_->CEntryStubConstant(1); 3392 } 3393 3394 void WasmCompilationUnit::ExecuteCompilation() { 3395 // TODO(ahaas): The counters are not thread-safe at the moment. 3396 // HistogramTimerScope wasm_compile_function_time_scope( 3397 // isolate_->counters()->wasm_compile_function_time()); 3398 if (FLAG_trace_wasm_compiler) { 3399 OFStream os(stdout); 3400 os << "Compiling WASM function " 3401 << wasm::WasmFunctionName(function_, module_env_) << std::endl; 3402 os << std::endl; 3403 } 3404 3405 double decode_ms = 0; 3406 size_t node_count = 0; 3407 3408 std::unique_ptr<Zone> graph_zone(graph_zone_.release()); 3409 SourcePositionTable* source_positions = BuildGraphForWasmFunction(&decode_ms); 3410 3411 if (graph_construction_result_.failed()) { 3412 ok_ = false; 3413 return; 3414 } 3415 3416 base::ElapsedTimer pipeline_timer; 3417 if (FLAG_trace_wasm_decode_time) { 3418 node_count = jsgraph_->graph()->NodeCount(); 3419 pipeline_timer.Start(); 3420 } 3421 3422 // Run the compiler pipeline to generate machine code. 3423 CallDescriptor* descriptor = wasm::ModuleEnv::GetWasmCallDescriptor( 3424 &compilation_zone_, function_->sig); 3425 if (jsgraph_->machine()->Is32()) { 3426 descriptor = 3427 module_env_->GetI32WasmCallDescriptor(&compilation_zone_, descriptor); 3428 } 3429 job_.reset(Pipeline::NewWasmCompilationJob(&info_, jsgraph_->graph(), 3430 descriptor, source_positions)); 3431 ok_ = job_->ExecuteJob() == CompilationJob::SUCCEEDED; 3432 // TODO(bradnelson): Improve histogram handling of size_t. 3433 // TODO(ahaas): The counters are not thread-safe at the moment. 3434 // isolate_->counters()->wasm_compile_function_peak_memory_bytes() 3435 // ->AddSample( 3436 // static_cast<int>(jsgraph->graph()->zone()->allocation_size())); 3437 3438 if (FLAG_trace_wasm_decode_time) { 3439 double pipeline_ms = pipeline_timer.Elapsed().InMillisecondsF(); 3440 PrintF( 3441 "wasm-compilation phase 1 ok: %d bytes, %0.3f ms decode, %zu nodes, " 3442 "%0.3f ms pipeline\n", 3443 static_cast<int>(function_->code_end_offset - 3444 function_->code_start_offset), 3445 decode_ms, node_count, pipeline_ms); 3446 } 3447 } 3448 3449 Handle<Code> WasmCompilationUnit::FinishCompilation() { 3450 if (!ok_) { 3451 if (graph_construction_result_.failed()) { 3452 // Add the function as another context for the exception 3453 ScopedVector<char> buffer(128); 3454 wasm::WasmName name = module_env_->module->GetName( 3455 function_->name_offset, function_->name_length); 3456 SNPrintF(buffer, "Compiling WASM function #%d:%.*s failed:", 3457 function_->func_index, name.length(), name.start()); 3458 thrower_->CompileFailed(buffer.start(), graph_construction_result_); 3459 } 3460 3461 return Handle<Code>::null(); 3462 } 3463 if (job_->FinalizeJob() != CompilationJob::SUCCEEDED) { 3464 return Handle<Code>::null(); 3465 } 3466 base::ElapsedTimer compile_timer; 3467 if (FLAG_trace_wasm_decode_time) { 3468 compile_timer.Start(); 3469 } 3470 Handle<Code> code = info_.code(); 3471 DCHECK(!code.is_null()); 3472 3473 if (isolate_->logger()->is_logging_code_events() || 3474 isolate_->is_profiling()) { 3475 RecordFunctionCompilation( 3476 CodeEventListener::FUNCTION_TAG, isolate_, code, "WASM_function", 3477 function_->func_index, wasm::WasmName("module"), 3478 module_env_->module->GetName(function_->name_offset, 3479 function_->name_length)); 3480 } 3481 3482 if (FLAG_trace_wasm_decode_time) { 3483 double compile_ms = compile_timer.Elapsed().InMillisecondsF(); 3484 PrintF("wasm-code-generation ok: %d bytes, %0.3f ms code generation\n", 3485 static_cast<int>(function_->code_end_offset - 3486 function_->code_start_offset), 3487 compile_ms); 3488 } 3489 3490 return code; 3491 } 3492 3493 } // namespace compiler 3494 } // namespace internal 3495 } // namespace v8 3496