OLD | NEW |
(Empty) | |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 != 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() != signature()->parameter_count()) { |
| 155 int old_index = ParameterIndexOf(node->op()); |
| 156 int new_index = GetParameterIndexAfterLowering(signature(), old_index); |
| 157 if (old_index == new_index) { |
| 158 NodeProperties::ChangeOp(node, common()->Parameter(new_index)); |
| 159 |
| 160 Node* new_node[kMaxLanes]; |
| 161 for (int i = 0; i < kMaxLanes; i++) { |
| 162 new_node[i] = nullptr; |
| 163 } |
| 164 new_node[0] = node; |
| 165 if (signature()->GetParam(old_index) == |
| 166 MachineRepresentation::kSimd128) { |
| 167 for (int i = 1; i < kMaxLanes; i++) { |
| 168 new_node[i] = graph()->NewNode(common()->Parameter(new_index + i), |
| 169 graph()->start()); |
| 170 } |
| 171 } |
| 172 ReplaceNode(node, new_node); |
| 173 } |
| 174 } |
| 175 break; |
| 176 } |
| 177 case IrOpcode::kReturn: { |
| 178 DefaultLowering(node); |
| 179 int new_return_count = GetReturnCountAfterLowering(signature()); |
| 180 if (signature()->return_count() != new_return_count) { |
| 181 NodeProperties::ChangeOp(node, common()->Return(new_return_count)); |
| 182 } |
| 183 break; |
| 184 } |
| 185 case IrOpcode::kCall: { |
| 186 // TODO(turbofan): Make WASM code const-correct wrt. CallDescriptor. |
| 187 CallDescriptor* descriptor = |
| 188 const_cast<CallDescriptor*>(CallDescriptorOf(node->op())); |
| 189 if (DefaultLowering(node) || |
| 190 (descriptor->ReturnCount() == 1 && |
| 191 descriptor->GetReturnType(0) == MachineType::Simd128())) { |
| 192 // We have to adjust the call descriptor. |
| 193 const Operator* op = |
| 194 common()->Call(wasm::ModuleEnv::GetI32WasmCallDescriptorForSimd( |
| 195 zone(), descriptor)); |
| 196 NodeProperties::ChangeOp(node, op); |
| 197 } |
| 198 if (descriptor->ReturnCount() == 1 && |
| 199 descriptor->GetReturnType(0) == MachineType::Simd128()) { |
| 200 // We access the additional return values through projections. |
| 201 Node* rep_node[kMaxLanes]; |
| 202 for (int i = 0; i < kMaxLanes; i++) { |
| 203 rep_node[i] = |
| 204 graph()->NewNode(common()->Projection(i), node, graph()->start()); |
| 205 } |
| 206 ReplaceNode(node, rep_node); |
| 207 } |
| 208 break; |
| 209 } |
| 210 case IrOpcode::kPhi: { |
| 211 MachineRepresentation rep = PhiRepresentationOf(node->op()); |
| 212 if (rep == MachineRepresentation::kSimd128) { |
| 213 // The replacement nodes have already been created, we only have to |
| 214 // replace placeholder nodes. |
| 215 Node** rep_node = GetReplacements(node); |
| 216 for (int i = 0; i < node->op()->ValueInputCount(); i++) { |
| 217 Node** rep_input = |
| 218 GetReplacementsWithType(node->InputAt(i), rep_type); |
| 219 for (int j = 0; j < kMaxLanes; j++) { |
| 220 rep_node[j]->ReplaceInput(i, rep_input[j]); |
| 221 } |
| 222 } |
| 223 } else { |
| 224 DefaultLowering(node); |
| 225 } |
| 226 break; |
| 227 } |
| 228 |
| 229 case IrOpcode::kInt32x4Add: { |
| 230 DCHECK(node->InputCount() == 2); |
| 231 Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| 232 Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type); |
| 233 Node* rep_node[kMaxLanes]; |
| 234 for (int i = 0; i < kMaxLanes; i++) { |
| 235 rep_node[i] = |
| 236 graph()->NewNode(machine()->Int32Add(), rep_left[i], rep_right[i]); |
| 237 } |
| 238 ReplaceNode(node, rep_node); |
| 239 break; |
| 240 } |
| 241 |
| 242 case IrOpcode::kCreateInt32x4: { |
| 243 Node* rep_node[kMaxLanes]; |
| 244 for (int i = 0; i < kMaxLanes; i++) { |
| 245 DCHECK(!HasReplacement(1, node->InputAt(i))); |
| 246 rep_node[i] = node->InputAt(i); |
| 247 } |
| 248 ReplaceNode(node, rep_node); |
| 249 break; |
| 250 } |
| 251 |
| 252 case IrOpcode::kInt32x4ExtractLane: { |
| 253 Node* laneNode = node->InputAt(1); |
| 254 DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant); |
| 255 int32_t lane = OpParameter<int32_t>(laneNode); |
| 256 Node* rep_node[kMaxLanes] = { |
| 257 GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr, |
| 258 nullptr, nullptr}; |
| 259 ReplaceNode(node, rep_node); |
| 260 break; |
| 261 } |
| 262 |
| 263 case IrOpcode::kFloat32x4Add: { |
| 264 DCHECK(node->InputCount() == 2); |
| 265 Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| 266 Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type); |
| 267 Node* rep_node[kMaxLanes]; |
| 268 for (int i = 0; i < kMaxLanes; i++) { |
| 269 rep_node[i] = graph()->NewNode(machine()->Float32Add(), rep_left[i], |
| 270 rep_right[i]); |
| 271 } |
| 272 ReplaceNode(node, rep_node); |
| 273 break; |
| 274 } |
| 275 |
| 276 case IrOpcode::kCreateFloat32x4: { |
| 277 Node* rep_node[kMaxLanes]; |
| 278 for (int i = 0; i < kMaxLanes; i++) { |
| 279 DCHECK(!HasReplacement(1, node->InputAt(i))); |
| 280 rep_node[i] = node->InputAt(i); |
| 281 } |
| 282 ReplaceNode(node, rep_node); |
| 283 break; |
| 284 } |
| 285 |
| 286 case IrOpcode::kFloat32x4ExtractLane: { |
| 287 Node* laneNode = node->InputAt(1); |
| 288 DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant); |
| 289 int32_t lane = OpParameter<int32_t>(laneNode); |
| 290 Node* rep_node[kMaxLanes] = { |
| 291 GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr, |
| 292 nullptr, nullptr}; |
| 293 ReplaceNode(node, rep_node); |
| 294 break; |
| 295 } |
| 296 |
| 297 default: { DefaultLowering(node); } |
| 298 } |
| 299 } |
| 300 |
| 301 bool SimdScalarLowering::DefaultLowering(Node* node) { |
| 302 bool something_changed = false; |
| 303 for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) { |
| 304 Node* input = node->InputAt(i); |
| 305 if (HasReplacement(0, input)) { |
| 306 something_changed = true; |
| 307 node->ReplaceInput(i, GetReplacements(input)[0]); |
| 308 } |
| 309 if (HasReplacement(1, input)) { |
| 310 something_changed = true; |
| 311 for (int j = 1; j < kMaxLanes; j++) { |
| 312 node->InsertInput(zone(), i + j, GetReplacements(input)[j]); |
| 313 } |
| 314 } |
| 315 } |
| 316 return something_changed; |
| 317 } |
| 318 |
| 319 void SimdScalarLowering::ReplaceNode(Node* old, Node** new_node) { |
| 320 // if new_low == nullptr, then also new_high == nullptr. |
| 321 DCHECK(new_node[0] != nullptr || |
| 322 (new_node[1] == nullptr && new_node[2] == nullptr && |
| 323 new_node[3] == nullptr)); |
| 324 for (int i = 0; i < kMaxLanes; i++) { |
| 325 replacements_[old->id()].node[i] = new_node[i]; |
| 326 } |
| 327 } |
| 328 |
| 329 bool SimdScalarLowering::HasReplacement(size_t index, Node* node) { |
| 330 return replacements_[node->id()].node[index] != nullptr; |
| 331 } |
| 332 |
| 333 SimdScalarLowering::SimdType SimdScalarLowering::ReplacementType(Node* node) { |
| 334 return replacements_[node->id()].type; |
| 335 } |
| 336 |
| 337 Node** SimdScalarLowering::GetReplacements(Node* node) { |
| 338 Node** result = replacements_[node->id()].node; |
| 339 DCHECK(result); |
| 340 return result; |
| 341 } |
| 342 |
| 343 Node** SimdScalarLowering::GetReplacementsWithType(Node* node, SimdType type) { |
| 344 Node** replacements = GetReplacements(node); |
| 345 if (ReplacementType(node) == type) { |
| 346 return GetReplacements(node); |
| 347 } |
| 348 Node** result = zone()->NewArray<Node*>(kMaxLanes); |
| 349 if (ReplacementType(node) == SimdType::kInt32 && type == SimdType::kFloat32) { |
| 350 for (int i = 0; i < kMaxLanes; i++) { |
| 351 if (replacements[i] != nullptr) { |
| 352 result[i] = graph()->NewNode(machine()->BitcastInt32ToFloat32(), |
| 353 replacements[i]); |
| 354 } else { |
| 355 result[i] = nullptr; |
| 356 } |
| 357 } |
| 358 } else { |
| 359 for (int i = 0; i < kMaxLanes; i++) { |
| 360 if (replacements[i] != nullptr) { |
| 361 result[i] = graph()->NewNode(machine()->BitcastFloat32ToInt32(), |
| 362 replacements[i]); |
| 363 } else { |
| 364 result[i] = nullptr; |
| 365 } |
| 366 } |
| 367 } |
| 368 return result; |
| 369 } |
| 370 |
| 371 void SimdScalarLowering::PreparePhiReplacement(Node* phi) { |
| 372 MachineRepresentation rep = PhiRepresentationOf(phi->op()); |
| 373 if (rep == MachineRepresentation::kSimd128) { |
| 374 // We have to create the replacements for a phi node before we actually |
| 375 // lower the phi to break potential cycles in the graph. The replacements of |
| 376 // input nodes do not exist yet, so we use a placeholder node to pass the |
| 377 // graph verifier. |
| 378 int value_count = phi->op()->ValueInputCount(); |
| 379 SimdType type = ReplacementType(phi); |
| 380 Node** inputs_rep[kMaxLanes]; |
| 381 for (int i = 0; i < kMaxLanes; i++) { |
| 382 inputs_rep[i] = zone()->NewArray<Node*>(value_count + 1); |
| 383 inputs_rep[i][value_count] = NodeProperties::GetControlInput(phi, 0); |
| 384 } |
| 385 for (int i = 0; i < value_count; i++) { |
| 386 for (int j = 0; j < kMaxLanes; j++) { |
| 387 inputs_rep[j][i] = placeholder_; |
| 388 } |
| 389 } |
| 390 Node* rep_nodes[kMaxLanes]; |
| 391 for (int i = 0; i < kMaxLanes; i++) { |
| 392 if (type == SimdType::kInt32) { |
| 393 rep_nodes[i] = graph()->NewNode( |
| 394 common()->Phi(MachineRepresentation::kWord32, value_count), |
| 395 value_count + 1, inputs_rep[i], false); |
| 396 } else if (type == SimdType::kFloat32) { |
| 397 rep_nodes[i] = graph()->NewNode( |
| 398 common()->Phi(MachineRepresentation::kFloat32, value_count), |
| 399 value_count + 1, inputs_rep[i], false); |
| 400 } else { |
| 401 UNREACHABLE(); |
| 402 } |
| 403 } |
| 404 ReplaceNode(phi, rep_nodes); |
| 405 } |
| 406 } |
| 407 } // namespace compiler |
| 408 } // namespace internal |
| 409 } // namespace v8 |
OLD | NEW |