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