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