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 {
UnsupportedOpcode(wasm::WasmOpcode opcode)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
MergeControlToEnd(JSGraph * jsgraph,Node * node)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:
WasmTrapHelper(WasmGraphBuilder * builder)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.
Unreachable(wasm::WasmCodePosition position)79 void Unreachable(wasm::WasmCodePosition position) {
80 ConnectTrap(wasm::kTrapUnreachable, position);
81 }
82
83 // Always trap with the given reason.
TrapAlways(wasm::TrapReason reason,wasm::WasmCodePosition position)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}.
TrapIfEq32(wasm::TrapReason reason,Node * node,int32_t val,wasm::WasmCodePosition position)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.
ZeroCheck32(wasm::TrapReason reason,Node * node,wasm::WasmCodePosition position)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}.
TrapIfEq64(wasm::TrapReason reason,Node * node,int64_t val,wasm::WasmCodePosition position)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.
ZeroCheck64(wasm::TrapReason reason,Node * node,wasm::WasmCodePosition position)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.
AddTrapIfTrue(wasm::TrapReason reason,Node * cond,wasm::WasmCodePosition position)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.
AddTrapIfFalse(wasm::TrapReason reason,Node * cond,wasm::WasmCodePosition position)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}.
AddTrapIf(wasm::TrapReason reason,Node * cond,bool iftrue,wasm::WasmCodePosition position)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
GetTrapValue(wasm::FunctionSig * sig)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
jsgraph()187 JSGraph* jsgraph() { return jsgraph_; }
graph()188 Graph* graph() { return jsgraph_->graph(); }
common()189 CommonOperatorBuilder* common() { return jsgraph()->common(); }
190
ConnectTrap(wasm::TrapReason reason,wasm::WasmCodePosition position)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
BuildTrapCode(Node * reason_node,Node * position_node)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
WasmGraphBuilder(Zone * zone,JSGraph * jsgraph,wasm::FunctionSig * function_signature,compiler::SourcePositionTable * source_position_table)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
Error()289 Node* WasmGraphBuilder::Error() { return jsgraph()->Dead(); }
290
Start(unsigned params)291 Node* WasmGraphBuilder::Start(unsigned params) {
292 Node* start = graph()->NewNode(jsgraph()->common()->Start(params));
293 graph()->SetStart(start);
294 return start;
295 }
296
Param(unsigned index,wasm::LocalType type)297 Node* WasmGraphBuilder::Param(unsigned index, wasm::LocalType type) {
298 return graph()->NewNode(jsgraph()->common()->Parameter(index),
299 graph()->start());
300 }
301
Loop(Node * entry)302 Node* WasmGraphBuilder::Loop(Node* entry) {
303 return graph()->NewNode(jsgraph()->common()->Loop(1), entry);
304 }
305
Terminate(Node * effect,Node * control)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
InputCount(Node * node)313 unsigned WasmGraphBuilder::InputCount(Node* node) {
314 return static_cast<unsigned>(node->InputCount());
315 }
316
IsPhiWithMerge(Node * phi,Node * merge)317 bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
318 return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
319 NodeProperties::GetControlInput(phi) == merge;
320 }
321
AppendToMerge(Node * merge,Node * from)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
AppendToPhi(Node * phi,Node * from)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
Merge(unsigned count,Node ** controls)338 Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) {
339 return graph()->NewNode(jsgraph()->common()->Merge(count), count, controls);
340 }
341
Phi(wasm::LocalType type,unsigned count,Node ** vals,Node * control)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
EffectPhi(unsigned count,Node ** effects,Node * control)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
NumberConstant(int32_t value)360 Node* WasmGraphBuilder::NumberConstant(int32_t value) {
361 return jsgraph()->Constant(value);
362 }
363
Int32Constant(int32_t value)364 Node* WasmGraphBuilder::Int32Constant(int32_t value) {
365 return jsgraph()->Int32Constant(value);
366 }
367
Int64Constant(int64_t value)368 Node* WasmGraphBuilder::Int64Constant(int64_t value) {
369 return jsgraph()->Int64Constant(value);
370 }
371
Binop(wasm::WasmOpcode opcode,Node * left,Node * right,wasm::WasmCodePosition position)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
Unop(wasm::WasmOpcode opcode,Node * input,wasm::WasmCodePosition position)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
Float32Constant(float value)904 Node* WasmGraphBuilder::Float32Constant(float value) {
905 return jsgraph()->Float32Constant(value);
906 }
907
Float64Constant(double value)908 Node* WasmGraphBuilder::Float64Constant(double value) {
909 return jsgraph()->Float64Constant(value);
910 }
911
HeapConstant(Handle<HeapObject> value)912 Node* WasmGraphBuilder::HeapConstant(Handle<HeapObject> value) {
913 return jsgraph()->HeapConstant(value);
914 }
915
Branch(Node * cond,Node ** true_node,Node ** false_node)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
Switch(unsigned count,Node * key)927 Node* WasmGraphBuilder::Switch(unsigned count, Node* key) {
928 return graph()->NewNode(jsgraph()->common()->Switch(count), key, *control_);
929 }
930
IfValue(int32_t value,Node * sw)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
IfDefault(Node * sw)936 Node* WasmGraphBuilder::IfDefault(Node* sw) {
937 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
938 return graph()->NewNode(jsgraph()->common()->IfDefault(), sw);
939 }
940
Return(unsigned count,Node ** vals)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
ReturnVoid()960 Node* WasmGraphBuilder::ReturnVoid() { return Return(0, Buffer(0)); }
961
Unreachable(wasm::WasmCodePosition position)962 Node* WasmGraphBuilder::Unreachable(wasm::WasmCodePosition position) {
963 trap_->Unreachable(position);
964 return nullptr;
965 }
966
MaskShiftCount32(Node * node)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
MaskShiftCount64(Node * node)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
BuildF32Neg(Node * input)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
BuildF64Neg(Node * input)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
BuildF32CopySign(Node * left,Node * right)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
BuildF64CopySign(Node * left,Node * right)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
BuildF32Min(Node * left,Node * right)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
BuildF32Max(Node * left,Node * right)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
BuildF64Min(Node * left,Node * right)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
BuildF64Max(Node * left,Node * right)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
BuildI32SConvertF32(Node * input,wasm::WasmCodePosition position)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
BuildI32SConvertF64(Node * input,wasm::WasmCodePosition position)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
BuildI32UConvertF32(Node * input,wasm::WasmCodePosition position)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
BuildI32UConvertF64(Node * input,wasm::WasmCodePosition position)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
BuildI32AsmjsSConvertF32(Node * input)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
BuildI32AsmjsSConvertF64(Node * input)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
BuildI32AsmjsUConvertF32(Node * input)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
BuildI32AsmjsUConvertF64(Node * input)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
BuildBitCountingCall(Node * input,ExternalReference ref,MachineRepresentation input_type)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
BuildI32Ctz(Node * input)1258 Node* WasmGraphBuilder::BuildI32Ctz(Node* input) {
1259 return BuildBitCountingCall(
1260 input, ExternalReference::wasm_word32_ctz(jsgraph()->isolate()),
1261 MachineRepresentation::kWord32);
1262 }
1263
BuildI64Ctz(Node * input)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
BuildI32Popcnt(Node * input)1271 Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) {
1272 return BuildBitCountingCall(
1273 input, ExternalReference::wasm_word32_popcnt(jsgraph()->isolate()),
1274 MachineRepresentation::kWord32);
1275 }
1276
BuildI64Popcnt(Node * input)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
BuildF32Trunc(Node * input)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
BuildF32Floor(Node * input)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
BuildF32Ceil(Node * input)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
BuildF32NearestInt(Node * input)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
BuildF64Trunc(Node * input)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
BuildF64Floor(Node * input)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
BuildF64Ceil(Node * input)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
BuildF64NearestInt(Node * input)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
BuildF64Acos(Node * input)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
BuildF64Asin(Node * input)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
BuildF64Pow(Node * left,Node * right)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
BuildF64Mod(Node * left,Node * right)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
BuildCFuncInstruction(ExternalReference ref,MachineType type,Node * input0,Node * input1)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
BuildF32SConvertI64(Node * input)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 }
BuildF32UConvertI64(Node * input)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 }
BuildF64SConvertI64(Node * input)1435 Node* WasmGraphBuilder::BuildF64SConvertI64(Node* input) {
1436 return BuildIntToFloatConversionInstruction(
1437 input, ExternalReference::wasm_int64_to_float64(jsgraph()->isolate()),
1438 MachineRepresentation::kWord64, MachineType::Float64());
1439 }
BuildF64UConvertI64(Node * input)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
BuildIntToFloatConversionInstruction(Node * input,ExternalReference ref,MachineRepresentation parameter_representation,const MachineType result_type)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
BuildI64SConvertF32(Node * input,wasm::WasmCodePosition position)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
BuildI64UConvertF32(Node * input,wasm::WasmCodePosition position)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
BuildI64SConvertF64(Node * input,wasm::WasmCodePosition position)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
BuildI64UConvertF64(Node * input,wasm::WasmCodePosition position)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
BuildFloatToIntConversionInstruction(Node * input,ExternalReference ref,MachineRepresentation parameter_representation,const MachineType result_type,wasm::WasmCodePosition position)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
BuildI32DivS(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI32RemS(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI32DivU(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI32RemU(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI32AsmjsDivS(Node * left,Node * right)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
BuildI32AsmjsRemS(Node * left,Node * right)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
BuildI32AsmjsDivU(Node * left,Node * right)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
BuildI32AsmjsRemU(Node * left,Node * right)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
BuildI64DivS(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI64RemS(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildI64DivU(Node * left,Node * right,wasm::WasmCodePosition position)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 }
BuildI64RemU(Node * left,Node * right,wasm::WasmCodePosition position)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
BuildDiv64Call(Node * left,Node * right,ExternalReference ref,MachineType result_type,int trap_zero,wasm::WasmCodePosition position)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
BuildCCall(MachineSignature * sig,Node ** args)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
BuildWasmCall(wasm::FunctionSig * sig,Node ** args,wasm::WasmCodePosition position)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
CallDirect(uint32_t index,Node ** args,wasm::WasmCodePosition position)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
CallImport(uint32_t index,Node ** args,wasm::WasmCodePosition position)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
CallIndirect(uint32_t index,Node ** args,wasm::WasmCodePosition position)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
BuildI32Rol(Node * left,Node * right)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
BuildI64Rol(Node * left,Node * right)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
Invert(Node * node)1971 Node* WasmGraphBuilder::Invert(Node* node) {
1972 return Unop(wasm::kExprI32Eqz, node);
1973 }
1974
BuildChangeInt32ToTagged(Node * value)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
BuildChangeFloat64ToTagged(Node * value)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
ToJS(Node * node,Node * context,wasm::LocalType type)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
BuildJavaScriptToNumber(Node * node,Node * context,Node * effect,Node * control)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
CanCover(Node * value,IrOpcode::Value opcode)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
BuildChangeTaggedToFloat64(Node * value)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
FromJS(Node * node,Node * context,wasm::LocalType type)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
BuildChangeInt32ToSmi(Node * value)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
BuildChangeSmiToInt32(Node * value)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
BuildChangeSmiToFloat64(Node * value)2286 Node* WasmGraphBuilder::BuildChangeSmiToFloat64(Node* value) {
2287 return graph()->NewNode(jsgraph()->machine()->ChangeInt32ToFloat64(),
2288 BuildChangeSmiToInt32(value));
2289 }
2290
BuildTestNotSmi(Node * value)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
BuildSmiShiftBitsConstant()2298 Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() {
2299 return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize);
2300 }
2301
BuildAllocateHeapNumberWithValue(Node * value,Node * control)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
BuildLoadHeapNumberValue(Node * value,Node * control)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
BuildHeapNumberValueIndexConstant()2336 Node* WasmGraphBuilder::BuildHeapNumberValueIndexConstant() {
2337 return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag);
2338 }
2339
BuildJSToWasmWrapper(Handle<Code> wasm_code,wasm::FunctionSig * sig)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
BuildWasmToJSWrapper(Handle<JSFunction> function,wasm::FunctionSig * sig)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
MemBuffer(uint32_t offset)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
MemSize(uint32_t offset)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
FunctionTable()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
LoadGlobal(uint32_t index)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
StoreGlobal(uint32_t index,Node * val)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
BoundsCheckMem(MachineType memtype,Node * index,uint32_t offset,wasm::WasmCodePosition position)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
GetTypeForUnalignedAccess(uint32_t alignment,bool signExtend)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
GetUnalignedLoadOffsetNode(Node * baseOffset,int numberOfBytes,int stride,int current)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
BuildUnalignedLoad(wasm::LocalType type,MachineType memtype,Node * index,uint32_t offset,uint32_t alignment)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
LoadMem(wasm::LocalType type,MachineType memtype,Node * index,uint32_t offset,uint32_t alignment,wasm::WasmCodePosition position)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
GetUnalignedStoreOffsetNode(Node * baseOffset,int numberOfBytes,int stride,int current)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
BuildUnalignedStore(MachineType memtype,Node * index,uint32_t offset,uint32_t alignment,Node * val)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
StoreMem(MachineType memtype,Node * index,uint32_t offset,uint32_t alignment,Node * val,wasm::WasmCodePosition position)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
BuildAsmjsLoadMem(MachineType type,Node * index)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
BuildAsmjsStoreMem(MachineType type,Node * index,Node * val)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
PrintDebugName(Node * node)2893 void WasmGraphBuilder::PrintDebugName(Node* node) {
2894 PrintF("#%d:%s", node->id(), node->op()->mnemonic());
2895 }
2896
String(const char * string)2897 Node* WasmGraphBuilder::String(const char* string) {
2898 return jsgraph()->Constant(
2899 jsgraph()->isolate()->factory()->NewStringFromAsciiChecked(string));
2900 }
2901
graph()2902 Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); }
2903
Int64LoweringForTesting()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
SetSourcePosition(Node * node,wasm::WasmCodePosition position)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
RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,CompilationInfo * info,const char * message,uint32_t index,wasm::WasmName func_name)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
CompileJSToWasmWrapper(Isolate * isolate,wasm::ModuleEnv * module,Handle<String> name,Handle<Code> wasm_code,Handle<JSObject> module_object,uint32_t index)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
CompileWasmToJSWrapper(Isolate * isolate,Handle<JSFunction> function,wasm::FunctionSig * sig,wasm::WasmName module_name,wasm::WasmName function_name)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
BuildGraphForWasmFunction(double * decode_ms)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
WasmCompilationUnit(wasm::ErrorThrower * thrower,Isolate * isolate,wasm::ModuleEnv * module_env,const wasm::WasmFunction * function,uint32_t index)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
ExecuteCompilation()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
FinishCompilation()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