• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/compiler/simd-scalar-lowering.h"
6 #include "src/compiler/diamond.h"
7 #include "src/compiler/linkage.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
10 
11 #include "src/compiler/node.h"
12 #include "src/objects-inl.h"
13 #include "src/wasm/wasm-module.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18 
SimdScalarLowering(Graph * graph,MachineOperatorBuilder * machine,CommonOperatorBuilder * common,Zone * zone,Signature<MachineRepresentation> * signature)19 SimdScalarLowering::SimdScalarLowering(
20     Graph* graph, MachineOperatorBuilder* machine,
21     CommonOperatorBuilder* common, Zone* zone,
22     Signature<MachineRepresentation>* signature)
23     : zone_(zone),
24       graph_(graph),
25       machine_(machine),
26       common_(common),
27       state_(graph, 3),
28       stack_(zone),
29       replacements_(nullptr),
30       signature_(signature),
31       placeholder_(
32           graph->NewNode(common->Parameter(-2, "placeholder"), graph->start())),
33       parameter_count_after_lowering_(-1) {
34   DCHECK_NOT_NULL(graph);
35   DCHECK_NOT_NULL(graph->end());
36   replacements_ = zone->NewArray<Replacement>(graph->NodeCount());
37   memset(replacements_, 0, sizeof(Replacement) * graph->NodeCount());
38 }
39 
LowerGraph()40 void SimdScalarLowering::LowerGraph() {
41   stack_.push_back({graph()->end(), 0});
42   state_.Set(graph()->end(), State::kOnStack);
43   replacements_[graph()->end()->id()].type = SimdType::kInt32;
44 
45   while (!stack_.empty()) {
46     NodeState& top = stack_.back();
47     if (top.input_index == top.node->InputCount()) {
48       // All inputs of top have already been lowered, now lower top.
49       stack_.pop_back();
50       state_.Set(top.node, State::kVisited);
51       LowerNode(top.node);
52     } else {
53       // Push the next input onto the stack.
54       Node* input = top.node->InputAt(top.input_index++);
55       if (state_.Get(input) == State::kUnvisited) {
56         SetLoweredType(input, top.node);
57         if (input->opcode() == IrOpcode::kPhi) {
58           // To break cycles with phi nodes we push phis on a separate stack so
59           // that they are processed after all other nodes.
60           PreparePhiReplacement(input);
61           stack_.push_front({input, 0});
62         } else if (input->opcode() == IrOpcode::kEffectPhi ||
63                    input->opcode() == IrOpcode::kLoop) {
64           stack_.push_front({input, 0});
65         } else {
66           stack_.push_back({input, 0});
67         }
68         state_.Set(input, State::kOnStack);
69       }
70     }
71   }
72 }
73 
74 #define FOREACH_INT32X4_OPCODE(V) \
75   V(Int32x4Add)                   \
76   V(Int32x4ExtractLane)           \
77   V(CreateInt32x4)                \
78   V(Int32x4ReplaceLane)
79 
80 #define FOREACH_FLOAT32X4_OPCODE(V) \
81   V(Float32x4Add)                   \
82   V(Float32x4ExtractLane)           \
83   V(CreateFloat32x4)                \
84   V(Float32x4ReplaceLane)
85 
SetLoweredType(Node * node,Node * output)86 void SimdScalarLowering::SetLoweredType(Node* node, Node* output) {
87   switch (node->opcode()) {
88 #define CASE_STMT(name) case IrOpcode::k##name:
89     FOREACH_INT32X4_OPCODE(CASE_STMT)
90     case IrOpcode::kReturn:
91     case IrOpcode::kParameter:
92     case IrOpcode::kCall: {
93       replacements_[node->id()].type = SimdType::kInt32;
94       break;
95     }
96       FOREACH_FLOAT32X4_OPCODE(CASE_STMT) {
97         replacements_[node->id()].type = SimdType::kFloat32;
98         break;
99       }
100 #undef CASE_STMT
101     default:
102       replacements_[node->id()].type = replacements_[output->id()].type;
103   }
104 }
105 
GetParameterIndexAfterLowering(Signature<MachineRepresentation> * signature,int old_index)106 static int GetParameterIndexAfterLowering(
107     Signature<MachineRepresentation>* signature, int old_index) {
108   // In function calls, the simd128 types are passed as 4 Int32 types. The
109   // parameters are typecast to the types as needed for various operations.
110   int result = old_index;
111   for (int i = 0; i < old_index; ++i) {
112     if (signature->GetParam(i) == MachineRepresentation::kSimd128) {
113       result += 3;
114     }
115   }
116   return result;
117 }
118 
GetParameterCountAfterLowering()119 int SimdScalarLowering::GetParameterCountAfterLowering() {
120   if (parameter_count_after_lowering_ == -1) {
121     // GetParameterIndexAfterLowering(parameter_count) returns the parameter
122     // count after lowering.
123     parameter_count_after_lowering_ = GetParameterIndexAfterLowering(
124         signature(), static_cast<int>(signature()->parameter_count()));
125   }
126   return parameter_count_after_lowering_;
127 }
128 
GetReturnCountAfterLowering(Signature<MachineRepresentation> * signature)129 static int GetReturnCountAfterLowering(
130     Signature<MachineRepresentation>* signature) {
131   int result = static_cast<int>(signature->return_count());
132   for (int i = 0; i < static_cast<int>(signature->return_count()); ++i) {
133     if (signature->GetReturn(i) == MachineRepresentation::kSimd128) {
134       result += 3;
135     }
136   }
137   return result;
138 }
139 
GetIndexNodes(Node * index,Node ** new_indices)140 void SimdScalarLowering::GetIndexNodes(Node* index, Node** new_indices) {
141   new_indices[0] = index;
142   for (size_t i = 1; i < kMaxLanes; ++i) {
143     new_indices[i] = graph()->NewNode(machine()->Int32Add(), index,
144                                       graph()->NewNode(common()->Int32Constant(
145                                           static_cast<int>(i) * kLaneWidth)));
146   }
147 }
148 
LowerLoadOp(MachineRepresentation rep,Node * node,const Operator * load_op)149 void SimdScalarLowering::LowerLoadOp(MachineRepresentation rep, Node* node,
150                                      const Operator* load_op) {
151   if (rep == MachineRepresentation::kSimd128) {
152     Node* base = node->InputAt(0);
153     Node* index = node->InputAt(1);
154     Node* indices[kMaxLanes];
155     GetIndexNodes(index, indices);
156     Node* rep_nodes[kMaxLanes];
157     rep_nodes[0] = node;
158     NodeProperties::ChangeOp(rep_nodes[0], load_op);
159     if (node->InputCount() > 2) {
160       DCHECK(node->InputCount() > 3);
161       Node* effect_input = node->InputAt(2);
162       Node* control_input = node->InputAt(3);
163       rep_nodes[3] = graph()->NewNode(load_op, base, indices[3], effect_input,
164                                       control_input);
165       rep_nodes[2] = graph()->NewNode(load_op, base, indices[2], rep_nodes[3],
166                                       control_input);
167       rep_nodes[1] = graph()->NewNode(load_op, base, indices[1], rep_nodes[2],
168                                       control_input);
169       rep_nodes[0]->ReplaceInput(2, rep_nodes[1]);
170     } else {
171       for (size_t i = 1; i < kMaxLanes; ++i) {
172         rep_nodes[i] = graph()->NewNode(load_op, base, indices[i]);
173       }
174     }
175     ReplaceNode(node, rep_nodes);
176   } else {
177     DefaultLowering(node);
178   }
179 }
180 
LowerStoreOp(MachineRepresentation rep,Node * node,const Operator * store_op,SimdType rep_type)181 void SimdScalarLowering::LowerStoreOp(MachineRepresentation rep, Node* node,
182                                       const Operator* store_op,
183                                       SimdType rep_type) {
184   if (rep == MachineRepresentation::kSimd128) {
185     Node* base = node->InputAt(0);
186     Node* index = node->InputAt(1);
187     Node* indices[kMaxLanes];
188     GetIndexNodes(index, indices);
189     DCHECK(node->InputCount() > 2);
190     Node* value = node->InputAt(2);
191     DCHECK(HasReplacement(1, value));
192     Node* rep_nodes[kMaxLanes];
193     rep_nodes[0] = node;
194     Node** rep_inputs = GetReplacementsWithType(value, rep_type);
195     rep_nodes[0]->ReplaceInput(2, rep_inputs[0]);
196     NodeProperties::ChangeOp(node, store_op);
197     if (node->InputCount() > 3) {
198       DCHECK(node->InputCount() > 4);
199       Node* effect_input = node->InputAt(3);
200       Node* control_input = node->InputAt(4);
201       rep_nodes[3] = graph()->NewNode(store_op, base, indices[3], rep_inputs[3],
202                                       effect_input, control_input);
203       rep_nodes[2] = graph()->NewNode(store_op, base, indices[2], rep_inputs[2],
204                                       rep_nodes[3], control_input);
205       rep_nodes[1] = graph()->NewNode(store_op, base, indices[1], rep_inputs[1],
206                                       rep_nodes[2], control_input);
207       rep_nodes[0]->ReplaceInput(3, rep_nodes[1]);
208 
209     } else {
210       for (size_t i = 1; i < kMaxLanes; ++i) {
211         rep_nodes[i] =
212             graph()->NewNode(store_op, base, indices[i], rep_inputs[i]);
213       }
214     }
215 
216     ReplaceNode(node, rep_nodes);
217   } else {
218     DefaultLowering(node);
219   }
220 }
221 
LowerBinaryOp(Node * node,SimdType rep_type,const Operator * op)222 void SimdScalarLowering::LowerBinaryOp(Node* node, SimdType rep_type,
223                                        const Operator* op) {
224   DCHECK(node->InputCount() == 2);
225   Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
226   Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
227   Node* rep_node[kMaxLanes];
228   for (int i = 0; i < kMaxLanes; ++i) {
229     rep_node[i] = graph()->NewNode(op, rep_left[i], rep_right[i]);
230   }
231   ReplaceNode(node, rep_node);
232 }
233 
LowerNode(Node * node)234 void SimdScalarLowering::LowerNode(Node* node) {
235   SimdType rep_type = ReplacementType(node);
236   switch (node->opcode()) {
237     case IrOpcode::kStart: {
238       int parameter_count = GetParameterCountAfterLowering();
239       // Only exchange the node if the parameter count actually changed.
240       if (parameter_count != static_cast<int>(signature()->parameter_count())) {
241         int delta =
242             parameter_count - static_cast<int>(signature()->parameter_count());
243         int new_output_count = node->op()->ValueOutputCount() + delta;
244         NodeProperties::ChangeOp(node, common()->Start(new_output_count));
245       }
246       break;
247     }
248     case IrOpcode::kParameter: {
249       DCHECK(node->InputCount() == 1);
250       // Only exchange the node if the parameter count actually changed. We do
251       // not even have to do the default lowering because the the start node,
252       // the only input of a parameter node, only changes if the parameter count
253       // changes.
254       if (GetParameterCountAfterLowering() !=
255           static_cast<int>(signature()->parameter_count())) {
256         int old_index = ParameterIndexOf(node->op());
257         int new_index = GetParameterIndexAfterLowering(signature(), old_index);
258         if (old_index == new_index) {
259           NodeProperties::ChangeOp(node, common()->Parameter(new_index));
260 
261           Node* new_node[kMaxLanes];
262           for (int i = 0; i < kMaxLanes; ++i) {
263             new_node[i] = nullptr;
264           }
265           new_node[0] = node;
266           if (signature()->GetParam(old_index) ==
267               MachineRepresentation::kSimd128) {
268             for (int i = 1; i < kMaxLanes; ++i) {
269               new_node[i] = graph()->NewNode(common()->Parameter(new_index + i),
270                                              graph()->start());
271             }
272           }
273           ReplaceNode(node, new_node);
274         }
275       }
276       break;
277     }
278     case IrOpcode::kLoad: {
279       MachineRepresentation rep =
280           LoadRepresentationOf(node->op()).representation();
281       const Operator* load_op;
282       if (rep_type == SimdType::kInt32) {
283         load_op = machine()->Load(MachineType::Int32());
284       } else if (rep_type == SimdType::kFloat32) {
285         load_op = machine()->Load(MachineType::Float32());
286       }
287       LowerLoadOp(rep, node, load_op);
288       break;
289     }
290     case IrOpcode::kUnalignedLoad: {
291       MachineRepresentation rep =
292           UnalignedLoadRepresentationOf(node->op()).representation();
293       const Operator* load_op;
294       if (rep_type == SimdType::kInt32) {
295         load_op = machine()->UnalignedLoad(MachineType::Int32());
296       } else if (rep_type == SimdType::kFloat32) {
297         load_op = machine()->UnalignedLoad(MachineType::Float32());
298       }
299       LowerLoadOp(rep, node, load_op);
300       break;
301     }
302     case IrOpcode::kStore: {
303       MachineRepresentation rep =
304           StoreRepresentationOf(node->op()).representation();
305       WriteBarrierKind write_barrier_kind =
306           StoreRepresentationOf(node->op()).write_barrier_kind();
307       const Operator* store_op;
308       if (rep_type == SimdType::kInt32) {
309         store_op = machine()->Store(StoreRepresentation(
310             MachineRepresentation::kWord32, write_barrier_kind));
311       } else {
312         store_op = machine()->Store(StoreRepresentation(
313             MachineRepresentation::kFloat32, write_barrier_kind));
314       }
315       LowerStoreOp(rep, node, store_op, rep_type);
316       break;
317     }
318     case IrOpcode::kUnalignedStore: {
319       MachineRepresentation rep = UnalignedStoreRepresentationOf(node->op());
320       const Operator* store_op;
321       if (rep_type == SimdType::kInt32) {
322         store_op = machine()->UnalignedStore(MachineRepresentation::kWord32);
323       } else {
324         store_op = machine()->UnalignedStore(MachineRepresentation::kFloat32);
325       }
326       LowerStoreOp(rep, node, store_op, rep_type);
327       break;
328     }
329     case IrOpcode::kReturn: {
330       DefaultLowering(node);
331       int new_return_count = GetReturnCountAfterLowering(signature());
332       if (static_cast<int>(signature()->return_count()) != new_return_count) {
333         NodeProperties::ChangeOp(node, common()->Return(new_return_count));
334       }
335       break;
336     }
337     case IrOpcode::kCall: {
338       // TODO(turbofan): Make WASM code const-correct wrt. CallDescriptor.
339       CallDescriptor* descriptor =
340           const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
341       if (DefaultLowering(node) ||
342           (descriptor->ReturnCount() == 1 &&
343            descriptor->GetReturnType(0) == MachineType::Simd128())) {
344         // We have to adjust the call descriptor.
345         const Operator* op =
346             common()->Call(wasm::ModuleEnv::GetI32WasmCallDescriptorForSimd(
347                 zone(), descriptor));
348         NodeProperties::ChangeOp(node, op);
349       }
350       if (descriptor->ReturnCount() == 1 &&
351           descriptor->GetReturnType(0) == MachineType::Simd128()) {
352         // We access the additional return values through projections.
353         Node* rep_node[kMaxLanes];
354         for (int i = 0; i < kMaxLanes; ++i) {
355           rep_node[i] =
356               graph()->NewNode(common()->Projection(i), node, graph()->start());
357         }
358         ReplaceNode(node, rep_node);
359       }
360       break;
361     }
362     case IrOpcode::kPhi: {
363       MachineRepresentation rep = PhiRepresentationOf(node->op());
364       if (rep == MachineRepresentation::kSimd128) {
365         // The replacement nodes have already been created, we only have to
366         // replace placeholder nodes.
367         Node** rep_node = GetReplacements(node);
368         for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
369           Node** rep_input =
370               GetReplacementsWithType(node->InputAt(i), rep_type);
371           for (int j = 0; j < kMaxLanes; j++) {
372             rep_node[j]->ReplaceInput(i, rep_input[j]);
373           }
374         }
375       } else {
376         DefaultLowering(node);
377       }
378       break;
379     }
380     case IrOpcode::kInt32x4Add: {
381       LowerBinaryOp(node, rep_type, machine()->Int32Add());
382       break;
383     }
384     case IrOpcode::kFloat32x4Add: {
385       LowerBinaryOp(node, rep_type, machine()->Float32Add());
386       break;
387     }
388     case IrOpcode::kCreateInt32x4:
389     case IrOpcode::kCreateFloat32x4: {
390       Node* rep_node[kMaxLanes];
391       for (int i = 0; i < kMaxLanes; ++i) {
392         if (HasReplacement(0, node->InputAt(i))) {
393           rep_node[i] = GetReplacements(node->InputAt(i))[0];
394         } else {
395           rep_node[i] = node->InputAt(i);
396         }
397       }
398       ReplaceNode(node, rep_node);
399       break;
400     }
401     case IrOpcode::kInt32x4ExtractLane:
402     case IrOpcode::kFloat32x4ExtractLane: {
403       int32_t lane = OpParameter<int32_t>(node);
404       Node* rep_node[kMaxLanes] = {
405           GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr,
406           nullptr, nullptr};
407       ReplaceNode(node, rep_node);
408       break;
409     }
410     case IrOpcode::kInt32x4ReplaceLane:
411     case IrOpcode::kFloat32x4ReplaceLane: {
412       DCHECK_EQ(2, node->InputCount());
413       Node* repNode = node->InputAt(1);
414       int32_t lane = OpParameter<int32_t>(node);
415       DCHECK(lane >= 0 && lane <= 3);
416       Node** rep_node = GetReplacementsWithType(node->InputAt(0), rep_type);
417       if (HasReplacement(0, repNode)) {
418         rep_node[lane] = GetReplacements(repNode)[0];
419       } else {
420         rep_node[lane] = repNode;
421       }
422       ReplaceNode(node, rep_node);
423       break;
424     }
425     default: { DefaultLowering(node); }
426   }
427 }
428 
DefaultLowering(Node * node)429 bool SimdScalarLowering::DefaultLowering(Node* node) {
430   bool something_changed = false;
431   for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) {
432     Node* input = node->InputAt(i);
433     if (HasReplacement(0, input)) {
434       something_changed = true;
435       node->ReplaceInput(i, GetReplacements(input)[0]);
436     }
437     if (HasReplacement(1, input)) {
438       something_changed = true;
439       for (int j = 1; j < kMaxLanes; j++) {
440         node->InsertInput(zone(), i + j, GetReplacements(input)[j]);
441       }
442     }
443   }
444   return something_changed;
445 }
446 
ReplaceNode(Node * old,Node ** new_node)447 void SimdScalarLowering::ReplaceNode(Node* old, Node** new_node) {
448   // if new_low == nullptr, then also new_high == nullptr.
449   DCHECK(new_node[0] != nullptr ||
450          (new_node[1] == nullptr && new_node[2] == nullptr &&
451           new_node[3] == nullptr));
452   for (int i = 0; i < kMaxLanes; ++i) {
453     replacements_[old->id()].node[i] = new_node[i];
454   }
455 }
456 
HasReplacement(size_t index,Node * node)457 bool SimdScalarLowering::HasReplacement(size_t index, Node* node) {
458   return replacements_[node->id()].node[index] != nullptr;
459 }
460 
ReplacementType(Node * node)461 SimdScalarLowering::SimdType SimdScalarLowering::ReplacementType(Node* node) {
462   return replacements_[node->id()].type;
463 }
464 
GetReplacements(Node * node)465 Node** SimdScalarLowering::GetReplacements(Node* node) {
466   Node** result = replacements_[node->id()].node;
467   DCHECK(result);
468   return result;
469 }
470 
GetReplacementsWithType(Node * node,SimdType type)471 Node** SimdScalarLowering::GetReplacementsWithType(Node* node, SimdType type) {
472   Node** replacements = GetReplacements(node);
473   if (ReplacementType(node) == type) {
474     return GetReplacements(node);
475   }
476   Node** result = zone()->NewArray<Node*>(kMaxLanes);
477   if (ReplacementType(node) == SimdType::kInt32 && type == SimdType::kFloat32) {
478     for (int i = 0; i < kMaxLanes; ++i) {
479       if (replacements[i] != nullptr) {
480         result[i] = graph()->NewNode(machine()->BitcastInt32ToFloat32(),
481                                      replacements[i]);
482       } else {
483         result[i] = nullptr;
484       }
485     }
486   } else {
487     for (int i = 0; i < kMaxLanes; ++i) {
488       if (replacements[i] != nullptr) {
489         result[i] = graph()->NewNode(machine()->BitcastFloat32ToInt32(),
490                                      replacements[i]);
491       } else {
492         result[i] = nullptr;
493       }
494     }
495   }
496   return result;
497 }
498 
PreparePhiReplacement(Node * phi)499 void SimdScalarLowering::PreparePhiReplacement(Node* phi) {
500   MachineRepresentation rep = PhiRepresentationOf(phi->op());
501   if (rep == MachineRepresentation::kSimd128) {
502     // We have to create the replacements for a phi node before we actually
503     // lower the phi to break potential cycles in the graph. The replacements of
504     // input nodes do not exist yet, so we use a placeholder node to pass the
505     // graph verifier.
506     int value_count = phi->op()->ValueInputCount();
507     SimdType type = ReplacementType(phi);
508     Node** inputs_rep[kMaxLanes];
509     for (int i = 0; i < kMaxLanes; ++i) {
510       inputs_rep[i] = zone()->NewArray<Node*>(value_count + 1);
511       inputs_rep[i][value_count] = NodeProperties::GetControlInput(phi, 0);
512     }
513     for (int i = 0; i < value_count; ++i) {
514       for (int j = 0; j < kMaxLanes; j++) {
515         inputs_rep[j][i] = placeholder_;
516       }
517     }
518     Node* rep_nodes[kMaxLanes];
519     for (int i = 0; i < kMaxLanes; ++i) {
520       if (type == SimdType::kInt32) {
521         rep_nodes[i] = graph()->NewNode(
522             common()->Phi(MachineRepresentation::kWord32, value_count),
523             value_count + 1, inputs_rep[i], false);
524       } else if (type == SimdType::kFloat32) {
525         rep_nodes[i] = graph()->NewNode(
526             common()->Phi(MachineRepresentation::kFloat32, value_count),
527             value_count + 1, inputs_rep[i], false);
528       } else {
529         UNREACHABLE();
530       }
531     }
532     ReplaceNode(phi, rep_nodes);
533   }
534 }
535 }  // namespace compiler
536 }  // namespace internal
537 }  // namespace v8
538