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/wasm/wasm-module.h"
13 
14 namespace v8 {
15 namespace internal {
16 namespace compiler {
17 
SimdScalarLowering(Graph * graph,MachineOperatorBuilder * machine,CommonOperatorBuilder * common,Zone * zone,Signature<MachineRepresentation> * signature)18 SimdScalarLowering::SimdScalarLowering(
19     Graph* graph, MachineOperatorBuilder* machine,
20     CommonOperatorBuilder* common, Zone* zone,
21     Signature<MachineRepresentation>* signature)
22     : zone_(zone),
23       graph_(graph),
24       machine_(machine),
25       common_(common),
26       state_(graph, 3),
27       stack_(zone),
28       replacements_(nullptr),
29       signature_(signature),
30       placeholder_(
31           graph->NewNode(common->Parameter(-2, "placeholder"), graph->start())),
32       parameter_count_after_lowering_(-1) {
33   DCHECK_NOT_NULL(graph);
34   DCHECK_NOT_NULL(graph->end());
35   replacements_ = zone->NewArray<Replacement>(graph->NodeCount());
36   memset(replacements_, 0, sizeof(Replacement) * graph->NodeCount());
37 }
38 
LowerGraph()39 void SimdScalarLowering::LowerGraph() {
40   stack_.push_back({graph()->end(), 0});
41   state_.Set(graph()->end(), State::kOnStack);
42   replacements_[graph()->end()->id()].type = SimdType::kInt32;
43 
44   while (!stack_.empty()) {
45     NodeState& top = stack_.back();
46     if (top.input_index == top.node->InputCount()) {
47       // All inputs of top have already been lowered, now lower top.
48       stack_.pop_back();
49       state_.Set(top.node, State::kVisited);
50       LowerNode(top.node);
51     } else {
52       // Push the next input onto the stack.
53       Node* input = top.node->InputAt(top.input_index++);
54       if (state_.Get(input) == State::kUnvisited) {
55         SetLoweredType(input, top.node);
56         if (input->opcode() == IrOpcode::kPhi) {
57           // To break cycles with phi nodes we push phis on a separate stack so
58           // that they are processed after all other nodes.
59           PreparePhiReplacement(input);
60           stack_.push_front({input, 0});
61         } else {
62           stack_.push_back({input, 0});
63         }
64         state_.Set(input, State::kOnStack);
65       }
66     }
67   }
68 }
69 
70 #define FOREACH_INT32X4_OPCODE(V) \
71   V(Int32x4Add)                   \
72   V(Int32x4ExtractLane)           \
73   V(CreateInt32x4)
74 
75 #define FOREACH_FLOAT32X4_OPCODE(V) \
76   V(Float32x4Add)                   \
77   V(Float32x4ExtractLane)           \
78   V(CreateFloat32x4)
79 
SetLoweredType(Node * node,Node * output)80 void SimdScalarLowering::SetLoweredType(Node* node, Node* output) {
81   switch (node->opcode()) {
82 #define CASE_STMT(name) case IrOpcode::k##name:
83     FOREACH_INT32X4_OPCODE(CASE_STMT)
84     case IrOpcode::kReturn:
85     case IrOpcode::kParameter:
86     case IrOpcode::kCall: {
87       replacements_[node->id()].type = SimdType::kInt32;
88       break;
89     }
90       FOREACH_FLOAT32X4_OPCODE(CASE_STMT) {
91         replacements_[node->id()].type = SimdType::kFloat32;
92         break;
93       }
94 #undef CASE_STMT
95     default:
96       replacements_[node->id()].type = replacements_[output->id()].type;
97   }
98 }
99 
GetParameterIndexAfterLowering(Signature<MachineRepresentation> * signature,int old_index)100 static int GetParameterIndexAfterLowering(
101     Signature<MachineRepresentation>* signature, int old_index) {
102   // In function calls, the simd128 types are passed as 4 Int32 types. The
103   // parameters are typecast to the types as needed for various operations.
104   int result = old_index;
105   for (int i = 0; i < old_index; i++) {
106     if (signature->GetParam(i) == MachineRepresentation::kSimd128) {
107       result += 3;
108     }
109   }
110   return result;
111 }
112 
GetParameterCountAfterLowering()113 int SimdScalarLowering::GetParameterCountAfterLowering() {
114   if (parameter_count_after_lowering_ == -1) {
115     // GetParameterIndexAfterLowering(parameter_count) returns the parameter
116     // count after lowering.
117     parameter_count_after_lowering_ = GetParameterIndexAfterLowering(
118         signature(), static_cast<int>(signature()->parameter_count()));
119   }
120   return parameter_count_after_lowering_;
121 }
122 
GetReturnCountAfterLowering(Signature<MachineRepresentation> * signature)123 static int GetReturnCountAfterLowering(
124     Signature<MachineRepresentation>* signature) {
125   int result = static_cast<int>(signature->return_count());
126   for (int i = 0; i < static_cast<int>(signature->return_count()); i++) {
127     if (signature->GetReturn(i) == MachineRepresentation::kSimd128) {
128       result += 3;
129     }
130   }
131   return result;
132 }
133 
LowerNode(Node * node)134 void SimdScalarLowering::LowerNode(Node* node) {
135   SimdType rep_type = ReplacementType(node);
136   switch (node->opcode()) {
137     case IrOpcode::kStart: {
138       int parameter_count = GetParameterCountAfterLowering();
139       // Only exchange the node if the parameter count actually changed.
140       if (parameter_count != static_cast<int>(signature()->parameter_count())) {
141         int delta =
142             parameter_count - static_cast<int>(signature()->parameter_count());
143         int new_output_count = node->op()->ValueOutputCount() + delta;
144         NodeProperties::ChangeOp(node, common()->Start(new_output_count));
145       }
146       break;
147     }
148     case IrOpcode::kParameter: {
149       DCHECK(node->InputCount() == 1);
150       // Only exchange the node if the parameter count actually changed. We do
151       // not even have to do the default lowering because the the start node,
152       // the only input of a parameter node, only changes if the parameter count
153       // changes.
154       if (GetParameterCountAfterLowering() !=
155           static_cast<int>(signature()->parameter_count())) {
156         int old_index = ParameterIndexOf(node->op());
157         int new_index = GetParameterIndexAfterLowering(signature(), old_index);
158         if (old_index == new_index) {
159           NodeProperties::ChangeOp(node, common()->Parameter(new_index));
160 
161           Node* new_node[kMaxLanes];
162           for (int i = 0; i < kMaxLanes; i++) {
163             new_node[i] = nullptr;
164           }
165           new_node[0] = node;
166           if (signature()->GetParam(old_index) ==
167               MachineRepresentation::kSimd128) {
168             for (int i = 1; i < kMaxLanes; i++) {
169               new_node[i] = graph()->NewNode(common()->Parameter(new_index + i),
170                                              graph()->start());
171             }
172           }
173           ReplaceNode(node, new_node);
174         }
175       }
176       break;
177     }
178     case IrOpcode::kReturn: {
179       DefaultLowering(node);
180       int new_return_count = GetReturnCountAfterLowering(signature());
181       if (static_cast<int>(signature()->return_count()) != new_return_count) {
182         NodeProperties::ChangeOp(node, common()->Return(new_return_count));
183       }
184       break;
185     }
186     case IrOpcode::kCall: {
187       // TODO(turbofan): Make WASM code const-correct wrt. CallDescriptor.
188       CallDescriptor* descriptor =
189           const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
190       if (DefaultLowering(node) ||
191           (descriptor->ReturnCount() == 1 &&
192            descriptor->GetReturnType(0) == MachineType::Simd128())) {
193         // We have to adjust the call descriptor.
194         const Operator* op =
195             common()->Call(wasm::ModuleEnv::GetI32WasmCallDescriptorForSimd(
196                 zone(), descriptor));
197         NodeProperties::ChangeOp(node, op);
198       }
199       if (descriptor->ReturnCount() == 1 &&
200           descriptor->GetReturnType(0) == MachineType::Simd128()) {
201         // We access the additional return values through projections.
202         Node* rep_node[kMaxLanes];
203         for (int i = 0; i < kMaxLanes; i++) {
204           rep_node[i] =
205               graph()->NewNode(common()->Projection(i), node, graph()->start());
206         }
207         ReplaceNode(node, rep_node);
208       }
209       break;
210     }
211     case IrOpcode::kPhi: {
212       MachineRepresentation rep = PhiRepresentationOf(node->op());
213       if (rep == MachineRepresentation::kSimd128) {
214         // The replacement nodes have already been created, we only have to
215         // replace placeholder nodes.
216         Node** rep_node = GetReplacements(node);
217         for (int i = 0; i < node->op()->ValueInputCount(); i++) {
218           Node** rep_input =
219               GetReplacementsWithType(node->InputAt(i), rep_type);
220           for (int j = 0; j < kMaxLanes; j++) {
221             rep_node[j]->ReplaceInput(i, rep_input[j]);
222           }
223         }
224       } else {
225         DefaultLowering(node);
226       }
227       break;
228     }
229 
230     case IrOpcode::kInt32x4Add: {
231       DCHECK(node->InputCount() == 2);
232       Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
233       Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
234       Node* rep_node[kMaxLanes];
235       for (int i = 0; i < kMaxLanes; i++) {
236         rep_node[i] =
237             graph()->NewNode(machine()->Int32Add(), rep_left[i], rep_right[i]);
238       }
239       ReplaceNode(node, rep_node);
240       break;
241     }
242 
243     case IrOpcode::kCreateInt32x4: {
244       Node* rep_node[kMaxLanes];
245       for (int i = 0; i < kMaxLanes; i++) {
246         DCHECK(!HasReplacement(1, node->InputAt(i)));
247         rep_node[i] = node->InputAt(i);
248       }
249       ReplaceNode(node, rep_node);
250       break;
251     }
252 
253     case IrOpcode::kInt32x4ExtractLane: {
254       Node* laneNode = node->InputAt(1);
255       DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant);
256       int32_t lane = OpParameter<int32_t>(laneNode);
257       Node* rep_node[kMaxLanes] = {
258           GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr,
259           nullptr, nullptr};
260       ReplaceNode(node, rep_node);
261       break;
262     }
263 
264     case IrOpcode::kFloat32x4Add: {
265       DCHECK(node->InputCount() == 2);
266       Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
267       Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
268       Node* rep_node[kMaxLanes];
269       for (int i = 0; i < kMaxLanes; i++) {
270         rep_node[i] = graph()->NewNode(machine()->Float32Add(), rep_left[i],
271                                        rep_right[i]);
272       }
273       ReplaceNode(node, rep_node);
274       break;
275     }
276 
277     case IrOpcode::kCreateFloat32x4: {
278       Node* rep_node[kMaxLanes];
279       for (int i = 0; i < kMaxLanes; i++) {
280         DCHECK(!HasReplacement(1, node->InputAt(i)));
281         rep_node[i] = node->InputAt(i);
282       }
283       ReplaceNode(node, rep_node);
284       break;
285     }
286 
287     case IrOpcode::kFloat32x4ExtractLane: {
288       Node* laneNode = node->InputAt(1);
289       DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant);
290       int32_t lane = OpParameter<int32_t>(laneNode);
291       Node* rep_node[kMaxLanes] = {
292           GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr,
293           nullptr, nullptr};
294       ReplaceNode(node, rep_node);
295       break;
296     }
297 
298     default: { DefaultLowering(node); }
299   }
300 }
301 
DefaultLowering(Node * node)302 bool SimdScalarLowering::DefaultLowering(Node* node) {
303   bool something_changed = false;
304   for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) {
305     Node* input = node->InputAt(i);
306     if (HasReplacement(0, input)) {
307       something_changed = true;
308       node->ReplaceInput(i, GetReplacements(input)[0]);
309     }
310     if (HasReplacement(1, input)) {
311       something_changed = true;
312       for (int j = 1; j < kMaxLanes; j++) {
313         node->InsertInput(zone(), i + j, GetReplacements(input)[j]);
314       }
315     }
316   }
317   return something_changed;
318 }
319 
ReplaceNode(Node * old,Node ** new_node)320 void SimdScalarLowering::ReplaceNode(Node* old, Node** new_node) {
321   // if new_low == nullptr, then also new_high == nullptr.
322   DCHECK(new_node[0] != nullptr ||
323          (new_node[1] == nullptr && new_node[2] == nullptr &&
324           new_node[3] == nullptr));
325   for (int i = 0; i < kMaxLanes; i++) {
326     replacements_[old->id()].node[i] = new_node[i];
327   }
328 }
329 
HasReplacement(size_t index,Node * node)330 bool SimdScalarLowering::HasReplacement(size_t index, Node* node) {
331   return replacements_[node->id()].node[index] != nullptr;
332 }
333 
ReplacementType(Node * node)334 SimdScalarLowering::SimdType SimdScalarLowering::ReplacementType(Node* node) {
335   return replacements_[node->id()].type;
336 }
337 
GetReplacements(Node * node)338 Node** SimdScalarLowering::GetReplacements(Node* node) {
339   Node** result = replacements_[node->id()].node;
340   DCHECK(result);
341   return result;
342 }
343 
GetReplacementsWithType(Node * node,SimdType type)344 Node** SimdScalarLowering::GetReplacementsWithType(Node* node, SimdType type) {
345   Node** replacements = GetReplacements(node);
346   if (ReplacementType(node) == type) {
347     return GetReplacements(node);
348   }
349   Node** result = zone()->NewArray<Node*>(kMaxLanes);
350   if (ReplacementType(node) == SimdType::kInt32 && type == SimdType::kFloat32) {
351     for (int i = 0; i < kMaxLanes; i++) {
352       if (replacements[i] != nullptr) {
353         result[i] = graph()->NewNode(machine()->BitcastInt32ToFloat32(),
354                                      replacements[i]);
355       } else {
356         result[i] = nullptr;
357       }
358     }
359   } else {
360     for (int i = 0; i < kMaxLanes; i++) {
361       if (replacements[i] != nullptr) {
362         result[i] = graph()->NewNode(machine()->BitcastFloat32ToInt32(),
363                                      replacements[i]);
364       } else {
365         result[i] = nullptr;
366       }
367     }
368   }
369   return result;
370 }
371 
PreparePhiReplacement(Node * phi)372 void SimdScalarLowering::PreparePhiReplacement(Node* phi) {
373   MachineRepresentation rep = PhiRepresentationOf(phi->op());
374   if (rep == MachineRepresentation::kSimd128) {
375     // We have to create the replacements for a phi node before we actually
376     // lower the phi to break potential cycles in the graph. The replacements of
377     // input nodes do not exist yet, so we use a placeholder node to pass the
378     // graph verifier.
379     int value_count = phi->op()->ValueInputCount();
380     SimdType type = ReplacementType(phi);
381     Node** inputs_rep[kMaxLanes];
382     for (int i = 0; i < kMaxLanes; i++) {
383       inputs_rep[i] = zone()->NewArray<Node*>(value_count + 1);
384       inputs_rep[i][value_count] = NodeProperties::GetControlInput(phi, 0);
385     }
386     for (int i = 0; i < value_count; i++) {
387       for (int j = 0; j < kMaxLanes; j++) {
388         inputs_rep[j][i] = placeholder_;
389       }
390     }
391     Node* rep_nodes[kMaxLanes];
392     for (int i = 0; i < kMaxLanes; i++) {
393       if (type == SimdType::kInt32) {
394         rep_nodes[i] = graph()->NewNode(
395             common()->Phi(MachineRepresentation::kWord32, value_count),
396             value_count + 1, inputs_rep[i], false);
397       } else if (type == SimdType::kFloat32) {
398         rep_nodes[i] = graph()->NewNode(
399             common()->Phi(MachineRepresentation::kFloat32, value_count),
400             value_count + 1, inputs_rep[i], false);
401       } else {
402         UNREACHABLE();
403       }
404     }
405     ReplaceNode(phi, rep_nodes);
406   }
407 }
408 }  // namespace compiler
409 }  // namespace internal
410 }  // namespace v8
411