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/platform.h"
10
11 #include "src/compiler/access-builder.h"
12 #include "src/compiler/change-lowering.h"
13 #include "src/compiler/common-operator.h"
14 #include "src/compiler/diamond.h"
15 #include "src/compiler/graph.h"
16 #include "src/compiler/graph-visualizer.h"
17 #include "src/compiler/instruction-selector.h"
18 #include "src/compiler/js-generic-lowering.h"
19 #include "src/compiler/js-graph.h"
20 #include "src/compiler/js-operator.h"
21 #include "src/compiler/linkage.h"
22 #include "src/compiler/machine-operator.h"
23 #include "src/compiler/node-matchers.h"
24 #include "src/compiler/pipeline.h"
25 #include "src/compiler/simplified-lowering.h"
26 #include "src/compiler/simplified-operator.h"
27 #include "src/compiler/source-position.h"
28 #include "src/compiler/typer.h"
29
30 #include "src/code-factory.h"
31 #include "src/code-stubs.h"
32
33 #include "src/wasm/ast-decoder.h"
34 #include "src/wasm/wasm-module.h"
35 #include "src/wasm/wasm-opcodes.h"
36
37 // TODO(titzer): pull WASM_64 up to a common header.
38 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
39 #define WASM_64 1
40 #else
41 #define WASM_64 0
42 #endif
43
44 namespace v8 {
45 namespace internal {
46 namespace compiler {
47
48 namespace {
UnsupportedOpcode(wasm::WasmOpcode opcode)49 const Operator* UnsupportedOpcode(wasm::WasmOpcode opcode) {
50 if (wasm::WasmOpcodes::IsSupported(opcode)) {
51 V8_Fatal(__FILE__, __LINE__,
52 "Unsupported opcode #%d:%s reported as supported", opcode,
53 wasm::WasmOpcodes::OpcodeName(opcode));
54 }
55 V8_Fatal(__FILE__, __LINE__, "Unsupported opcode #%d:%s", opcode,
56 wasm::WasmOpcodes::OpcodeName(opcode));
57 return nullptr;
58 }
59
60
MergeControlToEnd(JSGraph * jsgraph,Node * node)61 void MergeControlToEnd(JSGraph* jsgraph, Node* node) {
62 Graph* g = jsgraph->graph();
63 if (g->end()) {
64 NodeProperties::MergeControlToEnd(g, jsgraph->common(), node);
65 } else {
66 g->SetEnd(g->NewNode(jsgraph->common()->End(1), node));
67 }
68 }
69
70
71 enum TrapReason {
72 kTrapUnreachable,
73 kTrapMemOutOfBounds,
74 kTrapDivByZero,
75 kTrapDivUnrepresentable,
76 kTrapRemByZero,
77 kTrapFloatUnrepresentable,
78 kTrapFuncInvalid,
79 kTrapFuncSigMismatch,
80 kTrapCount
81 };
82
83
84 static const char* kTrapMessages[] = {
85 "unreachable", "memory access out of bounds",
86 "divide by zero", "divide result unrepresentable",
87 "remainder by zero", "integer result unrepresentable",
88 "invalid function", "function signature mismatch"};
89 } // namespace
90
91
92 // A helper that handles building graph fragments for trapping.
93 // To avoid generating a ton of redundant code that just calls the runtime
94 // to trap, we generate a per-trap-reason block of code that all trap sites
95 // in this function will branch to.
96 class WasmTrapHelper : public ZoneObject {
97 public:
WasmTrapHelper(WasmGraphBuilder * builder)98 explicit WasmTrapHelper(WasmGraphBuilder* builder)
99 : builder_(builder),
100 jsgraph_(builder->jsgraph()),
101 graph_(builder->jsgraph() ? builder->jsgraph()->graph() : nullptr) {
102 for (int i = 0; i < kTrapCount; i++) traps_[i] = nullptr;
103 }
104
105 // Make the current control path trap to unreachable.
Unreachable()106 void Unreachable() { ConnectTrap(kTrapUnreachable); }
107
108 // Add a check that traps if {node} is equal to {val}.
TrapIfEq32(TrapReason reason,Node * node,int32_t val)109 Node* TrapIfEq32(TrapReason reason, Node* node, int32_t val) {
110 Int32Matcher m(node);
111 if (m.HasValue() && !m.Is(val)) return graph()->start();
112 if (val == 0) {
113 AddTrapIfFalse(reason, node);
114 } else {
115 AddTrapIfTrue(reason,
116 graph()->NewNode(jsgraph()->machine()->Word32Equal(), node,
117 jsgraph()->Int32Constant(val)));
118 }
119 return builder_->Control();
120 }
121
122 // Add a check that traps if {node} is zero.
ZeroCheck32(TrapReason reason,Node * node)123 Node* ZeroCheck32(TrapReason reason, Node* node) {
124 return TrapIfEq32(reason, node, 0);
125 }
126
127 // Add a check that traps if {node} is equal to {val}.
TrapIfEq64(TrapReason reason,Node * node,int64_t val)128 Node* TrapIfEq64(TrapReason reason, Node* node, int64_t val) {
129 Int64Matcher m(node);
130 if (m.HasValue() && !m.Is(val)) return graph()->start();
131 AddTrapIfTrue(reason,
132 graph()->NewNode(jsgraph()->machine()->Word64Equal(), node,
133 jsgraph()->Int64Constant(val)));
134 return builder_->Control();
135 }
136
137 // Add a check that traps if {node} is zero.
ZeroCheck64(TrapReason reason,Node * node)138 Node* ZeroCheck64(TrapReason reason, Node* node) {
139 return TrapIfEq64(reason, node, 0);
140 }
141
142 // Add a trap if {cond} is true.
AddTrapIfTrue(TrapReason reason,Node * cond)143 void AddTrapIfTrue(TrapReason reason, Node* cond) {
144 AddTrapIf(reason, cond, true);
145 }
146
147 // Add a trap if {cond} is false.
AddTrapIfFalse(TrapReason reason,Node * cond)148 void AddTrapIfFalse(TrapReason reason, Node* cond) {
149 AddTrapIf(reason, cond, false);
150 }
151
152 // Add a trap if {cond} is true or false according to {iftrue}.
AddTrapIf(TrapReason reason,Node * cond,bool iftrue)153 void AddTrapIf(TrapReason reason, Node* cond, bool iftrue) {
154 Node** effect_ptr = builder_->effect_;
155 Node** control_ptr = builder_->control_;
156 Node* before = *effect_ptr;
157 BranchHint hint = iftrue ? BranchHint::kFalse : BranchHint::kTrue;
158 Node* branch = graph()->NewNode(common()->Branch(hint), cond, *control_ptr);
159 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
160 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
161
162 *control_ptr = iftrue ? if_true : if_false;
163 ConnectTrap(reason);
164 *control_ptr = iftrue ? if_false : if_true;
165 *effect_ptr = before;
166 }
167
168 private:
169 WasmGraphBuilder* builder_;
170 JSGraph* jsgraph_;
171 Graph* graph_;
172 Node* traps_[kTrapCount];
173 Node* effects_[kTrapCount];
174
jsgraph()175 JSGraph* jsgraph() { return jsgraph_; }
graph()176 Graph* graph() { return jsgraph_->graph(); }
common()177 CommonOperatorBuilder* common() { return jsgraph()->common(); }
178
ConnectTrap(TrapReason reason)179 void ConnectTrap(TrapReason reason) {
180 if (traps_[reason] == nullptr) {
181 // Create trap code for the first time this trap is used.
182 return BuildTrapCode(reason);
183 }
184 // Connect the current control and effect to the existing trap code.
185 builder_->AppendToMerge(traps_[reason], builder_->Control());
186 builder_->AppendToPhi(traps_[reason], effects_[reason], builder_->Effect());
187 }
188
BuildTrapCode(TrapReason reason)189 void BuildTrapCode(TrapReason reason) {
190 Node* exception = builder_->String(kTrapMessages[reason]);
191 Node* end;
192 Node** control_ptr = builder_->control_;
193 Node** effect_ptr = builder_->effect_;
194 wasm::ModuleEnv* module = builder_->module_;
195 *control_ptr = traps_[reason] =
196 graph()->NewNode(common()->Merge(1), *control_ptr);
197 *effect_ptr = effects_[reason] =
198 graph()->NewNode(common()->EffectPhi(1), *effect_ptr, *control_ptr);
199
200 if (module && !module->context.is_null()) {
201 // Use the module context to call the runtime to throw an exception.
202 Runtime::FunctionId f = Runtime::kThrow;
203 const Runtime::Function* fun = Runtime::FunctionForId(f);
204 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
205 jsgraph()->zone(), f, fun->nargs, Operator::kNoProperties,
206 CallDescriptor::kNoFlags);
207 Node* inputs[] = {
208 jsgraph()->CEntryStubConstant(fun->result_size), // C entry
209 exception, // exception
210 jsgraph()->ExternalConstant(
211 ExternalReference(f, jsgraph()->isolate())), // ref
212 jsgraph()->Int32Constant(fun->nargs), // arity
213 jsgraph()->Constant(module->context), // context
214 *effect_ptr,
215 *control_ptr};
216
217 Node* node = graph()->NewNode(
218 common()->Call(desc), static_cast<int>(arraysize(inputs)), inputs);
219 *control_ptr = node;
220 *effect_ptr = node;
221 }
222 if (false) {
223 // End the control flow with a throw
224 Node* thrw =
225 graph()->NewNode(common()->Throw(), jsgraph()->ZeroConstant(),
226 *effect_ptr, *control_ptr);
227 end = thrw;
228 } else {
229 // End the control flow with returning 0xdeadbeef
230 Node* ret_value;
231 if (builder_->GetFunctionSignature()->return_count() > 0) {
232 switch (builder_->GetFunctionSignature()->GetReturn()) {
233 case wasm::kAstI32:
234 ret_value = jsgraph()->Int32Constant(0xdeadbeef);
235 break;
236 case wasm::kAstI64:
237 ret_value = jsgraph()->Int64Constant(0xdeadbeefdeadbeef);
238 break;
239 case wasm::kAstF32:
240 ret_value = jsgraph()->Float32Constant(bit_cast<float>(0xdeadbeef));
241 break;
242 case wasm::kAstF64:
243 ret_value = jsgraph()->Float64Constant(
244 bit_cast<double>(0xdeadbeefdeadbeef));
245 break;
246 default:
247 UNREACHABLE();
248 ret_value = nullptr;
249 }
250 } else {
251 ret_value = jsgraph()->Int32Constant(0xdeadbeef);
252 }
253 end = graph()->NewNode(jsgraph()->common()->Return(), ret_value,
254 *effect_ptr, *control_ptr);
255 }
256
257 MergeControlToEnd(jsgraph(), end);
258 }
259 };
260
261
WasmGraphBuilder(Zone * zone,JSGraph * jsgraph,wasm::FunctionSig * function_signature)262 WasmGraphBuilder::WasmGraphBuilder(Zone* zone, JSGraph* jsgraph,
263 wasm::FunctionSig* function_signature)
264 : zone_(zone),
265 jsgraph_(jsgraph),
266 module_(nullptr),
267 mem_buffer_(nullptr),
268 mem_size_(nullptr),
269 function_table_(nullptr),
270 control_(nullptr),
271 effect_(nullptr),
272 cur_buffer_(def_buffer_),
273 cur_bufsize_(kDefaultBufferSize),
274 trap_(new (zone) WasmTrapHelper(this)),
275 function_signature_(function_signature) {
276 DCHECK_NOT_NULL(jsgraph_);
277 }
278
279
Error()280 Node* WasmGraphBuilder::Error() { return jsgraph()->Dead(); }
281
282
Start(unsigned params)283 Node* WasmGraphBuilder::Start(unsigned params) {
284 Node* start = graph()->NewNode(jsgraph()->common()->Start(params));
285 graph()->SetStart(start);
286 return start;
287 }
288
289
Param(unsigned index,wasm::LocalType type)290 Node* WasmGraphBuilder::Param(unsigned index, wasm::LocalType type) {
291 return graph()->NewNode(jsgraph()->common()->Parameter(index),
292 graph()->start());
293 }
294
295
Loop(Node * entry)296 Node* WasmGraphBuilder::Loop(Node* entry) {
297 return graph()->NewNode(jsgraph()->common()->Loop(1), entry);
298 }
299
300
Terminate(Node * effect,Node * control)301 Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) {
302 Node* terminate =
303 graph()->NewNode(jsgraph()->common()->Terminate(), effect, control);
304 MergeControlToEnd(jsgraph(), terminate);
305 return terminate;
306 }
307
308
InputCount(Node * node)309 unsigned WasmGraphBuilder::InputCount(Node* node) {
310 return static_cast<unsigned>(node->InputCount());
311 }
312
313
IsPhiWithMerge(Node * phi,Node * merge)314 bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
315 return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
316 NodeProperties::GetControlInput(phi) == merge;
317 }
318
319
AppendToMerge(Node * merge,Node * from)320 void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) {
321 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
322 merge->AppendInput(jsgraph()->zone(), from);
323 int new_size = merge->InputCount();
324 NodeProperties::ChangeOp(
325 merge, jsgraph()->common()->ResizeMergeOrPhi(merge->op(), new_size));
326 }
327
328
AppendToPhi(Node * merge,Node * phi,Node * from)329 void WasmGraphBuilder::AppendToPhi(Node* merge, Node* phi, Node* from) {
330 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
331 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
332 int new_size = phi->InputCount();
333 phi->InsertInput(jsgraph()->zone(), phi->InputCount() - 1, from);
334 NodeProperties::ChangeOp(
335 phi, jsgraph()->common()->ResizeMergeOrPhi(phi->op(), new_size));
336 }
337
338
Merge(unsigned count,Node ** controls)339 Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) {
340 return graph()->NewNode(jsgraph()->common()->Merge(count), count, controls);
341 }
342
343
Phi(wasm::LocalType type,unsigned count,Node ** vals,Node * control)344 Node* WasmGraphBuilder::Phi(wasm::LocalType type, unsigned count, Node** vals,
345 Node* control) {
346 DCHECK(IrOpcode::IsMergeOpcode(control->opcode()));
347 Node** buf = Realloc(vals, count);
348 buf = Realloc(buf, count + 1);
349 buf[count] = control;
350 return graph()->NewNode(jsgraph()->common()->Phi(type, count), count + 1,
351 buf);
352 }
353
354
EffectPhi(unsigned count,Node ** effects,Node * control)355 Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects,
356 Node* control) {
357 DCHECK(IrOpcode::IsMergeOpcode(control->opcode()));
358 Node** buf = Realloc(effects, count);
359 buf = Realloc(buf, count + 1);
360 buf[count] = control;
361 return graph()->NewNode(jsgraph()->common()->EffectPhi(count), count + 1,
362 buf);
363 }
364
365
Int32Constant(int32_t value)366 Node* WasmGraphBuilder::Int32Constant(int32_t value) {
367 return jsgraph()->Int32Constant(value);
368 }
369
370
Int64Constant(int64_t value)371 Node* WasmGraphBuilder::Int64Constant(int64_t value) {
372 return jsgraph()->Int64Constant(value);
373 }
374
375
Binop(wasm::WasmOpcode opcode,Node * left,Node * right)376 Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
377 Node* right) {
378 const Operator* op;
379 MachineOperatorBuilder* m = jsgraph()->machine();
380 switch (opcode) {
381 case wasm::kExprI32Add:
382 op = m->Int32Add();
383 break;
384 case wasm::kExprI32Sub:
385 op = m->Int32Sub();
386 break;
387 case wasm::kExprI32Mul:
388 op = m->Int32Mul();
389 break;
390 case wasm::kExprI32DivS: {
391 trap_->ZeroCheck32(kTrapDivByZero, right);
392 Node* before = *control_;
393 Node* denom_is_m1;
394 Node* denom_is_not_m1;
395 Branch(graph()->NewNode(jsgraph()->machine()->Word32Equal(), right,
396 jsgraph()->Int32Constant(-1)),
397 &denom_is_m1, &denom_is_not_m1);
398 *control_ = denom_is_m1;
399 trap_->TrapIfEq32(kTrapDivUnrepresentable, left, kMinInt);
400 if (*control_ != denom_is_m1) {
401 *control_ = graph()->NewNode(jsgraph()->common()->Merge(2),
402 denom_is_not_m1, *control_);
403 } else {
404 *control_ = before;
405 }
406 return graph()->NewNode(m->Int32Div(), left, right, *control_);
407 }
408 case wasm::kExprI32DivU:
409 op = m->Uint32Div();
410 return graph()->NewNode(op, left, right,
411 trap_->ZeroCheck32(kTrapDivByZero, right));
412 case wasm::kExprI32RemS: {
413 trap_->ZeroCheck32(kTrapRemByZero, right);
414 Diamond d(graph(), jsgraph()->common(),
415 graph()->NewNode(jsgraph()->machine()->Word32Equal(), right,
416 jsgraph()->Int32Constant(-1)));
417
418 Node* rem = graph()->NewNode(m->Int32Mod(), left, right, d.if_false);
419
420 return d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
421 rem);
422 }
423 case wasm::kExprI32RemU:
424 op = m->Uint32Mod();
425 return graph()->NewNode(op, left, right,
426 trap_->ZeroCheck32(kTrapRemByZero, right));
427 case wasm::kExprI32And:
428 op = m->Word32And();
429 break;
430 case wasm::kExprI32Ior:
431 op = m->Word32Or();
432 break;
433 case wasm::kExprI32Xor:
434 op = m->Word32Xor();
435 break;
436 case wasm::kExprI32Shl:
437 op = m->Word32Shl();
438 break;
439 case wasm::kExprI32ShrU:
440 op = m->Word32Shr();
441 break;
442 case wasm::kExprI32ShrS:
443 op = m->Word32Sar();
444 break;
445 case wasm::kExprI32Eq:
446 op = m->Word32Equal();
447 break;
448 case wasm::kExprI32Ne:
449 return Invert(Binop(wasm::kExprI32Eq, left, right));
450 case wasm::kExprI32LtS:
451 op = m->Int32LessThan();
452 break;
453 case wasm::kExprI32LeS:
454 op = m->Int32LessThanOrEqual();
455 break;
456 case wasm::kExprI32LtU:
457 op = m->Uint32LessThan();
458 break;
459 case wasm::kExprI32LeU:
460 op = m->Uint32LessThanOrEqual();
461 break;
462 case wasm::kExprI32GtS:
463 op = m->Int32LessThan();
464 std::swap(left, right);
465 break;
466 case wasm::kExprI32GeS:
467 op = m->Int32LessThanOrEqual();
468 std::swap(left, right);
469 break;
470 case wasm::kExprI32GtU:
471 op = m->Uint32LessThan();
472 std::swap(left, right);
473 break;
474 case wasm::kExprI32GeU:
475 op = m->Uint32LessThanOrEqual();
476 std::swap(left, right);
477 break;
478 #if WASM_64
479 // Opcodes only supported on 64-bit platforms.
480 // TODO(titzer): query the machine operator builder here instead of #ifdef.
481 case wasm::kExprI64Add:
482 op = m->Int64Add();
483 break;
484 case wasm::kExprI64Sub:
485 op = m->Int64Sub();
486 break;
487 case wasm::kExprI64Mul:
488 op = m->Int64Mul();
489 break;
490 case wasm::kExprI64DivS: {
491 trap_->ZeroCheck64(kTrapDivByZero, right);
492 Node* before = *control_;
493 Node* denom_is_m1;
494 Node* denom_is_not_m1;
495 Branch(graph()->NewNode(jsgraph()->machine()->Word64Equal(), right,
496 jsgraph()->Int64Constant(-1)),
497 &denom_is_m1, &denom_is_not_m1);
498 *control_ = denom_is_m1;
499 trap_->TrapIfEq64(kTrapDivUnrepresentable, left,
500 std::numeric_limits<int64_t>::min());
501 if (*control_ != denom_is_m1) {
502 *control_ = graph()->NewNode(jsgraph()->common()->Merge(2),
503 denom_is_not_m1, *control_);
504 } else {
505 *control_ = before;
506 }
507 return graph()->NewNode(m->Int64Div(), left, right, *control_);
508 }
509 case wasm::kExprI64DivU:
510 op = m->Uint64Div();
511 return graph()->NewNode(op, left, right,
512 trap_->ZeroCheck64(kTrapDivByZero, right));
513 case wasm::kExprI64RemS: {
514 trap_->ZeroCheck64(kTrapRemByZero, right);
515 Diamond d(jsgraph()->graph(), jsgraph()->common(),
516 graph()->NewNode(jsgraph()->machine()->Word64Equal(), right,
517 jsgraph()->Int64Constant(-1)));
518
519 Node* rem = graph()->NewNode(m->Int64Mod(), left, right, d.if_false);
520
521 return d.Phi(MachineRepresentation::kWord64, jsgraph()->Int64Constant(0),
522 rem);
523 }
524 case wasm::kExprI64RemU:
525 op = m->Uint64Mod();
526 return graph()->NewNode(op, left, right,
527 trap_->ZeroCheck64(kTrapRemByZero, right));
528 case wasm::kExprI64And:
529 op = m->Word64And();
530 break;
531 case wasm::kExprI64Ior:
532 op = m->Word64Or();
533 break;
534 case wasm::kExprI64Xor:
535 op = m->Word64Xor();
536 break;
537 case wasm::kExprI64Shl:
538 op = m->Word64Shl();
539 break;
540 case wasm::kExprI64ShrU:
541 op = m->Word64Shr();
542 break;
543 case wasm::kExprI64ShrS:
544 op = m->Word64Sar();
545 break;
546 case wasm::kExprI64Eq:
547 op = m->Word64Equal();
548 break;
549 case wasm::kExprI64Ne:
550 return Invert(Binop(wasm::kExprI64Eq, left, right));
551 case wasm::kExprI64LtS:
552 op = m->Int64LessThan();
553 break;
554 case wasm::kExprI64LeS:
555 op = m->Int64LessThanOrEqual();
556 break;
557 case wasm::kExprI64LtU:
558 op = m->Uint64LessThan();
559 break;
560 case wasm::kExprI64LeU:
561 op = m->Uint64LessThanOrEqual();
562 break;
563 case wasm::kExprI64GtS:
564 op = m->Int64LessThan();
565 std::swap(left, right);
566 break;
567 case wasm::kExprI64GeS:
568 op = m->Int64LessThanOrEqual();
569 std::swap(left, right);
570 break;
571 case wasm::kExprI64GtU:
572 op = m->Uint64LessThan();
573 std::swap(left, right);
574 break;
575 case wasm::kExprI64GeU:
576 op = m->Uint64LessThanOrEqual();
577 std::swap(left, right);
578 break;
579 #endif
580
581 case wasm::kExprF32CopySign:
582 return BuildF32CopySign(left, right);
583 case wasm::kExprF64CopySign:
584 return BuildF64CopySign(left, right);
585 case wasm::kExprF32Add:
586 op = m->Float32Add();
587 break;
588 case wasm::kExprF32Sub:
589 op = m->Float32Sub();
590 break;
591 case wasm::kExprF32Mul:
592 op = m->Float32Mul();
593 break;
594 case wasm::kExprF32Div:
595 op = m->Float32Div();
596 break;
597 case wasm::kExprF32Eq:
598 op = m->Float32Equal();
599 break;
600 case wasm::kExprF32Ne:
601 return Invert(Binop(wasm::kExprF32Eq, left, right));
602 case wasm::kExprF32Lt:
603 op = m->Float32LessThan();
604 break;
605 case wasm::kExprF32Ge:
606 op = m->Float32LessThanOrEqual();
607 std::swap(left, right);
608 break;
609 case wasm::kExprF32Gt:
610 op = m->Float32LessThan();
611 std::swap(left, right);
612 break;
613 case wasm::kExprF32Le:
614 op = m->Float32LessThanOrEqual();
615 break;
616 case wasm::kExprF64Add:
617 op = m->Float64Add();
618 break;
619 case wasm::kExprF64Sub:
620 op = m->Float64Sub();
621 break;
622 case wasm::kExprF64Mul:
623 op = m->Float64Mul();
624 break;
625 case wasm::kExprF64Div:
626 op = m->Float64Div();
627 break;
628 case wasm::kExprF64Eq:
629 op = m->Float64Equal();
630 break;
631 case wasm::kExprF64Ne:
632 return Invert(Binop(wasm::kExprF64Eq, left, right));
633 case wasm::kExprF64Lt:
634 op = m->Float64LessThan();
635 break;
636 case wasm::kExprF64Le:
637 op = m->Float64LessThanOrEqual();
638 break;
639 case wasm::kExprF64Gt:
640 op = m->Float64LessThan();
641 std::swap(left, right);
642 break;
643 case wasm::kExprF64Ge:
644 op = m->Float64LessThanOrEqual();
645 std::swap(left, right);
646 break;
647 case wasm::kExprF32Min:
648 return BuildF32Min(left, right);
649 case wasm::kExprF64Min:
650 return BuildF64Min(left, right);
651 case wasm::kExprF32Max:
652 return BuildF32Max(left, right);
653 case wasm::kExprF64Max:
654 return BuildF64Max(left, right);
655 default:
656 op = UnsupportedOpcode(opcode);
657 }
658 return graph()->NewNode(op, left, right);
659 }
660
661
Unop(wasm::WasmOpcode opcode,Node * input)662 Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
663 const Operator* op;
664 MachineOperatorBuilder* m = jsgraph()->machine();
665 switch (opcode) {
666 case wasm::kExprBoolNot:
667 op = m->Word32Equal();
668 return graph()->NewNode(op, input, jsgraph()->Int32Constant(0));
669 case wasm::kExprF32Abs:
670 op = m->Float32Abs();
671 break;
672 case wasm::kExprF32Neg:
673 return BuildF32Neg(input);
674 case wasm::kExprF32Sqrt:
675 op = m->Float32Sqrt();
676 break;
677 case wasm::kExprF64Abs:
678 op = m->Float64Abs();
679 break;
680 case wasm::kExprF64Neg:
681 return BuildF64Neg(input);
682 case wasm::kExprF64Sqrt:
683 op = m->Float64Sqrt();
684 break;
685 case wasm::kExprI32SConvertF64:
686 return BuildI32SConvertF64(input);
687 case wasm::kExprI32UConvertF64:
688 return BuildI32UConvertF64(input);
689 case wasm::kExprF32ConvertF64:
690 op = m->TruncateFloat64ToFloat32();
691 break;
692 case wasm::kExprF64SConvertI32:
693 op = m->ChangeInt32ToFloat64();
694 break;
695 case wasm::kExprF64UConvertI32:
696 op = m->ChangeUint32ToFloat64();
697 break;
698 case wasm::kExprF32SConvertI32:
699 op = m->ChangeInt32ToFloat64(); // TODO(titzer): two conversions
700 input = graph()->NewNode(op, input);
701 op = m->TruncateFloat64ToFloat32();
702 break;
703 case wasm::kExprF32UConvertI32:
704 op = m->ChangeUint32ToFloat64();
705 input = graph()->NewNode(op, input);
706 op = m->TruncateFloat64ToFloat32();
707 break;
708 case wasm::kExprI32SConvertF32:
709 return BuildI32SConvertF32(input);
710 case wasm::kExprI32UConvertF32:
711 return BuildI32UConvertF32(input);
712 case wasm::kExprF64ConvertF32:
713 op = m->ChangeFloat32ToFloat64();
714 break;
715 case wasm::kExprF32ReinterpretI32:
716 op = m->BitcastInt32ToFloat32();
717 break;
718 case wasm::kExprI32ReinterpretF32:
719 op = m->BitcastFloat32ToInt32();
720 break;
721 case wasm::kExprI32Clz:
722 op = m->Word32Clz();
723 break;
724 case wasm::kExprI32Ctz: {
725 if (m->Word32Ctz().IsSupported()) {
726 op = m->Word32Ctz().op();
727 break;
728 } else {
729 return BuildI32Ctz(input);
730 }
731 }
732 case wasm::kExprI32Popcnt: {
733 if (m->Word32Popcnt().IsSupported()) {
734 op = m->Word32Popcnt().op();
735 break;
736 } else {
737 return BuildI32Popcnt(input);
738 }
739 }
740 case wasm::kExprF32Floor: {
741 if (m->Float32RoundDown().IsSupported()) {
742 op = m->Float32RoundDown().op();
743 break;
744 } else {
745 op = UnsupportedOpcode(opcode);
746 break;
747 }
748 }
749 case wasm::kExprF32Ceil: {
750 if (m->Float32RoundUp().IsSupported()) {
751 op = m->Float32RoundUp().op();
752 break;
753 } else {
754 op = UnsupportedOpcode(opcode);
755 break;
756 }
757 }
758 case wasm::kExprF32Trunc: {
759 if (m->Float32RoundTruncate().IsSupported()) {
760 op = m->Float32RoundTruncate().op();
761 break;
762 } else {
763 op = UnsupportedOpcode(opcode);
764 break;
765 }
766 }
767 case wasm::kExprF32NearestInt: {
768 if (m->Float32RoundTiesEven().IsSupported()) {
769 op = m->Float32RoundTiesEven().op();
770 break;
771 } else {
772 op = UnsupportedOpcode(opcode);
773 break;
774 }
775 }
776 case wasm::kExprF64Floor: {
777 if (m->Float64RoundDown().IsSupported()) {
778 op = m->Float64RoundDown().op();
779 break;
780 } else {
781 op = UnsupportedOpcode(opcode);
782 break;
783 }
784 }
785 case wasm::kExprF64Ceil: {
786 if (m->Float64RoundUp().IsSupported()) {
787 op = m->Float64RoundUp().op();
788 break;
789 } else {
790 op = UnsupportedOpcode(opcode);
791 break;
792 }
793 }
794 case wasm::kExprF64Trunc: {
795 if (m->Float64RoundTruncate().IsSupported()) {
796 op = m->Float64RoundTruncate().op();
797 break;
798 } else {
799 op = UnsupportedOpcode(opcode);
800 break;
801 }
802 }
803 case wasm::kExprF64NearestInt: {
804 if (m->Float64RoundTiesEven().IsSupported()) {
805 op = m->Float64RoundTiesEven().op();
806 break;
807 } else {
808 op = UnsupportedOpcode(opcode);
809 break;
810 }
811 }
812
813 #if WASM_64
814 // Opcodes only supported on 64-bit platforms.
815 // TODO(titzer): query the machine operator builder here instead of #ifdef.
816 case wasm::kExprI32ConvertI64:
817 op = m->TruncateInt64ToInt32();
818 break;
819 case wasm::kExprI64SConvertI32:
820 op = m->ChangeInt32ToInt64();
821 break;
822 case wasm::kExprI64UConvertI32:
823 op = m->ChangeUint32ToUint64();
824 break;
825 case wasm::kExprF32SConvertI64:
826 op = m->RoundInt64ToFloat32();
827 break;
828 case wasm::kExprF32UConvertI64:
829 op = m->RoundUint64ToFloat32();
830 break;
831 case wasm::kExprF64SConvertI64:
832 op = m->RoundInt64ToFloat64();
833 break;
834 case wasm::kExprF64UConvertI64:
835 op = m->RoundUint64ToFloat64();
836 break;
837 case wasm::kExprI64SConvertF32: {
838 Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToInt64(), input);
839 Node* result =
840 graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
841 Node* overflow =
842 graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
843 trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
844 return result;
845 }
846 case wasm::kExprI64SConvertF64: {
847 Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToInt64(), input);
848 Node* result =
849 graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
850 Node* overflow =
851 graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
852 trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
853 return result;
854 }
855 case wasm::kExprI64UConvertF32: {
856 Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToUint64(), input);
857 Node* result =
858 graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
859 Node* overflow =
860 graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
861 trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
862 return result;
863 }
864 case wasm::kExprI64UConvertF64: {
865 Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToUint64(), input);
866 Node* result =
867 graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
868 Node* overflow =
869 graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
870 trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
871 return result;
872 }
873 case wasm::kExprF64ReinterpretI64:
874 op = m->BitcastInt64ToFloat64();
875 break;
876 case wasm::kExprI64ReinterpretF64:
877 op = m->BitcastFloat64ToInt64();
878 break;
879 case wasm::kExprI64Clz:
880 op = m->Word64Clz();
881 break;
882 case wasm::kExprI64Ctz: {
883 if (m->Word64Ctz().IsSupported()) {
884 op = m->Word64Ctz().op();
885 break;
886 } else {
887 return BuildI64Ctz(input);
888 }
889 }
890 case wasm::kExprI64Popcnt: {
891 if (m->Word64Popcnt().IsSupported()) {
892 op = m->Word64Popcnt().op();
893 break;
894 } else {
895 return BuildI64Popcnt(input);
896 }
897 }
898 #endif
899 default:
900 op = UnsupportedOpcode(opcode);
901 }
902 return graph()->NewNode(op, input);
903 }
904
905
Float32Constant(float value)906 Node* WasmGraphBuilder::Float32Constant(float value) {
907 return jsgraph()->Float32Constant(value);
908 }
909
910
Float64Constant(double value)911 Node* WasmGraphBuilder::Float64Constant(double value) {
912 return jsgraph()->Float64Constant(value);
913 }
914
915
Constant(Handle<Object> value)916 Node* WasmGraphBuilder::Constant(Handle<Object> value) {
917 return jsgraph()->Constant(value);
918 }
919
920
Branch(Node * cond,Node ** true_node,Node ** false_node)921 Node* WasmGraphBuilder::Branch(Node* cond, Node** true_node,
922 Node** false_node) {
923 DCHECK_NOT_NULL(cond);
924 DCHECK_NOT_NULL(*control_);
925 Node* branch =
926 graph()->NewNode(jsgraph()->common()->Branch(), cond, *control_);
927 *true_node = graph()->NewNode(jsgraph()->common()->IfTrue(), branch);
928 *false_node = graph()->NewNode(jsgraph()->common()->IfFalse(), branch);
929 return branch;
930 }
931
932
Switch(unsigned count,Node * key)933 Node* WasmGraphBuilder::Switch(unsigned count, Node* key) {
934 return graph()->NewNode(jsgraph()->common()->Switch(count), key, *control_);
935 }
936
937
IfValue(int32_t value,Node * sw)938 Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) {
939 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
940 return graph()->NewNode(jsgraph()->common()->IfValue(value), sw);
941 }
942
943
IfDefault(Node * sw)944 Node* WasmGraphBuilder::IfDefault(Node* sw) {
945 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
946 return graph()->NewNode(jsgraph()->common()->IfDefault(), sw);
947 }
948
949
Return(unsigned count,Node ** vals)950 Node* WasmGraphBuilder::Return(unsigned count, Node** vals) {
951 DCHECK_NOT_NULL(*control_);
952 DCHECK_NOT_NULL(*effect_);
953
954 if (count == 0) {
955 // Handle a return of void.
956 vals[0] = jsgraph()->Int32Constant(0);
957 count = 1;
958 }
959
960 Node** buf = Realloc(vals, count);
961 buf = Realloc(buf, count + 2);
962 buf[count] = *effect_;
963 buf[count + 1] = *control_;
964 Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals);
965
966 MergeControlToEnd(jsgraph(), ret);
967 return ret;
968 }
969
970
ReturnVoid()971 Node* WasmGraphBuilder::ReturnVoid() { return Return(0, Buffer(0)); }
972
973
Unreachable()974 Node* WasmGraphBuilder::Unreachable() {
975 trap_->Unreachable();
976 return nullptr;
977 }
978
979
BuildF32Neg(Node * input)980 Node* WasmGraphBuilder::BuildF32Neg(Node* input) {
981 Node* result =
982 Unop(wasm::kExprF32ReinterpretI32,
983 Binop(wasm::kExprI32Xor, Unop(wasm::kExprI32ReinterpretF32, input),
984 jsgraph()->Int32Constant(0x80000000)));
985
986 return result;
987 }
988
989
BuildF64Neg(Node * input)990 Node* WasmGraphBuilder::BuildF64Neg(Node* input) {
991 #if WASM_64
992 Node* result =
993 Unop(wasm::kExprF64ReinterpretI64,
994 Binop(wasm::kExprI64Xor, Unop(wasm::kExprI64ReinterpretF64, input),
995 jsgraph()->Int64Constant(0x8000000000000000)));
996
997 return result;
998 #else
999 MachineOperatorBuilder* m = jsgraph()->machine();
1000
1001 Node* old_high_word = graph()->NewNode(m->Float64ExtractHighWord32(), input);
1002 Node* new_high_word = Binop(wasm::kExprI32Xor, old_high_word,
1003 jsgraph()->Int32Constant(0x80000000));
1004
1005 return graph()->NewNode(m->Float64InsertHighWord32(), input, new_high_word);
1006 #endif
1007 }
1008
1009
BuildF32CopySign(Node * left,Node * right)1010 Node* WasmGraphBuilder::BuildF32CopySign(Node* left, Node* right) {
1011 Node* result = Unop(
1012 wasm::kExprF32ReinterpretI32,
1013 Binop(wasm::kExprI32Ior,
1014 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, left),
1015 jsgraph()->Int32Constant(0x7fffffff)),
1016 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, right),
1017 jsgraph()->Int32Constant(0x80000000))));
1018
1019 return result;
1020 }
1021
1022
BuildF64CopySign(Node * left,Node * right)1023 Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) {
1024 #if WASM_64
1025 Node* result = Unop(
1026 wasm::kExprF64ReinterpretI64,
1027 Binop(wasm::kExprI64Ior,
1028 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, left),
1029 jsgraph()->Int64Constant(0x7fffffffffffffff)),
1030 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, right),
1031 jsgraph()->Int64Constant(0x8000000000000000))));
1032
1033 return result;
1034 #else
1035 MachineOperatorBuilder* m = jsgraph()->machine();
1036
1037 Node* high_word_left = graph()->NewNode(m->Float64ExtractHighWord32(), left);
1038 Node* high_word_right =
1039 graph()->NewNode(m->Float64ExtractHighWord32(), right);
1040
1041 Node* new_high_word =
1042 Binop(wasm::kExprI32Ior, Binop(wasm::kExprI32And, high_word_left,
1043 jsgraph()->Int32Constant(0x7fffffff)),
1044 Binop(wasm::kExprI32And, high_word_right,
1045 jsgraph()->Int32Constant(0x80000000)));
1046
1047 return graph()->NewNode(m->Float64InsertHighWord32(), left, new_high_word);
1048 #endif
1049 }
1050
1051
BuildF32Min(Node * left,Node * right)1052 Node* WasmGraphBuilder::BuildF32Min(Node* left, Node* right) {
1053 Diamond left_le_right(graph(), jsgraph()->common(),
1054 Binop(wasm::kExprF32Le, left, right));
1055
1056 Diamond right_lt_left(graph(), jsgraph()->common(),
1057 Binop(wasm::kExprF32Lt, right, left));
1058
1059 Diamond left_is_not_nan(graph(), jsgraph()->common(),
1060 Binop(wasm::kExprF32Eq, left, left));
1061
1062 return left_le_right.Phi(
1063 wasm::kAstF32, left,
1064 right_lt_left.Phi(wasm::kAstF32, right,
1065 left_is_not_nan.Phi(wasm::kAstF32, right, left)));
1066 }
1067
1068
BuildF32Max(Node * left,Node * right)1069 Node* WasmGraphBuilder::BuildF32Max(Node* left, Node* right) {
1070 Diamond left_ge_right(graph(), jsgraph()->common(),
1071 Binop(wasm::kExprF32Ge, left, right));
1072
1073 Diamond right_gt_left(graph(), jsgraph()->common(),
1074 Binop(wasm::kExprF32Gt, right, left));
1075
1076 Diamond left_is_not_nan(graph(), jsgraph()->common(),
1077 Binop(wasm::kExprF32Eq, left, left));
1078
1079 return left_ge_right.Phi(
1080 wasm::kAstF32, left,
1081 right_gt_left.Phi(wasm::kAstF32, right,
1082 left_is_not_nan.Phi(wasm::kAstF32, right, left)));
1083 }
1084
1085
BuildF64Min(Node * left,Node * right)1086 Node* WasmGraphBuilder::BuildF64Min(Node* left, Node* right) {
1087 Diamond left_le_right(graph(), jsgraph()->common(),
1088 Binop(wasm::kExprF64Le, left, right));
1089
1090 Diamond right_lt_left(graph(), jsgraph()->common(),
1091 Binop(wasm::kExprF64Lt, right, left));
1092
1093 Diamond left_is_not_nan(graph(), jsgraph()->common(),
1094 Binop(wasm::kExprF64Eq, left, left));
1095
1096 return left_le_right.Phi(
1097 wasm::kAstF64, left,
1098 right_lt_left.Phi(wasm::kAstF64, right,
1099 left_is_not_nan.Phi(wasm::kAstF64, right, left)));
1100 }
1101
1102
BuildF64Max(Node * left,Node * right)1103 Node* WasmGraphBuilder::BuildF64Max(Node* left, Node* right) {
1104 Diamond left_ge_right(graph(), jsgraph()->common(),
1105 Binop(wasm::kExprF64Ge, left, right));
1106
1107 Diamond right_gt_left(graph(), jsgraph()->common(),
1108 Binop(wasm::kExprF64Lt, right, left));
1109
1110 Diamond left_is_not_nan(graph(), jsgraph()->common(),
1111 Binop(wasm::kExprF64Eq, left, left));
1112
1113 return left_ge_right.Phi(
1114 wasm::kAstF64, left,
1115 right_gt_left.Phi(wasm::kAstF64, right,
1116 left_is_not_nan.Phi(wasm::kAstF64, right, left)));
1117 }
1118
1119
BuildI32SConvertF32(Node * input)1120 Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input) {
1121 MachineOperatorBuilder* m = jsgraph()->machine();
1122 // Truncation of the input value is needed for the overflow check later.
1123 Node* trunc = Unop(wasm::kExprF32Trunc, input);
1124 // TODO(titzer): two conversions
1125 Node* f64_trunc = graph()->NewNode(m->ChangeFloat32ToFloat64(), trunc);
1126 Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), f64_trunc);
1127
1128 // Convert the result back to f64. If we end up at a different value than the
1129 // truncated input value, then there has been an overflow and we trap.
1130 Node* check = Unop(wasm::kExprF64SConvertI32, result);
1131 Node* overflow = Binop(wasm::kExprF64Ne, f64_trunc, check);
1132 trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
1133
1134 return result;
1135 }
1136
1137
BuildI32SConvertF64(Node * input)1138 Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input) {
1139 MachineOperatorBuilder* m = jsgraph()->machine();
1140 // Truncation of the input value is needed for the overflow check later.
1141 Node* trunc = Unop(wasm::kExprF64Trunc, input);
1142 Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), trunc);
1143
1144 // Convert the result back to f64. If we end up at a different value than the
1145 // truncated input value, then there has been an overflow and we trap.
1146 Node* check = Unop(wasm::kExprF64SConvertI32, result);
1147 Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
1148 trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
1149
1150 return result;
1151 }
1152
1153
BuildI32UConvertF32(Node * input)1154 Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input) {
1155 MachineOperatorBuilder* m = jsgraph()->machine();
1156 // Truncation of the input value is needed for the overflow check later.
1157 Node* trunc = Unop(wasm::kExprF32Trunc, input);
1158 // TODO(titzer): two conversions
1159 Node* f64_trunc = graph()->NewNode(m->ChangeFloat32ToFloat64(), trunc);
1160 Node* result = graph()->NewNode(m->ChangeFloat64ToUint32(), f64_trunc);
1161
1162 // Convert the result back to f64. If we end up at a different value than the
1163 // truncated input value, then there has been an overflow and we trap.
1164 Node* check = Unop(wasm::kExprF64UConvertI32, result);
1165 Node* overflow = Binop(wasm::kExprF64Ne, f64_trunc, check);
1166 trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
1167
1168 return result;
1169 }
1170
1171
BuildI32UConvertF64(Node * input)1172 Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input) {
1173 MachineOperatorBuilder* m = jsgraph()->machine();
1174 // Truncation of the input value is needed for the overflow check later.
1175 Node* trunc = Unop(wasm::kExprF64Trunc, input);
1176 Node* result = graph()->NewNode(m->ChangeFloat64ToUint32(), trunc);
1177
1178 // Convert the result back to f64. If we end up at a different value than the
1179 // truncated input value, then there has been an overflow and we trap.
1180 Node* check = Unop(wasm::kExprF64UConvertI32, result);
1181 Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
1182 trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
1183
1184 return result;
1185 }
1186
1187
BuildI32Ctz(Node * input)1188 Node* WasmGraphBuilder::BuildI32Ctz(Node* input) {
1189 //// Implement the following code as TF graph.
1190 // value = value | (value << 1);
1191 // value = value | (value << 2);
1192 // value = value | (value << 4);
1193 // value = value | (value << 8);
1194 // value = value | (value << 16);
1195 // return CountPopulation32(0xffffffff XOR value);
1196
1197 Node* result =
1198 Binop(wasm::kExprI32Ior, input,
1199 Binop(wasm::kExprI32Shl, input, jsgraph()->Int32Constant(1)));
1200
1201 result = Binop(wasm::kExprI32Ior, result,
1202 Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(2)));
1203
1204 result = Binop(wasm::kExprI32Ior, result,
1205 Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(4)));
1206
1207 result = Binop(wasm::kExprI32Ior, result,
1208 Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(8)));
1209
1210 result =
1211 Binop(wasm::kExprI32Ior, result,
1212 Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(16)));
1213
1214 result = BuildI32Popcnt(
1215 Binop(wasm::kExprI32Xor, jsgraph()->Int32Constant(0xffffffff), result));
1216
1217 return result;
1218 }
1219
1220
BuildI64Ctz(Node * input)1221 Node* WasmGraphBuilder::BuildI64Ctz(Node* input) {
1222 //// Implement the following code as TF graph.
1223 // value = value | (value << 1);
1224 // value = value | (value << 2);
1225 // value = value | (value << 4);
1226 // value = value | (value << 8);
1227 // value = value | (value << 16);
1228 // value = value | (value << 32);
1229 // return CountPopulation64(0xffffffffffffffff XOR value);
1230
1231 Node* result =
1232 Binop(wasm::kExprI64Ior, input,
1233 Binop(wasm::kExprI64Shl, input, jsgraph()->Int64Constant(1)));
1234
1235 result = Binop(wasm::kExprI64Ior, result,
1236 Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(2)));
1237
1238 result = Binop(wasm::kExprI64Ior, result,
1239 Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(4)));
1240
1241 result = Binop(wasm::kExprI64Ior, result,
1242 Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(8)));
1243
1244 result =
1245 Binop(wasm::kExprI64Ior, result,
1246 Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(16)));
1247
1248 result =
1249 Binop(wasm::kExprI64Ior, result,
1250 Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(32)));
1251
1252 result = BuildI64Popcnt(Binop(
1253 wasm::kExprI64Xor, jsgraph()->Int64Constant(0xffffffffffffffff), result));
1254
1255 return result;
1256 }
1257
1258
BuildI32Popcnt(Node * input)1259 Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) {
1260 //// Implement the following code as a TF graph.
1261 // value = ((value >> 1) & 0x55555555) + (value & 0x55555555);
1262 // value = ((value >> 2) & 0x33333333) + (value & 0x33333333);
1263 // value = ((value >> 4) & 0x0f0f0f0f) + (value & 0x0f0f0f0f);
1264 // value = ((value >> 8) & 0x00ff00ff) + (value & 0x00ff00ff);
1265 // value = ((value >> 16) & 0x0000ffff) + (value & 0x0000ffff);
1266
1267 Node* result = Binop(
1268 wasm::kExprI32Add,
1269 Binop(wasm::kExprI32And,
1270 Binop(wasm::kExprI32ShrU, input, jsgraph()->Int32Constant(1)),
1271 jsgraph()->Int32Constant(0x55555555)),
1272 Binop(wasm::kExprI32And, input, jsgraph()->Int32Constant(0x55555555)));
1273
1274 result = Binop(
1275 wasm::kExprI32Add,
1276 Binop(wasm::kExprI32And,
1277 Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(2)),
1278 jsgraph()->Int32Constant(0x33333333)),
1279 Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x33333333)));
1280
1281 result = Binop(
1282 wasm::kExprI32Add,
1283 Binop(wasm::kExprI32And,
1284 Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(4)),
1285 jsgraph()->Int32Constant(0x0f0f0f0f)),
1286 Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x0f0f0f0f)));
1287
1288 result = Binop(
1289 wasm::kExprI32Add,
1290 Binop(wasm::kExprI32And,
1291 Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(8)),
1292 jsgraph()->Int32Constant(0x00ff00ff)),
1293 Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x00ff00ff)));
1294
1295 result = Binop(
1296 wasm::kExprI32Add,
1297 Binop(wasm::kExprI32And,
1298 Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(16)),
1299 jsgraph()->Int32Constant(0x0000ffff)),
1300 Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x0000ffff)));
1301
1302 return result;
1303 }
1304
1305
BuildI64Popcnt(Node * input)1306 Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) {
1307 //// Implement the following code as a TF graph.
1308 // value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555);
1309 // value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333);
1310 // value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f);
1311 // value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff);
1312 // value = ((value >> 16) & 0x0000ffff0000ffff) + (value &
1313 // 0x0000ffff0000ffff);
1314 // value = ((value >> 32) & 0x00000000ffffffff) + (value &
1315 // 0x00000000ffffffff);
1316
1317 Node* result =
1318 Binop(wasm::kExprI64Add,
1319 Binop(wasm::kExprI64And,
1320 Binop(wasm::kExprI64ShrU, input, jsgraph()->Int64Constant(1)),
1321 jsgraph()->Int64Constant(0x5555555555555555)),
1322 Binop(wasm::kExprI64And, input,
1323 jsgraph()->Int64Constant(0x5555555555555555)));
1324
1325 result = Binop(wasm::kExprI64Add,
1326 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
1327 jsgraph()->Int64Constant(2)),
1328 jsgraph()->Int64Constant(0x3333333333333333)),
1329 Binop(wasm::kExprI64And, result,
1330 jsgraph()->Int64Constant(0x3333333333333333)));
1331
1332 result = Binop(wasm::kExprI64Add,
1333 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
1334 jsgraph()->Int64Constant(4)),
1335 jsgraph()->Int64Constant(0x0f0f0f0f0f0f0f0f)),
1336 Binop(wasm::kExprI64And, result,
1337 jsgraph()->Int64Constant(0x0f0f0f0f0f0f0f0f)));
1338
1339 result = Binop(wasm::kExprI64Add,
1340 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
1341 jsgraph()->Int64Constant(8)),
1342 jsgraph()->Int64Constant(0x00ff00ff00ff00ff)),
1343 Binop(wasm::kExprI64And, result,
1344 jsgraph()->Int64Constant(0x00ff00ff00ff00ff)));
1345
1346 result = Binop(wasm::kExprI64Add,
1347 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
1348 jsgraph()->Int64Constant(16)),
1349 jsgraph()->Int64Constant(0x0000ffff0000ffff)),
1350 Binop(wasm::kExprI64And, result,
1351 jsgraph()->Int64Constant(0x0000ffff0000ffff)));
1352
1353 result = Binop(wasm::kExprI64Add,
1354 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
1355 jsgraph()->Int64Constant(32)),
1356 jsgraph()->Int64Constant(0x00000000ffffffff)),
1357 Binop(wasm::kExprI64And, result,
1358 jsgraph()->Int64Constant(0x00000000ffffffff)));
1359
1360 return result;
1361 }
1362
1363
BuildWasmCall(wasm::FunctionSig * sig,Node ** args)1364 Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args) {
1365 const size_t params = sig->parameter_count();
1366 const size_t extra = 2; // effect and control inputs.
1367 const size_t count = 1 + params + extra;
1368
1369 // Reallocate the buffer to make space for extra inputs.
1370 args = Realloc(args, count);
1371
1372 // Add effect and control inputs.
1373 args[params + 1] = *effect_;
1374 args[params + 2] = *control_;
1375
1376 const Operator* op = jsgraph()->common()->Call(
1377 module_->GetWasmCallDescriptor(jsgraph()->zone(), sig));
1378 Node* call = graph()->NewNode(op, static_cast<int>(count), args);
1379
1380 *effect_ = call;
1381 return call;
1382 }
1383
1384
CallDirect(uint32_t index,Node ** args)1385 Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args) {
1386 DCHECK_NULL(args[0]);
1387
1388 // Add code object as constant.
1389 args[0] = Constant(module_->GetFunctionCode(index));
1390 wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
1391
1392 return BuildWasmCall(sig, args);
1393 }
1394
1395
CallIndirect(uint32_t index,Node ** args)1396 Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args) {
1397 DCHECK_NOT_NULL(args[0]);
1398
1399 MachineOperatorBuilder* machine = jsgraph()->machine();
1400
1401 // Compute the code object by loading it from the function table.
1402 Node* key = args[0];
1403 Node* table = FunctionTable();
1404
1405 // Bounds check the index.
1406 int table_size = static_cast<int>(module_->FunctionTableSize());
1407 {
1408 Node* size = Int32Constant(static_cast<int>(table_size));
1409 Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
1410 trap_->AddTrapIfFalse(kTrapFuncInvalid, in_bounds);
1411 }
1412
1413 // Load signature from the table and check.
1414 // The table is a FixedArray; signatures are encoded as SMIs.
1415 // [sig1, sig2, sig3, ...., code1, code2, code3 ...]
1416 ElementAccess access = AccessBuilder::ForFixedArrayElement();
1417 const int fixed_offset = access.header_size - access.tag();
1418 {
1419 Node* load_sig = graph()->NewNode(
1420 machine->Load(MachineType::AnyTagged()), table,
1421 graph()->NewNode(machine->Int32Add(),
1422 graph()->NewNode(machine->Word32Shl(), key,
1423 Int32Constant(kPointerSizeLog2)),
1424 Int32Constant(fixed_offset)),
1425 *effect_, *control_);
1426 Node* sig_match = graph()->NewNode(machine->WordEqual(), load_sig,
1427 jsgraph()->SmiConstant(index));
1428 trap_->AddTrapIfFalse(kTrapFuncSigMismatch, sig_match);
1429 }
1430
1431 // Load code object from the table.
1432 int offset = fixed_offset + kPointerSize * table_size;
1433 Node* load_code = graph()->NewNode(
1434 machine->Load(MachineType::AnyTagged()), table,
1435 graph()->NewNode(machine->Int32Add(),
1436 graph()->NewNode(machine->Word32Shl(), key,
1437 Int32Constant(kPointerSizeLog2)),
1438 Int32Constant(offset)),
1439 *effect_, *control_);
1440
1441 args[0] = load_code;
1442 wasm::FunctionSig* sig = module_->GetSignature(index);
1443 return BuildWasmCall(sig, args);
1444 }
1445
1446
ToJS(Node * node,Node * context,wasm::LocalType type)1447 Node* WasmGraphBuilder::ToJS(Node* node, Node* context, wasm::LocalType type) {
1448 SimplifiedOperatorBuilder simplified(jsgraph()->zone());
1449 switch (type) {
1450 case wasm::kAstI32:
1451 return graph()->NewNode(simplified.ChangeInt32ToTagged(), node);
1452 case wasm::kAstI64:
1453 // TODO(titzer): i64->JS has no good solution right now. Using lower 32
1454 // bits.
1455 node =
1456 graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), node);
1457 return graph()->NewNode(simplified.ChangeInt32ToTagged(), node);
1458 case wasm::kAstF32:
1459 node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(),
1460 node);
1461 return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node);
1462 case wasm::kAstF64:
1463 return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node);
1464 case wasm::kAstStmt:
1465 return jsgraph()->UndefinedConstant();
1466 default:
1467 UNREACHABLE();
1468 return nullptr;
1469 }
1470 }
1471
1472
FromJS(Node * node,Node * context,wasm::LocalType type)1473 Node* WasmGraphBuilder::FromJS(Node* node, Node* context,
1474 wasm::LocalType type) {
1475 // Do a JavaScript ToNumber.
1476 Node* num =
1477 graph()->NewNode(jsgraph()->javascript()->ToNumber(), node, context,
1478 jsgraph()->EmptyFrameState(), *effect_, *control_);
1479 *control_ = num;
1480 *effect_ = num;
1481
1482 // Change representation.
1483 SimplifiedOperatorBuilder simplified(jsgraph()->zone());
1484 num = graph()->NewNode(simplified.ChangeTaggedToFloat64(), num);
1485
1486 switch (type) {
1487 case wasm::kAstI32: {
1488 num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToInt32(
1489 TruncationMode::kJavaScript),
1490 num);
1491 break;
1492 }
1493 case wasm::kAstI64:
1494 // TODO(titzer): JS->i64 has no good solution right now. Using 32 bits.
1495 num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToInt32(
1496 TruncationMode::kJavaScript),
1497 num);
1498 num = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), num);
1499 break;
1500 case wasm::kAstF32:
1501 num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToFloat32(),
1502 num);
1503 break;
1504 case wasm::kAstF64:
1505 break;
1506 case wasm::kAstStmt:
1507 num = jsgraph()->Int32Constant(0);
1508 break;
1509 default:
1510 UNREACHABLE();
1511 return nullptr;
1512 }
1513 return num;
1514 }
1515
1516
Invert(Node * node)1517 Node* WasmGraphBuilder::Invert(Node* node) {
1518 return Unop(wasm::kExprBoolNot, node);
1519 }
1520
1521
BuildJSToWasmWrapper(Handle<Code> wasm_code,wasm::FunctionSig * sig)1522 void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
1523 wasm::FunctionSig* sig) {
1524 int params = static_cast<int>(sig->parameter_count());
1525 int count = params + 3;
1526 Node** args = Buffer(count);
1527
1528 // Build the start and the JS parameter nodes.
1529 Node* start = Start(params + 3);
1530 *control_ = start;
1531 *effect_ = start;
1532 // JS context is the last parameter.
1533 Node* context = graph()->NewNode(
1534 jsgraph()->common()->Parameter(params + 1, "context"), start);
1535
1536 int pos = 0;
1537 args[pos++] = Constant(wasm_code);
1538
1539 // Convert JS parameters to WASM numbers.
1540 for (int i = 0; i < params; i++) {
1541 Node* param = graph()->NewNode(jsgraph()->common()->Parameter(i), start);
1542 args[pos++] = FromJS(param, context, sig->GetParam(i));
1543 }
1544
1545 args[pos++] = *effect_;
1546 args[pos++] = *control_;
1547
1548 // Call the WASM code.
1549 CallDescriptor* desc = module_->GetWasmCallDescriptor(jsgraph()->zone(), sig);
1550 Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), count, args);
1551 Node* jsval =
1552 ToJS(call, context,
1553 sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
1554 Node* ret =
1555 graph()->NewNode(jsgraph()->common()->Return(), jsval, call, start);
1556
1557 MergeControlToEnd(jsgraph(), ret);
1558 }
1559
1560
BuildWasmToJSWrapper(Handle<JSFunction> function,wasm::FunctionSig * sig)1561 void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
1562 wasm::FunctionSig* sig) {
1563 int js_count = function->shared()->internal_formal_parameter_count();
1564 int wasm_count = static_cast<int>(sig->parameter_count());
1565
1566 // Build the start and the parameter nodes.
1567 Isolate* isolate = jsgraph()->isolate();
1568 CallDescriptor* desc;
1569 Node* start = Start(wasm_count + 3);
1570 *effect_ = start;
1571 *control_ = start;
1572 // JS context is the last parameter.
1573 Node* context = Constant(Handle<Context>(function->context(), isolate));
1574 Node** args = Buffer(wasm_count + 7);
1575
1576 bool arg_count_before_args = false;
1577 bool add_new_target_undefined = false;
1578
1579 int pos = 0;
1580 if (js_count == wasm_count) {
1581 // exact arity match, just call the function directly.
1582 desc = Linkage::GetJSCallDescriptor(graph()->zone(), false, wasm_count + 1,
1583 CallDescriptor::kNoFlags);
1584 arg_count_before_args = false;
1585 add_new_target_undefined = true;
1586 } else {
1587 // Use the Call builtin.
1588 Callable callable = CodeFactory::Call(isolate);
1589 args[pos++] = jsgraph()->HeapConstant(callable.code());
1590 desc = Linkage::GetStubCallDescriptor(isolate, graph()->zone(),
1591 callable.descriptor(), wasm_count + 1,
1592 CallDescriptor::kNoFlags);
1593 arg_count_before_args = true;
1594 }
1595
1596 args[pos++] = jsgraph()->Constant(function); // JS function.
1597 if (arg_count_before_args) {
1598 args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
1599 }
1600 // JS receiver.
1601 Handle<Object> global(function->context()->global_object(), isolate);
1602 args[pos++] = jsgraph()->Constant(global);
1603
1604 // Convert WASM numbers to JS values.
1605 for (int i = 0; i < wasm_count; i++) {
1606 Node* param = graph()->NewNode(jsgraph()->common()->Parameter(i), start);
1607 args[pos++] = ToJS(param, context, sig->GetParam(i));
1608 }
1609
1610 if (add_new_target_undefined) {
1611 args[pos++] = jsgraph()->UndefinedConstant(); // new target
1612 }
1613
1614 if (!arg_count_before_args) {
1615 args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
1616 }
1617 args[pos++] = context;
1618 args[pos++] = *effect_;
1619 args[pos++] = *control_;
1620
1621 Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), pos, args);
1622
1623 // Convert the return value back.
1624 Node* val =
1625 FromJS(call, context,
1626 sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
1627 Node* ret = graph()->NewNode(jsgraph()->common()->Return(), val, call, start);
1628
1629 MergeControlToEnd(jsgraph(), ret);
1630 }
1631
1632
MemBuffer(uint32_t offset)1633 Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
1634 if (offset == 0) {
1635 if (!mem_buffer_)
1636 mem_buffer_ = jsgraph()->IntPtrConstant(module_->mem_start);
1637 return mem_buffer_;
1638 } else {
1639 return jsgraph()->IntPtrConstant(module_->mem_start + offset);
1640 }
1641 }
1642
1643
MemSize(uint32_t offset)1644 Node* WasmGraphBuilder::MemSize(uint32_t offset) {
1645 int32_t size = static_cast<int>(module_->mem_end - module_->mem_start);
1646 if (offset == 0) {
1647 if (!mem_size_) mem_size_ = jsgraph()->Int32Constant(size);
1648 return mem_size_;
1649 } else {
1650 return jsgraph()->Int32Constant(size + offset);
1651 }
1652 }
1653
1654
FunctionTable()1655 Node* WasmGraphBuilder::FunctionTable() {
1656 if (!function_table_) {
1657 DCHECK(!module_->function_table.is_null());
1658 function_table_ = jsgraph()->Constant(module_->function_table);
1659 }
1660 return function_table_;
1661 }
1662
1663
LoadGlobal(uint32_t index)1664 Node* WasmGraphBuilder::LoadGlobal(uint32_t index) {
1665 MachineType mem_type = module_->GetGlobalType(index);
1666 Node* addr = jsgraph()->IntPtrConstant(
1667 module_->globals_area + module_->module->globals->at(index).offset);
1668 const Operator* op = jsgraph()->machine()->Load(mem_type);
1669 Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), *effect_,
1670 *control_);
1671 *effect_ = node;
1672 return node;
1673 }
1674
1675
StoreGlobal(uint32_t index,Node * val)1676 Node* WasmGraphBuilder::StoreGlobal(uint32_t index, Node* val) {
1677 MachineType mem_type = module_->GetGlobalType(index);
1678 Node* addr = jsgraph()->IntPtrConstant(
1679 module_->globals_area + module_->module->globals->at(index).offset);
1680 const Operator* op = jsgraph()->machine()->Store(
1681 StoreRepresentation(mem_type.representation(), kNoWriteBarrier));
1682 Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), val,
1683 *effect_, *control_);
1684 *effect_ = node;
1685 return node;
1686 }
1687
1688
BoundsCheckMem(MachineType memtype,Node * index,uint32_t offset)1689 void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
1690 uint32_t offset) {
1691 // TODO(turbofan): fold bounds checks for constant indexes.
1692 CHECK_GE(module_->mem_end, module_->mem_start);
1693 ptrdiff_t size = module_->mem_end - module_->mem_start;
1694 byte memsize = wasm::WasmOpcodes::MemSize(memtype);
1695 Node* cond;
1696 if (static_cast<ptrdiff_t>(offset) >= size ||
1697 static_cast<ptrdiff_t>(offset + memsize) > size) {
1698 // The access will always throw.
1699 cond = jsgraph()->Int32Constant(0);
1700 } else {
1701 // Check against the limit.
1702 size_t limit = size - offset - memsize;
1703 CHECK(limit <= kMaxUInt32);
1704 cond = graph()->NewNode(
1705 jsgraph()->machine()->Uint32LessThanOrEqual(), index,
1706 jsgraph()->Int32Constant(static_cast<uint32_t>(limit)));
1707 }
1708
1709 trap_->AddTrapIfFalse(kTrapMemOutOfBounds, cond);
1710 }
1711
1712
LoadMem(wasm::LocalType type,MachineType memtype,Node * index,uint32_t offset)1713 Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype,
1714 Node* index, uint32_t offset) {
1715 Node* load;
1716
1717 if (module_ && module_->asm_js) {
1718 // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
1719 DCHECK_EQ(0, offset);
1720 const Operator* op = jsgraph()->machine()->CheckedLoad(memtype);
1721 load = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), *effect_,
1722 *control_);
1723 } else {
1724 // WASM semantics throw on OOB. Introduce explicit bounds check.
1725 BoundsCheckMem(memtype, index, offset);
1726 load = graph()->NewNode(jsgraph()->machine()->Load(memtype),
1727 MemBuffer(offset), index, *effect_, *control_);
1728 }
1729
1730 *effect_ = load;
1731
1732 if (type == wasm::kAstI64 &&
1733 ElementSizeLog2Of(memtype.representation()) < 3) {
1734 // TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes.
1735 if (memtype.IsSigned()) {
1736 // sign extend
1737 load = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), load);
1738 } else {
1739 // zero extend
1740 load =
1741 graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), load);
1742 }
1743 }
1744
1745 return load;
1746 }
1747
1748
StoreMem(MachineType memtype,Node * index,uint32_t offset,Node * val)1749 Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
1750 uint32_t offset, Node* val) {
1751 Node* store;
1752 if (module_ && module_->asm_js) {
1753 // asm.js semantics use CheckedStore (i.e. ignore OOB writes).
1754 DCHECK_EQ(0, offset);
1755 const Operator* op =
1756 jsgraph()->machine()->CheckedStore(memtype.representation());
1757 store = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val, *effect_,
1758 *control_);
1759 } else {
1760 // WASM semantics throw on OOB. Introduce explicit bounds check.
1761 BoundsCheckMem(memtype, index, offset);
1762 StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
1763 store =
1764 graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset),
1765 index, val, *effect_, *control_);
1766 }
1767 *effect_ = store;
1768 return store;
1769 }
1770
1771
PrintDebugName(Node * node)1772 void WasmGraphBuilder::PrintDebugName(Node* node) {
1773 PrintF("#%d:%s", node->id(), node->op()->mnemonic());
1774 }
1775
1776
String(const char * string)1777 Node* WasmGraphBuilder::String(const char* string) {
1778 return jsgraph()->Constant(
1779 jsgraph()->isolate()->factory()->NewStringFromAsciiChecked(string));
1780 }
1781
1782
graph()1783 Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); }
1784
1785
CompileJSToWasmWrapper(Isolate * isolate,wasm::ModuleEnv * module,Handle<String> name,Handle<Code> wasm_code,Handle<JSObject> module_object,uint32_t index)1786 Handle<JSFunction> CompileJSToWasmWrapper(
1787 Isolate* isolate, wasm::ModuleEnv* module, Handle<String> name,
1788 Handle<Code> wasm_code, Handle<JSObject> module_object, uint32_t index) {
1789 wasm::WasmFunction* func = &module->module->functions->at(index);
1790
1791 //----------------------------------------------------------------------------
1792 // Create the JSFunction object.
1793 //----------------------------------------------------------------------------
1794 Handle<SharedFunctionInfo> shared =
1795 isolate->factory()->NewSharedFunctionInfo(name, wasm_code, false);
1796 int params = static_cast<int>(func->sig->parameter_count());
1797 shared->set_length(params);
1798 shared->set_internal_formal_parameter_count(1 + params);
1799 Handle<JSFunction> function = isolate->factory()->NewFunction(
1800 isolate->wasm_function_map(), name, MaybeHandle<Code>());
1801 function->SetInternalField(0, *module_object);
1802 function->set_shared(*shared);
1803
1804 //----------------------------------------------------------------------------
1805 // Create the Graph
1806 //----------------------------------------------------------------------------
1807 Zone zone;
1808 Graph graph(&zone);
1809 CommonOperatorBuilder common(&zone);
1810 JSOperatorBuilder javascript(&zone);
1811 MachineOperatorBuilder machine(&zone);
1812 JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine);
1813
1814 Node* control = nullptr;
1815 Node* effect = nullptr;
1816
1817 WasmGraphBuilder builder(&zone, &jsgraph, func->sig);
1818 builder.set_control_ptr(&control);
1819 builder.set_effect_ptr(&effect);
1820 builder.set_module(module);
1821 builder.BuildJSToWasmWrapper(wasm_code, func->sig);
1822
1823 //----------------------------------------------------------------------------
1824 // Run the compilation pipeline.
1825 //----------------------------------------------------------------------------
1826 {
1827 // Changes lowering requires types.
1828 Typer typer(isolate, &graph);
1829 NodeVector roots(&zone);
1830 jsgraph.GetCachedNodes(&roots);
1831 typer.Run(roots);
1832
1833 // Run generic and change lowering.
1834 JSGenericLowering generic(true, &jsgraph);
1835 ChangeLowering changes(&jsgraph);
1836 GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead());
1837 graph_reducer.AddReducer(&changes);
1838 graph_reducer.AddReducer(&generic);
1839 graph_reducer.ReduceGraph();
1840
1841 if (FLAG_trace_turbo_graph) { // Simple textual RPO.
1842 OFStream os(stdout);
1843 os << "-- Graph after change lowering -- " << std::endl;
1844 os << AsRPO(graph);
1845 }
1846
1847 // Schedule and compile to machine code.
1848 int params = static_cast<int>(
1849 module->GetFunctionSignature(index)->parameter_count());
1850 CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
1851 &zone, false, params + 1, CallDescriptor::kNoFlags);
1852 CompilationInfo info("js-to-wasm", isolate, &zone);
1853 // TODO(titzer): this is technically a WASM wrapper, not a wasm function.
1854 info.set_output_code_kind(Code::WASM_FUNCTION);
1855 Handle<Code> code =
1856 Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
1857
1858 #ifdef ENABLE_DISASSEMBLER
1859 // Disassemble the wrapper code for debugging.
1860 if (!code.is_null() && FLAG_print_opt_code) {
1861 Vector<char> buffer;
1862 const char* name = "";
1863 if (func->name_offset > 0) {
1864 const byte* ptr = module->module->module_start + func->name_offset;
1865 name = reinterpret_cast<const char*>(ptr);
1866 }
1867 SNPrintF(buffer, "JS->WASM function wrapper #%d:%s", index, name);
1868 OFStream os(stdout);
1869 code->Disassemble(buffer.start(), os);
1870 }
1871 #endif
1872 // Set the JSFunction's machine code.
1873 function->set_code(*code);
1874 }
1875 return function;
1876 }
1877
1878
CompileWasmToJSWrapper(Isolate * isolate,wasm::ModuleEnv * module,Handle<JSFunction> function,uint32_t index)1879 Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
1880 Handle<JSFunction> function,
1881 uint32_t index) {
1882 wasm::WasmFunction* func = &module->module->functions->at(index);
1883
1884 //----------------------------------------------------------------------------
1885 // Create the Graph
1886 //----------------------------------------------------------------------------
1887 Zone zone;
1888 Graph graph(&zone);
1889 CommonOperatorBuilder common(&zone);
1890 JSOperatorBuilder javascript(&zone);
1891 MachineOperatorBuilder machine(&zone);
1892 JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine);
1893
1894 Node* control = nullptr;
1895 Node* effect = nullptr;
1896
1897 WasmGraphBuilder builder(&zone, &jsgraph, func->sig);
1898 builder.set_control_ptr(&control);
1899 builder.set_effect_ptr(&effect);
1900 builder.set_module(module);
1901 builder.BuildWasmToJSWrapper(function, func->sig);
1902
1903 Handle<Code> code = Handle<Code>::null();
1904 {
1905 // Changes lowering requires types.
1906 Typer typer(isolate, &graph);
1907 NodeVector roots(&zone);
1908 jsgraph.GetCachedNodes(&roots);
1909 typer.Run(roots);
1910
1911 // Run generic and change lowering.
1912 JSGenericLowering generic(true, &jsgraph);
1913 ChangeLowering changes(&jsgraph);
1914 GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead());
1915 graph_reducer.AddReducer(&changes);
1916 graph_reducer.AddReducer(&generic);
1917 graph_reducer.ReduceGraph();
1918
1919 if (FLAG_trace_turbo_graph) { // Simple textual RPO.
1920 OFStream os(stdout);
1921 os << "-- Graph after change lowering -- " << std::endl;
1922 os << AsRPO(graph);
1923 }
1924
1925 // Schedule and compile to machine code.
1926 CallDescriptor* incoming = module->GetWasmCallDescriptor(&zone, func->sig);
1927 CompilationInfo info("wasm-to-js", isolate, &zone);
1928 // TODO(titzer): this is technically a WASM wrapper, not a wasm function.
1929 info.set_output_code_kind(Code::WASM_FUNCTION);
1930 code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
1931
1932 #ifdef ENABLE_DISASSEMBLER
1933 // Disassemble the wrapper code for debugging.
1934 if (!code.is_null() && FLAG_print_opt_code) {
1935 Vector<char> buffer;
1936 const char* name = "";
1937 if (func->name_offset > 0) {
1938 const byte* ptr = module->module->module_start + func->name_offset;
1939 name = reinterpret_cast<const char*>(ptr);
1940 }
1941 SNPrintF(buffer, "WASM->JS function wrapper #%d:%s", index, name);
1942 OFStream os(stdout);
1943 code->Disassemble(buffer.start(), os);
1944 }
1945 #endif
1946 }
1947 return code;
1948 }
1949
1950
1951 // Helper function to compile a single function.
CompileWasmFunction(wasm::ErrorThrower & thrower,Isolate * isolate,wasm::ModuleEnv * module_env,const wasm::WasmFunction & function,int index)1952 Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
1953 wasm::ModuleEnv* module_env,
1954 const wasm::WasmFunction& function,
1955 int index) {
1956 if (FLAG_trace_wasm_compiler || FLAG_trace_wasm_decode_time) {
1957 // TODO(titzer): clean me up a bit.
1958 OFStream os(stdout);
1959 os << "Compiling WASM function #" << index << ":";
1960 if (function.name_offset > 0) {
1961 os << module_env->module->GetName(function.name_offset);
1962 }
1963 os << std::endl;
1964 }
1965 // Initialize the function environment for decoding.
1966 wasm::FunctionEnv env;
1967 env.module = module_env;
1968 env.sig = function.sig;
1969 env.local_int32_count = function.local_int32_count;
1970 env.local_int64_count = function.local_int64_count;
1971 env.local_float32_count = function.local_float32_count;
1972 env.local_float64_count = function.local_float64_count;
1973 env.SumLocals();
1974
1975 // Create a TF graph during decoding.
1976 Zone zone;
1977 Graph graph(&zone);
1978 CommonOperatorBuilder common(&zone);
1979 MachineOperatorBuilder machine(
1980 &zone, MachineType::PointerRepresentation(),
1981 InstructionSelector::SupportedMachineOperatorFlags());
1982 JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
1983 WasmGraphBuilder builder(&zone, &jsgraph, function.sig);
1984 wasm::TreeResult result = wasm::BuildTFGraph(
1985 &builder, &env, // --
1986 module_env->module->module_start, // --
1987 module_env->module->module_start + function.code_start_offset, // --
1988 module_env->module->module_start + function.code_end_offset); // --
1989
1990 if (result.failed()) {
1991 if (FLAG_trace_wasm_compiler) {
1992 OFStream os(stdout);
1993 os << "Compilation failed: " << result << std::endl;
1994 }
1995 // Add the function as another context for the exception
1996 Vector<char> buffer;
1997 SNPrintF(buffer, "Compiling WASM function #%d:%s failed:", index,
1998 module_env->module->GetName(function.name_offset));
1999 thrower.Failed(buffer.start(), result);
2000 return Handle<Code>::null();
2001 }
2002
2003 // Run the compiler pipeline to generate machine code.
2004 CallDescriptor* descriptor = const_cast<CallDescriptor*>(
2005 module_env->GetWasmCallDescriptor(&zone, function.sig));
2006 CompilationInfo info("wasm", isolate, &zone);
2007 info.set_output_code_kind(Code::WASM_FUNCTION);
2008 Handle<Code> code =
2009 Pipeline::GenerateCodeForTesting(&info, descriptor, &graph);
2010
2011 #ifdef ENABLE_DISASSEMBLER
2012 // Disassemble the code for debugging.
2013 if (!code.is_null() && FLAG_print_opt_code) {
2014 Vector<char> buffer;
2015 const char* name = "";
2016 if (function.name_offset > 0) {
2017 const byte* ptr = module_env->module->module_start + function.name_offset;
2018 name = reinterpret_cast<const char*>(ptr);
2019 }
2020 SNPrintF(buffer, "WASM function #%d:%s", index, name);
2021 OFStream os(stdout);
2022 code->Disassemble(buffer.start(), os);
2023 }
2024 #endif
2025 return code;
2026 }
2027
2028
2029 } // namespace compiler
2030 } // namespace internal
2031 } // namespace v8
2032