Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(305)

Side by Side Diff: src/compiler/wasm-compiler.cc

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

Powered by Google App Engine
This is Rietveld 408576698