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