| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/compiler/simd-scalar-lowering.h" |
| |
| #include "src/codegen/machine-type.h" |
| #include "src/common/globals.h" |
| #include "src/compiler/diamond.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/machine-operator.h" |
| #include "src/compiler/node-matchers.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/compiler/node.h" |
| #include "src/compiler/wasm-compiler.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| namespace { |
| static const int kNumLanes64 = 2; |
| static const int kNumLanes32 = 4; |
| static const int kNumLanes16 = 8; |
| static const int kNumLanes8 = 16; |
| static const int32_t kMask16 = 0xFFFF; |
| static const int32_t kMask8 = 0xFF; |
| static const int32_t kShift16 = 16; |
| static const int32_t kShift8 = 24; |
| static const int32_t kShiftMask8 = 0x7; |
| static const int32_t kShiftMask16 = 0xF; |
| static const int32_t kShiftMask32 = 0x1F; |
| static const int32_t kShiftMask64 = 0x3F; |
| |
| // Shift values are taken modulo lane size. This helper calculates the mask |
| // required for different shift opcodes. |
| int GetMaskForShift(Node* node) { |
| switch (node->opcode()) { |
| case IrOpcode::kI8x16Shl: |
| case IrOpcode::kI8x16ShrS: |
| case IrOpcode::kI8x16ShrU: |
| return kShiftMask8; |
| case IrOpcode::kI16x8Shl: |
| case IrOpcode::kI16x8ShrS: |
| case IrOpcode::kI16x8ShrU: |
| return kShiftMask16; |
| case IrOpcode::kI32x4Shl: |
| case IrOpcode::kI32x4ShrS: |
| case IrOpcode::kI32x4ShrU: |
| return kShiftMask32; |
| case IrOpcode::kI64x2Shl: |
| case IrOpcode::kI64x2ShrS: |
| case IrOpcode::kI64x2ShrU: |
| return kShiftMask64; |
| default: |
| UNIMPLEMENTED(); |
| } |
| } |
| } // anonymous namespace |
| |
| SimdScalarLowering::SimdScalarLowering( |
| MachineGraph* mcgraph, Signature<MachineRepresentation>* signature) |
| : mcgraph_(mcgraph), |
| state_(mcgraph->graph(), 3), |
| stack_(mcgraph_->zone()), |
| replacements_(nullptr), |
| signature_(signature), |
| placeholder_(graph()->NewNode(common()->Parameter(-2, "placeholder"), |
| graph()->start())), |
| parameter_count_after_lowering_(-1) { |
| DCHECK_NOT_NULL(graph()); |
| DCHECK_NOT_NULL(graph()->end()); |
| replacements_ = zone()->NewArray<Replacement>(graph()->NodeCount()); |
| memset(static_cast<void*>(replacements_), 0, |
| sizeof(Replacement) * graph()->NodeCount()); |
| } |
| |
| void SimdScalarLowering::LowerGraph() { |
| stack_.push_back({graph()->end(), 0}); |
| state_.Set(graph()->end(), State::kOnStack); |
| replacements_[graph()->end()->id()].type = SimdType::kInt32x4; |
| |
| while (!stack_.empty()) { |
| NodeState& top = stack_.back(); |
| if (top.input_index == top.node->InputCount()) { |
| // All inputs of top have already been lowered, now lower top. |
| stack_.pop_back(); |
| state_.Set(top.node, State::kVisited); |
| LowerNode(top.node); |
| } else { |
| // Push the next input onto the stack. |
| Node* input = top.node->InputAt(top.input_index++); |
| if (state_.Get(input) == State::kUnvisited) { |
| SetLoweredType(input, top.node); |
| if (input->opcode() == IrOpcode::kPhi) { |
| // To break cycles with phi nodes we push phis on a separate stack so |
| // that they are processed after all other nodes. |
| PreparePhiReplacement(input); |
| stack_.push_front({input, 0}); |
| } else if (input->opcode() == IrOpcode::kEffectPhi || |
| input->opcode() == IrOpcode::kLoop) { |
| stack_.push_front({input, 0}); |
| } else { |
| stack_.push_back({input, 0}); |
| } |
| state_.Set(input, State::kOnStack); |
| } |
| } |
| } |
| } |
| |
| #define FOREACH_INT64X2_OPCODE(V) \ |
| V(I64x2Splat) \ |
| V(I64x2ExtractLane) \ |
| V(I64x2ReplaceLane) \ |
| V(I64x2Neg) \ |
| V(I64x2Shl) \ |
| V(I64x2ShrS) \ |
| V(I64x2ShrU) \ |
| V(I64x2Add) \ |
| V(I64x2Sub) \ |
| V(I64x2Mul) |
| |
| #define FOREACH_INT32X4_OPCODE(V) \ |
| V(I32x4Splat) \ |
| V(I32x4ExtractLane) \ |
| V(I32x4ReplaceLane) \ |
| V(I32x4SConvertF32x4) \ |
| V(I32x4UConvertF32x4) \ |
| V(I32x4SConvertI16x8Low) \ |
| V(I32x4SConvertI16x8High) \ |
| V(I32x4Neg) \ |
| V(I32x4Shl) \ |
| V(I32x4ShrS) \ |
| V(I32x4Add) \ |
| V(I32x4AddHoriz) \ |
| V(I32x4Sub) \ |
| V(I32x4Mul) \ |
| V(I32x4MinS) \ |
| V(I32x4MaxS) \ |
| V(I32x4ShrU) \ |
| V(I32x4MinU) \ |
| V(I32x4MaxU) \ |
| V(I32x4DotI16x8S) \ |
| V(I32x4Eq) \ |
| V(I32x4Ne) \ |
| V(I32x4LtS) \ |
| V(I32x4LtU) \ |
| V(I32x4GtS) \ |
| V(I32x4GtU) \ |
| V(I32x4LeS) \ |
| V(I32x4LeU) \ |
| V(I32x4GeS) \ |
| V(I32x4GeU) \ |
| V(I32x4UConvertI16x8Low) \ |
| V(I32x4UConvertI16x8High) \ |
| V(I32x4Abs) \ |
| V(S128And) \ |
| V(S128Or) \ |
| V(S128Xor) \ |
| V(S128Not) \ |
| V(S128AndNot) \ |
| V(S128Select) \ |
| V(V32x4AnyTrue) \ |
| V(V32x4AllTrue) \ |
| V(V16x8AnyTrue) \ |
| V(V16x8AllTrue) \ |
| V(V8x16AnyTrue) \ |
| V(V8x16AllTrue) \ |
| V(I32x4BitMask) |
| |
| #define FOREACH_FLOAT64X2_OPCODE(V) \ |
| V(F64x2Splat) \ |
| V(F64x2ExtractLane) \ |
| V(F64x2ReplaceLane) \ |
| V(F64x2Abs) \ |
| V(F64x2Neg) \ |
| V(F64x2Sqrt) \ |
| V(F64x2Add) \ |
| V(F64x2Sub) \ |
| V(F64x2Mul) \ |
| V(F64x2Div) \ |
| V(F64x2Min) \ |
| V(F64x2Max) \ |
| V(F64x2Pmin) \ |
| V(F64x2Pmax) \ |
| V(F64x2Ceil) \ |
| V(F64x2Floor) \ |
| V(F64x2Trunc) \ |
| V(F64x2NearestInt) |
| |
| #define FOREACH_FLOAT32X4_OPCODE(V) \ |
| V(F32x4Splat) \ |
| V(F32x4ExtractLane) \ |
| V(F32x4ReplaceLane) \ |
| V(F32x4SConvertI32x4) \ |
| V(F32x4UConvertI32x4) \ |
| V(F32x4Abs) \ |
| V(F32x4Neg) \ |
| V(F32x4Sqrt) \ |
| V(F32x4RecipApprox) \ |
| V(F32x4RecipSqrtApprox) \ |
| V(F32x4Add) \ |
| V(F32x4AddHoriz) \ |
| V(F32x4Sub) \ |
| V(F32x4Mul) \ |
| V(F32x4Div) \ |
| V(F32x4Min) \ |
| V(F32x4Max) \ |
| V(F32x4Pmin) \ |
| V(F32x4Pmax) \ |
| V(F32x4Ceil) \ |
| V(F32x4Floor) \ |
| V(F32x4Trunc) \ |
| V(F32x4NearestInt) |
| |
| #define FOREACH_FLOAT64x2_TO_INT64x2OPCODE(V) \ |
| V(F64x2Eq) \ |
| V(F64x2Ne) \ |
| V(F64x2Lt) \ |
| V(F64x2Le) |
| |
| #define FOREACH_FLOAT32X4_TO_INT32X4OPCODE(V) \ |
| V(F32x4Eq) \ |
| V(F32x4Ne) \ |
| V(F32x4Lt) \ |
| V(F32x4Le) \ |
| V(F32x4Gt) \ |
| V(F32x4Ge) |
| |
| #define FOREACH_INT16X8_OPCODE(V) \ |
| V(I16x8Splat) \ |
| V(I16x8ExtractLaneU) \ |
| V(I16x8ExtractLaneS) \ |
| V(I16x8ReplaceLane) \ |
| V(I16x8SConvertI8x16Low) \ |
| V(I16x8SConvertI8x16High) \ |
| V(I16x8Neg) \ |
| V(I16x8Shl) \ |
| V(I16x8ShrS) \ |
| V(I16x8SConvertI32x4) \ |
| V(I16x8Add) \ |
| V(I16x8AddSatS) \ |
| V(I16x8AddHoriz) \ |
| V(I16x8Sub) \ |
| V(I16x8SubSatS) \ |
| V(I16x8Mul) \ |
| V(I16x8MinS) \ |
| V(I16x8MaxS) \ |
| V(I16x8UConvertI8x16Low) \ |
| V(I16x8UConvertI8x16High) \ |
| V(I16x8ShrU) \ |
| V(I16x8UConvertI32x4) \ |
| V(I16x8AddSatU) \ |
| V(I16x8SubSatU) \ |
| V(I16x8MinU) \ |
| V(I16x8MaxU) \ |
| V(I16x8Eq) \ |
| V(I16x8Ne) \ |
| V(I16x8LtS) \ |
| V(I16x8LtU) \ |
| V(I16x8GtS) \ |
| V(I16x8GtU) \ |
| V(I16x8LeS) \ |
| V(I16x8LeU) \ |
| V(I16x8GeS) \ |
| V(I16x8GeU) \ |
| V(I16x8RoundingAverageU) \ |
| V(I16x8Abs) \ |
| V(I16x8BitMask) |
| |
| #define FOREACH_INT8X16_OPCODE(V) \ |
| V(I8x16Splat) \ |
| V(I8x16ExtractLaneU) \ |
| V(I8x16ExtractLaneS) \ |
| V(I8x16ReplaceLane) \ |
| V(I8x16SConvertI16x8) \ |
| V(I8x16Neg) \ |
| V(I8x16Shl) \ |
| V(I8x16ShrS) \ |
| V(I8x16Add) \ |
| V(I8x16AddSatS) \ |
| V(I8x16Sub) \ |
| V(I8x16SubSatS) \ |
| V(I8x16Mul) \ |
| V(I8x16MinS) \ |
| V(I8x16MaxS) \ |
| V(I8x16ShrU) \ |
| V(I8x16UConvertI16x8) \ |
| V(I8x16AddSatU) \ |
| V(I8x16SubSatU) \ |
| V(I8x16MinU) \ |
| V(I8x16MaxU) \ |
| V(I8x16Eq) \ |
| V(I8x16Ne) \ |
| V(I8x16LtS) \ |
| V(I8x16LtU) \ |
| V(I8x16GtS) \ |
| V(I8x16GtU) \ |
| V(I8x16LeS) \ |
| V(I8x16LeU) \ |
| V(I8x16GeS) \ |
| V(I8x16GeU) \ |
| V(I8x16Swizzle) \ |
| V(I8x16Shuffle) \ |
| V(I8x16RoundingAverageU) \ |
| V(I8x16Abs) \ |
| V(I8x16BitMask) |
| |
| MachineType SimdScalarLowering::MachineTypeFrom(SimdType simdType) { |
| switch (simdType) { |
| case SimdType::kFloat64x2: |
| return MachineType::Float64(); |
| case SimdType::kFloat32x4: |
| return MachineType::Float32(); |
| case SimdType::kInt64x2: |
| return MachineType::Int64(); |
| case SimdType::kInt32x4: |
| return MachineType::Int32(); |
| case SimdType::kInt16x8: |
| return MachineType::Int16(); |
| case SimdType::kInt8x16: |
| return MachineType::Int8(); |
| } |
| return MachineType::None(); |
| } |
| |
| void SimdScalarLowering::SetLoweredType(Node* node, Node* output) { |
| switch (node->opcode()) { |
| #define CASE_STMT(name) case IrOpcode::k##name: |
| FOREACH_FLOAT64X2_OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kFloat64x2; |
| break; |
| } |
| FOREACH_INT64X2_OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kInt64x2; |
| break; |
| } |
| FOREACH_INT32X4_OPCODE(CASE_STMT) |
| case IrOpcode::kReturn: |
| case IrOpcode::kParameter: |
| case IrOpcode::kPhi: |
| case IrOpcode::kCall: { |
| replacements_[node->id()].type = SimdType::kInt32x4; |
| break; |
| } |
| FOREACH_FLOAT32X4_OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kFloat32x4; |
| break; |
| } |
| FOREACH_FLOAT32X4_TO_INT32X4OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kInt32x4; |
| break; |
| } |
| FOREACH_FLOAT64x2_TO_INT64x2OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kInt64x2; |
| break; |
| } |
| FOREACH_INT16X8_OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kInt16x8; |
| break; |
| } |
| FOREACH_INT8X16_OPCODE(CASE_STMT) { |
| replacements_[node->id()].type = SimdType::kInt8x16; |
| break; |
| } |
| case IrOpcode::kLoadTransform: { |
| LoadTransformParameters params = LoadTransformParametersOf(node->op()); |
| switch (params.transformation) { |
| case LoadTransformation::kS128Load8Splat: |
| replacements_[node->id()].type = SimdType::kInt8x16; |
| break; |
| case LoadTransformation::kS128Load16Splat: |
| case LoadTransformation::kS128Load8x8S: |
| case LoadTransformation::kS128Load8x8U: |
| replacements_[node->id()].type = SimdType::kInt16x8; |
| break; |
| case LoadTransformation::kS128Load32Splat: |
| case LoadTransformation::kS128Load16x4S: |
| case LoadTransformation::kS128Load16x4U: |
| case LoadTransformation::kS128Load32Zero: |
| replacements_[node->id()].type = SimdType::kInt32x4; |
| break; |
| case LoadTransformation::kS128Load64Splat: |
| case LoadTransformation::kS128Load32x2S: |
| case LoadTransformation::kS128Load32x2U: |
| case LoadTransformation::kS128Load64Zero: |
| replacements_[node->id()].type = SimdType::kInt64x2; |
| break; |
| default: |
| UNIMPLEMENTED(); |
| } |
| break; |
| } |
| default: { |
| switch (output->opcode()) { |
| case IrOpcode::kF32x4SConvertI32x4: |
| case IrOpcode::kF32x4UConvertI32x4: |
| case IrOpcode::kI16x8SConvertI32x4: |
| case IrOpcode::kI16x8UConvertI32x4: { |
| replacements_[node->id()].type = SimdType::kInt32x4; |
| break; |
| } |
| case IrOpcode::kI8x16SConvertI16x8: |
| case IrOpcode::kI8x16UConvertI16x8: |
| case IrOpcode::kI32x4SConvertI16x8Low: |
| case IrOpcode::kI32x4SConvertI16x8High: |
| case IrOpcode::kI32x4UConvertI16x8Low: |
| case IrOpcode::kI32x4UConvertI16x8High: { |
| replacements_[node->id()].type = SimdType::kInt16x8; |
| break; |
| } |
| case IrOpcode::kI16x8SConvertI8x16Low: |
| case IrOpcode::kI16x8SConvertI8x16High: |
| case IrOpcode::kI16x8UConvertI8x16Low: |
| case IrOpcode::kI16x8UConvertI8x16High: { |
| replacements_[node->id()].type = SimdType::kInt8x16; |
| break; |
| } |
| FOREACH_FLOAT32X4_TO_INT32X4OPCODE(CASE_STMT) |
| case IrOpcode::kI32x4SConvertF32x4: |
| case IrOpcode::kI32x4UConvertF32x4: { |
| replacements_[node->id()].type = SimdType::kFloat32x4; |
| break; |
| } |
| case IrOpcode::kS128Select: { |
| replacements_[node->id()].type = SimdType::kInt32x4; |
| break; |
| } |
| default: { |
| replacements_[node->id()].type = replacements_[output->id()].type; |
| } |
| } |
| } |
| #undef CASE_STMT |
| } |
| } |
| |
| static int GetParameterIndexAfterLoweringSimd128( |
| Signature<MachineRepresentation>* signature, int old_index) { |
| // In function calls, the simd128 types are passed as 4 Int32 types. The |
| // parameters are typecast to the types as needed for various operations. |
| int result = old_index; |
| for (int i = 0; i < old_index; ++i) { |
| if (signature->GetParam(i) == MachineRepresentation::kSimd128) { |
| result += 3; |
| } |
| } |
| return result; |
| } |
| |
| int SimdScalarLowering::GetParameterCountAfterLowering() { |
| if (parameter_count_after_lowering_ == -1) { |
| // GetParameterIndexAfterLoweringSimd128(parameter_count) returns the |
| // parameter count after lowering. |
| parameter_count_after_lowering_ = GetParameterIndexAfterLoweringSimd128( |
| signature(), static_cast<int>(signature()->parameter_count())); |
| } |
| return parameter_count_after_lowering_; |
| } |
| |
| static int GetReturnCountAfterLoweringSimd128( |
| Signature<MachineRepresentation>* signature) { |
| int result = static_cast<int>(signature->return_count()); |
| for (int i = 0; i < static_cast<int>(signature->return_count()); ++i) { |
| if (signature->GetReturn(i) == MachineRepresentation::kSimd128) { |
| result += 3; |
| } |
| } |
| return result; |
| } |
| |
| int GetReturnIndexAfterLowering(const CallDescriptor* call_descriptor, |
| int old_index) { |
| int result = old_index; |
| for (int i = 0; i < old_index; ++i) { |
| if (call_descriptor->GetReturnType(i).representation() == |
| MachineRepresentation::kSimd128) { |
| result += kNumLanes32 - 1; |
| } |
| } |
| return result; |
| } |
| |
| static int GetReturnCountAfterLoweringSimd128( |
| const CallDescriptor* call_descriptor) { |
| return GetReturnIndexAfterLowering( |
| call_descriptor, static_cast<int>(call_descriptor->ReturnCount())); |
| } |
| |
| int SimdScalarLowering::NumLanes(SimdType type) { |
| int num_lanes = 0; |
| if (type == SimdType::kFloat64x2 || type == SimdType::kInt64x2) { |
| num_lanes = kNumLanes64; |
| } else if (type == SimdType::kFloat32x4 || type == SimdType::kInt32x4) { |
| num_lanes = kNumLanes32; |
| } else if (type == SimdType::kInt16x8) { |
| num_lanes = kNumLanes16; |
| } else if (type == SimdType::kInt8x16) { |
| num_lanes = kNumLanes8; |
| } else { |
| UNREACHABLE(); |
| } |
| return num_lanes; |
| } |
| |
| constexpr int SimdScalarLowering::kLaneOffsets[]; |
| |
| void SimdScalarLowering::GetIndexNodes(Node* index, Node** new_indices, |
| SimdType type) { |
| int num_lanes = NumLanes(type); |
| int lane_width = kSimd128Size / num_lanes; |
| int laneIndex = kLaneOffsets[0] / lane_width; |
| new_indices[laneIndex] = index; |
| for (int i = 1; i < num_lanes; ++i) { |
| laneIndex = kLaneOffsets[i * lane_width] / lane_width; |
| new_indices[laneIndex] = graph()->NewNode( |
| machine()->Int32Add(), index, |
| graph()->NewNode( |
| common()->Int32Constant(static_cast<int>(i) * lane_width))); |
| } |
| } |
| |
| void SimdScalarLowering::LowerLoadOp(Node* node, SimdType type) { |
| MachineRepresentation rep = LoadRepresentationOf(node->op()).representation(); |
| const Operator* load_op; |
| switch (node->opcode()) { |
| case IrOpcode::kLoad: |
| load_op = machine()->Load(MachineTypeFrom(type)); |
| break; |
| case IrOpcode::kUnalignedLoad: |
| load_op = machine()->UnalignedLoad(MachineTypeFrom(type)); |
| break; |
| case IrOpcode::kProtectedLoad: |
| load_op = machine()->ProtectedLoad(MachineTypeFrom(type)); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| if (rep == MachineRepresentation::kSimd128) { |
| Node* base = node->InputAt(0); |
| Node* index = node->InputAt(1); |
| int num_lanes = NumLanes(type); |
| Node** indices = zone()->NewArray<Node*>(num_lanes); |
| GetIndexNodes(index, indices, type); |
| Node** rep_nodes = zone()->NewArray<Node*>(num_lanes); |
| rep_nodes[0] = node; |
| rep_nodes[0]->ReplaceInput(1, indices[0]); |
| NodeProperties::ChangeOp(rep_nodes[0], load_op); |
| if (node->InputCount() > 2) { |
| DCHECK_LT(3, node->InputCount()); |
| Node* effect_input = node->InputAt(2); |
| Node* control_input = node->InputAt(3); |
| for (int i = num_lanes - 1; i > 0; --i) { |
| rep_nodes[i] = graph()->NewNode(load_op, base, indices[i], effect_input, |
| control_input); |
| effect_input = rep_nodes[i]; |
| } |
| rep_nodes[0]->ReplaceInput(2, rep_nodes[1]); |
| } else { |
| for (int i = 1; i < num_lanes; ++i) { |
| rep_nodes[i] = graph()->NewNode(load_op, base, indices[i]); |
| } |
| } |
| ReplaceNode(node, rep_nodes, num_lanes); |
| } else { |
| DefaultLowering(node); |
| } |
| } |
| |
| void SimdScalarLowering::LowerLoadTransformOp(Node* node, SimdType type) { |
| LoadTransformParameters params = LoadTransformParametersOf(node->op()); |
| MachineType load_rep = MachineType::None(); |
| SimdType load_type = type; |
| |
| // Load extends have a different machine type for loading. |
| switch (params.transformation) { |
| case LoadTransformation::kS128Load8x8S: |
| load_rep = MachineType::Int8(); |
| load_type = SimdType::kInt8x16; |
| break; |
| case LoadTransformation::kS128Load8x8U: |
| load_rep = MachineType::Uint8(); |
| load_type = SimdType::kInt8x16; |
| break; |
| case LoadTransformation::kS128Load16x4S: |
| load_rep = MachineType::Int16(); |
| load_type = SimdType::kInt16x8; |
| break; |
| case LoadTransformation::kS128Load16x4U: |
| load_rep = MachineType::Uint16(); |
| load_type = SimdType::kInt16x8; |
| break; |
| case LoadTransformation::kS128Load32x2S: |
| load_rep = MachineType::Int32(); |
| load_type = SimdType::kInt32x4; |
| break; |
| case LoadTransformation::kS128Load32x2U: |
| load_rep = MachineType::Uint32(); |
| load_type = SimdType::kInt32x4; |
| break; |
| case LoadTransformation::kS128Load8Splat: |
| case LoadTransformation::kS128Load16Splat: |
| case LoadTransformation::kS128Load32Splat: |
| case LoadTransformation::kS128Load64Splat: |
| case LoadTransformation::kS128Load32Zero: |
| case LoadTransformation::kS128Load64Zero: |
| load_rep = MachineTypeFrom(type); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| DCHECK_NE(load_rep, MachineType::None()); |
| |
| const Operator* load_op; |
| switch (params.kind) { |
| case MemoryAccessKind::kNormal: |
| load_op = machine()->Load(load_rep); |
| break; |
| case MemoryAccessKind::kUnaligned: |
| load_op = machine()->UnalignedLoad(load_rep); |
| break; |
| case MemoryAccessKind::kProtected: |
| load_op = machine()->ProtectedLoad(load_rep); |
| break; |
| } |
| |
| Node* base = node->InputAt(0); |
| Node* index = node->InputAt(1); |
| int num_lanes = NumLanes(type); |
| Node** reps = zone()->NewArray<Node*>(num_lanes); |
| Node* effect_input = node->InputAt(2); |
| Node* control_input = node->InputAt(3); |
| |
| // This node is also used as effect input into other nodes, so we need to |
| // change this node in place. |
| reps[0] = node; |
| NodeProperties::ChangeOp(reps[0], load_op); |
| |
| if (type != load_type) { |
| // We load a smaller lane size, then extend to a larger lane size. So use |
| // the smaller lane size to calculte the index nodes for loads, but only |
| // actually load half of those lanes. |
| Node** indices = zone()->NewArray<Node*>(num_lanes * 2); |
| GetIndexNodes(index, indices, load_type); |
| |
| reps[0]->ReplaceInput(1, indices[0]); |
| |
| for (int i = num_lanes - 1; i > 0; --i) { |
| reps[i] = graph()->NewNode(load_op, base, indices[i], effect_input, |
| control_input); |
| effect_input = reps[i]; |
| } |
| } else { |
| if (params.transformation == LoadTransformation::kS128Load32Zero) { |
| for (int i = num_lanes - 1; i > 0; --i) { |
| reps[i] = mcgraph_->Int32Constant(0); |
| } |
| } else if (params.transformation == LoadTransformation::kS128Load64Zero) { |
| for (int i = num_lanes - 1; i > 0; --i) { |
| reps[i] = mcgraph_->Int64Constant(0); |
| } |
| } else { |
| // Load splat, load from the same index for every lane. |
| for (int i = num_lanes - 1; i > 0; --i) { |
| reps[i] = |
| graph()->NewNode(load_op, base, index, effect_input, control_input); |
| effect_input = reps[i]; |
| } |
| } |
| } |
| |
| // Update the effect input, completing the effect chain, but only if there is |
| // an effect output (LoadZero does not have an effect output, it is zero). |
| if (reps[1]->op()->EffectOutputCount() > 0) { |
| reps[0]->ReplaceInput(2, reps[1]); |
| } |
| |
| // Special case, the load nodes need to be sign extended, and we do it here so |
| // the loop above can connect all the effect edges correctly. |
| if (params.transformation == LoadTransformation::kS128Load32x2S) { |
| for (int i = 0; i < num_lanes; ++i) { |
| reps[i] = graph()->NewNode(machine()->ChangeInt32ToInt64(), reps[i]); |
| } |
| } else if (params.transformation == LoadTransformation::kS128Load32x2U) { |
| for (int i = 0; i < num_lanes; ++i) { |
| reps[i] = graph()->NewNode(machine()->ChangeUint32ToUint64(), reps[i]); |
| } |
| } |
| |
| ReplaceNode(node, reps, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerStoreOp(Node* node) { |
| // For store operation, use replacement type of its input instead of the |
| // one of its effected node. |
| DCHECK_LT(2, node->InputCount()); |
| SimdType rep_type = ReplacementType(node->InputAt(2)); |
| replacements_[node->id()].type = rep_type; |
| const Operator* store_op; |
| MachineRepresentation rep; |
| switch (node->opcode()) { |
| case IrOpcode::kStore: { |
| rep = StoreRepresentationOf(node->op()).representation(); |
| WriteBarrierKind write_barrier_kind = |
| StoreRepresentationOf(node->op()).write_barrier_kind(); |
| store_op = machine()->Store(StoreRepresentation( |
| MachineTypeFrom(rep_type).representation(), write_barrier_kind)); |
| break; |
| } |
| case IrOpcode::kUnalignedStore: { |
| rep = UnalignedStoreRepresentationOf(node->op()); |
| store_op = |
| machine()->UnalignedStore(MachineTypeFrom(rep_type).representation()); |
| break; |
| } |
| case IrOpcode::kProtectedStore: { |
| rep = StoreRepresentationOf(node->op()).representation(); |
| store_op = |
| machine()->ProtectedStore(MachineTypeFrom(rep_type).representation()); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| if (rep == MachineRepresentation::kSimd128) { |
| Node* base = node->InputAt(0); |
| Node* index = node->InputAt(1); |
| int num_lanes = NumLanes(rep_type); |
| Node** indices = zone()->NewArray<Node*>(num_lanes); |
| GetIndexNodes(index, indices, rep_type); |
| Node* value = node->InputAt(2); |
| DCHECK(HasReplacement(1, value)); |
| Node** rep_nodes = zone()->NewArray<Node*>(num_lanes); |
| rep_nodes[0] = node; |
| Node** rep_inputs = GetReplacementsWithType(value, rep_type); |
| rep_nodes[0]->ReplaceInput(2, rep_inputs[0]); |
| rep_nodes[0]->ReplaceInput(1, indices[0]); |
| NodeProperties::ChangeOp(node, store_op); |
| if (node->InputCount() > 3) { |
| DCHECK_LT(4, node->InputCount()); |
| Node* effect_input = node->InputAt(3); |
| Node* control_input = node->InputAt(4); |
| for (int i = num_lanes - 1; i > 0; --i) { |
| rep_nodes[i] = |
| graph()->NewNode(store_op, base, indices[i], rep_inputs[i], |
| effect_input, control_input); |
| effect_input = rep_nodes[i]; |
| } |
| rep_nodes[0]->ReplaceInput(3, rep_nodes[1]); |
| } else { |
| for (int i = 1; i < num_lanes; ++i) { |
| rep_nodes[i] = |
| graph()->NewNode(store_op, base, indices[i], rep_inputs[i]); |
| } |
| } |
| ReplaceNode(node, rep_nodes, num_lanes); |
| } else { |
| DefaultLowering(node); |
| } |
| } |
| |
| void SimdScalarLowering::LowerBinaryOp(Node* node, SimdType input_rep_type, |
| const Operator* op, |
| bool not_horizontal) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| if (not_horizontal) { |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = graph()->NewNode(op, rep_left[i], rep_right[i]); |
| } |
| } else { |
| for (int i = 0; i < num_lanes / 2; ++i) { |
| rep_node[i] = graph()->NewNode(op, rep_left[i * 2], rep_left[i * 2 + 1]); |
| rep_node[i + num_lanes / 2] = |
| graph()->NewNode(op, rep_right[i * 2], rep_right[i * 2 + 1]); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerCompareOp(Node* node, SimdType input_rep_type, |
| const Operator* op, |
| bool invert_inputs) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* cmp_result = nullptr; |
| if (invert_inputs) { |
| cmp_result = graph()->NewNode(op, rep_right[i], rep_left[i]); |
| } else { |
| cmp_result = graph()->NewNode(op, rep_left[i], rep_right[i]); |
| } |
| Diamond d_cmp(graph(), common(), cmp_result); |
| rep_node[i] = ConstructPhiForComparison(d_cmp, input_rep_type, -1, 0); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| Node* SimdScalarLowering::FixUpperBits(Node* input, int32_t shift) { |
| return graph()->NewNode(machine()->Word32Sar(), |
| graph()->NewNode(machine()->Word32Shl(), input, |
| mcgraph_->Int32Constant(shift)), |
| mcgraph_->Int32Constant(shift)); |
| } |
| |
| void SimdScalarLowering::LowerBinaryOpForSmallInt(Node* node, |
| SimdType input_rep_type, |
| const Operator* op, |
| bool not_horizontal) { |
| DCHECK_EQ(2, node->InputCount()); |
| DCHECK(input_rep_type == SimdType::kInt16x8 || |
| input_rep_type == SimdType::kInt8x16); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| int32_t shift_val = |
| (input_rep_type == SimdType::kInt16x8) ? kShift16 : kShift8; |
| if (not_horizontal) { |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = FixUpperBits( |
| graph()->NewNode(op, rep_left[i], rep_right[i]), shift_val); |
| } |
| } else { |
| for (int i = 0; i < num_lanes / 2; ++i) { |
| rep_node[i] = FixUpperBits( |
| graph()->NewNode(op, rep_left[i * 2], rep_left[i * 2 + 1]), |
| shift_val); |
| rep_node[i + num_lanes / 2] = FixUpperBits( |
| graph()->NewNode(op, rep_right[i * 2], rep_right[i * 2 + 1]), |
| shift_val); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| Node* SimdScalarLowering::Mask(Node* input, int32_t mask) { |
| return graph()->NewNode(machine()->Word32And(), input, |
| mcgraph_->Int32Constant(mask)); |
| } |
| |
| void SimdScalarLowering::LowerSaturateBinaryOp(Node* node, |
| SimdType input_rep_type, |
| const Operator* op, |
| bool is_signed) { |
| DCHECK_EQ(2, node->InputCount()); |
| DCHECK(input_rep_type == SimdType::kInt16x8 || |
| input_rep_type == SimdType::kInt8x16); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| int32_t min = 0; |
| int32_t max = 0; |
| int32_t mask = 0; |
| int32_t shift_val = 0; |
| MachineRepresentation phi_rep; |
| if (input_rep_type == SimdType::kInt16x8) { |
| if (is_signed) { |
| min = std::numeric_limits<int16_t>::min(); |
| max = std::numeric_limits<int16_t>::max(); |
| } else { |
| min = std::numeric_limits<uint16_t>::min(); |
| max = std::numeric_limits<uint16_t>::max(); |
| } |
| mask = kMask16; |
| shift_val = kShift16; |
| phi_rep = MachineRepresentation::kWord16; |
| } else { |
| if (is_signed) { |
| min = std::numeric_limits<int8_t>::min(); |
| max = std::numeric_limits<int8_t>::max(); |
| } else { |
| min = std::numeric_limits<uint8_t>::min(); |
| max = std::numeric_limits<uint8_t>::max(); |
| } |
| mask = kMask8; |
| shift_val = kShift8; |
| phi_rep = MachineRepresentation::kWord8; |
| } |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* op_result = nullptr; |
| Node* left = is_signed ? rep_left[i] : Mask(rep_left[i], mask); |
| Node* right = is_signed ? rep_right[i] : Mask(rep_right[i], mask); |
| op_result = graph()->NewNode(op, left, right); |
| Diamond d_min(graph(), common(), |
| graph()->NewNode(machine()->Int32LessThan(), op_result, |
| mcgraph_->Int32Constant(min))); |
| rep_node[i] = d_min.Phi(phi_rep, mcgraph_->Int32Constant(min), op_result); |
| Diamond d_max(graph(), common(), |
| graph()->NewNode(machine()->Int32LessThan(), |
| mcgraph_->Int32Constant(max), rep_node[i])); |
| rep_node[i] = d_max.Phi(phi_rep, mcgraph_->Int32Constant(max), rep_node[i]); |
| rep_node[i] = |
| is_signed ? rep_node[i] : FixUpperBits(rep_node[i], shift_val); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerUnaryOp(Node* node, SimdType input_rep_type, |
| const Operator* op) { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = graph()->NewNode(op, rep[i]); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerIntMinMax(Node* node, const Operator* op, |
| bool is_max, SimdType type) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), type); |
| int num_lanes = NumLanes(type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| MachineRepresentation rep = MachineRepresentation::kNone; |
| if (type == SimdType::kInt32x4) { |
| rep = MachineRepresentation::kWord32; |
| } else if (type == SimdType::kInt16x8) { |
| rep = MachineRepresentation::kWord16; |
| } else if (type == SimdType::kInt8x16) { |
| rep = MachineRepresentation::kWord8; |
| } else { |
| UNREACHABLE(); |
| } |
| for (int i = 0; i < num_lanes; ++i) { |
| Diamond d(graph(), common(), |
| graph()->NewNode(op, rep_left[i], rep_right[i])); |
| if (is_max) { |
| rep_node[i] = d.Phi(rep, rep_right[i], rep_left[i]); |
| } else { |
| rep_node[i] = d.Phi(rep, rep_left[i], rep_right[i]); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| Node* SimdScalarLowering::BuildF64Trunc(Node* input) { |
| if (machine()->Float64RoundTruncate().IsSupported()) { |
| return graph()->NewNode(machine()->Float64RoundTruncate().op(), input); |
| } else { |
| ExternalReference ref = ExternalReference::wasm_f64_trunc(); |
| Node* stack_slot = |
| graph()->NewNode(machine()->StackSlot(MachineRepresentation::kFloat64)); |
| const Operator* store_op = machine()->Store( |
| StoreRepresentation(MachineRepresentation::kFloat64, kNoWriteBarrier)); |
| Node* effect = |
| graph()->NewNode(store_op, stack_slot, mcgraph_->Int32Constant(0), |
| input, graph()->start(), graph()->start()); |
| Node* function = graph()->NewNode(common()->ExternalConstant(ref)); |
| Node** args = zone()->NewArray<Node*>(4); |
| args[0] = function; |
| args[1] = stack_slot; |
| args[2] = effect; |
| args[3] = graph()->start(); |
| Signature<MachineType>::Builder sig_builder(zone(), 0, 1); |
| sig_builder.AddParam(MachineType::Pointer()); |
| auto call_descriptor = |
| Linkage::GetSimplifiedCDescriptor(zone(), sig_builder.Build()); |
| Node* call = graph()->NewNode(common()->Call(call_descriptor), 4, args); |
| return graph()->NewNode(machine()->Load(LoadRepresentation::Float64()), |
| stack_slot, mcgraph_->Int32Constant(0), call, |
| graph()->start()); |
| } |
| } |
| |
| void SimdScalarLowering::LowerConvertFromFloat(Node* node, bool is_signed) { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), SimdType::kFloat32x4); |
| Node* rep_node[kNumLanes32]; |
| Node* double_zero = graph()->NewNode(common()->Float64Constant(0.0)); |
| Node* min = graph()->NewNode( |
| common()->Float64Constant(static_cast<double>(is_signed ? kMinInt : 0))); |
| Node* max = graph()->NewNode(common()->Float64Constant( |
| static_cast<double>(is_signed ? kMaxInt : 0xFFFFFFFFu))); |
| for (int i = 0; i < kNumLanes32; ++i) { |
| Node* double_rep = |
| graph()->NewNode(machine()->ChangeFloat32ToFloat64(), rep[i]); |
| Diamond nan_d( |
| graph(), common(), |
| graph()->NewNode(machine()->Float64Equal(), double_rep, double_rep)); |
| Node* temp = |
| nan_d.Phi(MachineRepresentation::kFloat64, double_rep, double_zero); |
| Diamond min_d(graph(), common(), |
| graph()->NewNode(machine()->Float64LessThan(), temp, min)); |
| temp = min_d.Phi(MachineRepresentation::kFloat64, min, temp); |
| Diamond max_d(graph(), common(), |
| graph()->NewNode(machine()->Float64LessThan(), max, temp)); |
| temp = max_d.Phi(MachineRepresentation::kFloat64, max, temp); |
| Node* trunc = BuildF64Trunc(temp); |
| if (is_signed) { |
| rep_node[i] = graph()->NewNode(machine()->ChangeFloat64ToInt32(), trunc); |
| } else { |
| rep_node[i] = |
| graph()->NewNode(machine()->TruncateFloat64ToUint32(), trunc); |
| } |
| } |
| ReplaceNode(node, rep_node, kNumLanes32); |
| } |
| |
| void SimdScalarLowering::LowerConvertFromInt(Node* node, |
| SimdType input_rep_type, |
| SimdType output_rep_type, |
| bool is_signed, int start_index) { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| |
| int32_t mask = 0; |
| if (input_rep_type == SimdType::kInt16x8) { |
| DCHECK_EQ(output_rep_type, SimdType::kInt32x4); |
| mask = kMask16; |
| } else { |
| DCHECK_EQ(output_rep_type, SimdType::kInt16x8); |
| DCHECK_EQ(input_rep_type, SimdType::kInt8x16); |
| mask = kMask8; |
| } |
| |
| int num_lanes = NumLanes(output_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = |
| is_signed ? rep[i + start_index] : Mask(rep[i + start_index], mask); |
| } |
| |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerPack(Node* node, SimdType input_rep_type, |
| SimdType output_rep_type, bool is_signed) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| const Operator* less_op = machine()->Int32LessThan(); |
| Node* min = nullptr; |
| Node* max = nullptr; |
| const Operator* sign_extend; |
| MachineRepresentation phi_rep; |
| if (output_rep_type == SimdType::kInt16x8) { |
| sign_extend = machine()->SignExtendWord16ToInt32(); |
| DCHECK(input_rep_type == SimdType::kInt32x4); |
| if (is_signed) { |
| min = mcgraph_->Int32Constant(std::numeric_limits<int16_t>::min()); |
| max = mcgraph_->Int32Constant(std::numeric_limits<int16_t>::max()); |
| } else { |
| min = mcgraph_->Uint32Constant(std::numeric_limits<uint16_t>::min()); |
| max = mcgraph_->Uint32Constant(std::numeric_limits<uint16_t>::max()); |
| } |
| phi_rep = MachineRepresentation::kWord16; |
| } else { |
| sign_extend = machine()->SignExtendWord8ToInt32(); |
| DCHECK(output_rep_type == SimdType::kInt8x16 && |
| input_rep_type == SimdType::kInt16x8); |
| if (is_signed) { |
| min = mcgraph_->Int32Constant(std::numeric_limits<int8_t>::min()); |
| max = mcgraph_->Int32Constant(std::numeric_limits<int8_t>::max()); |
| } else { |
| min = mcgraph_->Uint32Constant(std::numeric_limits<uint8_t>::min()); |
| max = mcgraph_->Uint32Constant(std::numeric_limits<uint8_t>::max()); |
| } |
| phi_rep = MachineRepresentation::kWord8; |
| } |
| int num_lanes = NumLanes(output_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* input = nullptr; |
| if (i < num_lanes / 2) |
| input = rep_left[i]; |
| else |
| input = rep_right[i - num_lanes / 2]; |
| Diamond d_min(graph(), common(), graph()->NewNode(less_op, input, min)); |
| input = d_min.Phi(phi_rep, min, input); |
| Diamond d_max(graph(), common(), graph()->NewNode(less_op, max, input)); |
| // We keep nodes in sign-extended form. E.g. for uint8_t, we need to |
| // compare with 0x000000ff (saturated narrowing), but the result of |
| // conversion should be 0xffffffff to work well with the rest of lowering. |
| rep_node[i] = graph()->NewNode(sign_extend, d_max.Phi(phi_rep, max, input)); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerShiftOp(Node* node, SimdType type) { |
| DCHECK_EQ(2, node->InputCount()); |
| |
| // The shift node, if it has a replacement, should be a single scalar. |
| DCHECK_GE(1, ReplacementCount(node->InputAt(1))); |
| Node* val = (HasReplacement(0, node->InputAt(1))) |
| ? GetReplacements(node->InputAt(1))[0] |
| : node->InputAt(1); |
| |
| Node* shift_node = Mask(val, GetMaskForShift(node)); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), type); |
| int num_lanes = NumLanes(type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = rep[i]; |
| switch (node->opcode()) { |
| case IrOpcode::kI8x16ShrU: |
| rep_node[i] = Mask(rep_node[i], kMask8); |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shr(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI16x8ShrU: |
| rep_node[i] = Mask(rep_node[i], kMask16); |
| V8_FALLTHROUGH; |
| case IrOpcode::kI32x4ShrU: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shr(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI64x2ShrU: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word64Shr(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI64x2Shl: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word64Shl(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI32x4Shl: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shl(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI16x8Shl: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shl(), rep_node[i], shift_node); |
| rep_node[i] = FixUpperBits(rep_node[i], kShift16); |
| break; |
| case IrOpcode::kI8x16Shl: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shl(), rep_node[i], shift_node); |
| rep_node[i] = FixUpperBits(rep_node[i], kShift8); |
| break; |
| case IrOpcode::kI64x2ShrS: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word64Sar(), rep_node[i], shift_node); |
| break; |
| case IrOpcode::kI32x4ShrS: |
| case IrOpcode::kI16x8ShrS: |
| case IrOpcode::kI8x16ShrS: |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Sar(), rep_node[i], shift_node); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| Node* SimdScalarLowering::ConstructPhiForComparison(Diamond d, |
| SimdType rep_type, |
| int true_value, |
| int false_value) { |
| // Close the given Diamond d using a Phi node, taking care of constructing the |
| // right kind of constants (Int32 or Int64) based on rep_type. |
| if (rep_type == SimdType::kFloat64x2) { |
| MachineRepresentation rep = MachineRepresentation::kWord64; |
| return d.Phi(rep, mcgraph_->Int64Constant(true_value), |
| mcgraph_->Int64Constant(false_value)); |
| } else { |
| MachineRepresentation rep = |
| (rep_type == SimdType::kFloat32x4) |
| ? MachineRepresentation::kWord32 |
| : MachineTypeFrom(rep_type).representation(); |
| return d.Phi(rep, mcgraph_->Int32Constant(true_value), |
| mcgraph_->Int32Constant(false_value)); |
| } |
| } |
| |
| void SimdScalarLowering::LowerNotEqual(Node* node, SimdType input_rep_type, |
| const Operator* op) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), input_rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), input_rep_type); |
| int num_lanes = NumLanes(input_rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Diamond d(graph(), common(), |
| graph()->NewNode(op, rep_left[i], rep_right[i])); |
| rep_node[i] = ConstructPhiForComparison(d, input_rep_type, 0, -1); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerBitMaskOp(Node* node, SimdType rep_type, |
| int msb_index) { |
| Node** reps = GetReplacementsWithType(node->InputAt(0), rep_type); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(1); |
| Node* result = mcgraph_->Int32Constant(0); |
| uint32_t mask = 1 << msb_index; |
| |
| for (int i = 0; i < num_lanes; ++i) { |
| // Lane i should end up at bit i in the final result. |
| // +-----------------------------------------------------------------+ |
| // | | msb_index | (i < msb_index) | (i > msb_index) | |
| // +-------+-----------+----------------------+----------------------+ |
| // | i8x16 | 7 | msb >> (msb_index-i) | msb << (i-msb_index) | |
| // | i16x8 | 15 | msb >> (msb_index-i) | n/a | |
| // | i32x4 | 31 | msb >> (msb_index-i) | n/a | |
| // +-------+-----------+----------------------+----------------------+ |
| Node* msb = Mask(reps[i], mask); |
| |
| if (i < msb_index) { |
| int shift = msb_index - i; |
| Node* shifted = graph()->NewNode(machine()->Word32Shr(), msb, |
| mcgraph_->Int32Constant(shift)); |
| result = graph()->NewNode(machine()->Word32Or(), shifted, result); |
| } else if (i > msb_index) { |
| int shift = i - msb_index; |
| Node* shifted = graph()->NewNode(machine()->Word32Shl(), msb, |
| mcgraph_->Int32Constant(shift)); |
| result = graph()->NewNode(machine()->Word32Or(), shifted, result); |
| } else { |
| result = graph()->NewNode(machine()->Word32Or(), msb, result); |
| } |
| } |
| |
| rep_node[0] = result; |
| ReplaceNode(node, rep_node, 1); |
| } |
| |
| void SimdScalarLowering::LowerAllTrueOp(Node* node, SimdType rep_type) { |
| // AllTrue ops require the input to be of a particular SimdType, but the op |
| // itself is always replaced by a Int32x4 with 1 node. |
| int num_lanes = NumLanes(rep_type); |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* zero = mcgraph_->Int32Constant(0); |
| Node* tmp_result = mcgraph_->Int32Constant(1); |
| for (int i = 0; i < num_lanes; ++i) { |
| Diamond d(graph(), common(), |
| graph()->NewNode(machine()->Word32Equal(), rep[i], zero)); |
| tmp_result = d.Phi(MachineRepresentation::kWord32, zero, tmp_result); |
| } |
| rep_node[0] = tmp_result; |
| ReplaceNode(node, rep_node, 1); |
| } |
| |
| void SimdScalarLowering::LowerFloatPseudoMinMax(Node* node, const Operator* op, |
| bool is_max, SimdType type) { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), type); |
| int num_lanes = NumLanes(type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| MachineRepresentation rep = MachineTypeFrom(type).representation(); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* cmp = is_max ? graph()->NewNode(op, rep_left[i], rep_right[i]) |
| : graph()->NewNode(op, rep_right[i], rep_left[i]); |
| Diamond d(graph(), common(), cmp); |
| rep_node[i] = d.Phi(rep, rep_right[i], rep_left[i]); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| } |
| |
| void SimdScalarLowering::LowerNode(Node* node) { |
| SimdType rep_type = ReplacementType(node); |
| int num_lanes = NumLanes(rep_type); |
| switch (node->opcode()) { |
| case IrOpcode::kS128Const: { |
| // We could use GetReplacementsWithType for all this, but it adds a lot of |
| // nodes, so sign extend the immediates ourselves here. |
| DCHECK_EQ(0, node->InputCount()); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| S128ImmediateParameter params = S128ImmediateParameterOf(node->op()); |
| |
| // For all the small ints below, we have a choice of static_cast or bit |
| // twiddling, clang seems to be able to optimize either |
| // (https://godbolt.org/z/9c65o8) so use static_cast for clarity. |
| switch (rep_type) { |
| case SimdType::kInt8x16: { |
| for (int i = 0; i < num_lanes; ++i) { |
| Address data_address = reinterpret_cast<Address>(params.data() + i); |
| rep_node[i] = mcgraph_->Int32Constant(static_cast<int32_t>( |
| base::ReadLittleEndianValue<int8_t>(data_address))); |
| } |
| break; |
| } |
| case SimdType::kInt16x8: { |
| int16_t val[kNumLanes16]; |
| memcpy(val, params.data(), kSimd128Size); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = mcgraph_->Int32Constant(static_cast<int32_t>( |
| base::ReadLittleEndianValue<int16_t>(&val[i]))); |
| } |
| break; |
| } |
| case SimdType::kInt32x4: { |
| uint32_t val[kNumLanes32]; |
| memcpy(val, params.data(), kSimd128Size); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = mcgraph_->Int32Constant( |
| base::ReadLittleEndianValue<uint32_t>(&val[i])); |
| } |
| break; |
| } |
| case SimdType::kInt64x2: { |
| uint64_t val[kNumLanes64]; |
| memcpy(val, params.data(), kSimd128Size); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = mcgraph_->Int64Constant( |
| base::ReadLittleEndianValue<uint64_t>(&val[i])); |
| } |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| float val[kNumLanes32]; |
| memcpy(val, params.data(), kSimd128Size); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = mcgraph_->Float32Constant( |
| base::ReadLittleEndianValue<float>(&val[i])); |
| } |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| double val[kNumLanes64]; |
| memcpy(val, params.data(), kSimd128Size); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = mcgraph_->Float64Constant( |
| base::ReadLittleEndianValue<double>(&val[i])); |
| } |
| break; |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kStart: { |
| int parameter_count = GetParameterCountAfterLowering(); |
| // Only exchange the node if the parameter count actually changed. |
| if (parameter_count != static_cast<int>(signature()->parameter_count())) { |
| int delta = |
| parameter_count - static_cast<int>(signature()->parameter_count()); |
| int new_output_count = node->op()->ValueOutputCount() + delta; |
| NodeProperties::ChangeOp(node, common()->Start(new_output_count)); |
| } |
| break; |
| } |
| case IrOpcode::kParameter: { |
| DCHECK_EQ(1, node->InputCount()); |
| int param_count = static_cast<int>(signature()->parameter_count()); |
| // Only exchange the node if the parameter count actually changed. We do |
| // not even have to do the default lowering because the start node, |
| // the only input of a parameter node, only changes if the parameter count |
| // changes. |
| if (GetParameterCountAfterLowering() != param_count) { |
| int old_index = ParameterIndexOf(node->op()); |
| // Parameter index 0 is the instance parameter, we will use old_index to |
| // index into the function signature, so we need to decrease it by 1. |
| --old_index; |
| int new_index = |
| GetParameterIndexAfterLoweringSimd128(signature(), old_index); |
| // Similarly, the index into function signature needs to account for the |
| // instance parameter, so increase it by 1. |
| ++new_index; |
| NodeProperties::ChangeOp(node, common()->Parameter(new_index)); |
| |
| if (old_index < 0) { |
| break; |
| } |
| |
| DCHECK(old_index < param_count); |
| |
| if (signature()->GetParam(old_index) == |
| MachineRepresentation::kSimd128) { |
| Node* new_node[kNumLanes32]; |
| new_node[0] = node; |
| for (int i = 1; i < kNumLanes32; ++i) { |
| new_node[i] = graph()->NewNode(common()->Parameter(new_index + i), |
| graph()->start()); |
| } |
| ReplaceNode(node, new_node, kNumLanes32); |
| } |
| } |
| break; |
| } |
| case IrOpcode::kSimd128ReverseBytes: { |
| DCHECK_EQ(1, node->InputCount()); |
| SimdType input_type = ReplacementType(node->InputAt(0)); |
| bool is_float = input_type == SimdType::kFloat32x4 || |
| input_type == SimdType::kFloat64x2; |
| replacements_[node->id()].type = |
| is_float ? SimdType::kFloat32x4 : SimdType::kInt32x4; |
| Node** rep = GetReplacementsWithType( |
| node->InputAt(0), |
| is_float ? SimdType::kFloat32x4 : SimdType::kInt32x4); |
| Node* rep_node[kNumLanes32]; |
| for (int i = 0; i < kNumLanes32; ++i) { |
| Node* temp = is_float ? graph()->NewNode( |
| machine()->BitcastFloat32ToInt32(), rep[i]) |
| : rep[i]; |
| temp = graph()->NewNode(machine()->Word32ReverseBytes(), temp); |
| rep_node[kNumLanes32 - 1 - i] = |
| is_float |
| ? graph()->NewNode(machine()->BitcastInt32ToFloat32(), temp) |
| : temp; |
| } |
| ReplaceNode(node, rep_node, kNumLanes32); |
| break; |
| } |
| case IrOpcode::kLoad: |
| case IrOpcode::kUnalignedLoad: |
| case IrOpcode::kProtectedLoad: { |
| LowerLoadOp(node, rep_type); |
| break; |
| } |
| case IrOpcode::kLoadTransform: { |
| LowerLoadTransformOp(node, rep_type); |
| break; |
| } |
| case IrOpcode::kStore: |
| case IrOpcode::kUnalignedStore: |
| case IrOpcode::kProtectedStore: { |
| LowerStoreOp(node); |
| break; |
| } |
| case IrOpcode::kReturn: { |
| int old_input_count = node->InputCount(); |
| int return_arity = static_cast<int>(signature()->return_count()); |
| for (int i = 0; i < return_arity; i++) { |
| if (signature()->GetReturn(i) != MachineRepresentation::kSimd128) { |
| continue; |
| } |
| |
| // Return nodes have a hidden input at value 0. |
| Node* input = node->InputAt(i + 1); |
| if (!HasReplacement(0, input)) { |
| continue; |
| } |
| |
| // V128 return types are lowered to i32x4. |
| Node** reps = GetReplacementsWithType(input, rep_type); |
| ReplaceNode(input, reps, NumLanes(rep_type)); |
| } |
| |
| DefaultLowering(node); |
| // Nothing needs to be done here since inputs did not change. |
| if (old_input_count == node->InputCount()) { |
| break; |
| } |
| |
| int new_return_count = GetReturnCountAfterLoweringSimd128(signature()); |
| if (static_cast<int>(signature()->return_count()) != new_return_count) { |
| NodeProperties::ChangeOp(node, common()->Return(new_return_count)); |
| } |
| break; |
| } |
| case IrOpcode::kCall: { |
| // TODO(turbofan): Make wasm code const-correct wrt. CallDescriptor. |
| auto call_descriptor = |
| const_cast<CallDescriptor*>(CallDescriptorOf(node->op())); |
| bool returns_require_lowering = |
| GetReturnCountAfterLoweringSimd128(call_descriptor) != |
| static_cast<int>(call_descriptor->ReturnCount()); |
| |
| // All call arguments are lowered to i32x4 in the call descriptor, so the |
| // arguments need to be converted to i32x4 as well. |
| for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) { |
| Node* input = node->InputAt(i); |
| if (ReplacementCount(input) == 1) { |
| // Special case for extract lanes |
| Node** reps = GetReplacements(input); |
| ReplaceNode(input, reps, 1); |
| } else if (HasReplacement(0, input)) { |
| Node** reps = GetReplacementsWithType(input, SimdType::kInt32x4); |
| ReplaceNode(input, reps, NumLanes(SimdType::kInt32x4)); |
| } |
| } |
| |
| if (DefaultLowering(node) || returns_require_lowering) { |
| // We have to adjust the call descriptor. |
| const Operator* op = common()->Call( |
| GetI32WasmCallDescriptorForSimd(zone(), call_descriptor)); |
| NodeProperties::ChangeOp(node, op); |
| } |
| |
| if (!returns_require_lowering) { |
| break; |
| } |
| |
| size_t return_arity = call_descriptor->ReturnCount(); |
| |
| if (return_arity == 1) { |
| // We access the additional return values through projections. |
| // Special case for return_arity 1, with multi-returns, we would have |
| // already built projections for each return value, and will be handled |
| // by the following code. |
| Node* rep_node[kNumLanes32]; |
| for (int i = 0; i < kNumLanes32; ++i) { |
| rep_node[i] = |
| graph()->NewNode(common()->Projection(i), node, graph()->start()); |
| } |
| ReplaceNode(node, rep_node, kNumLanes32); |
| break; |
| } |
| |
| ZoneVector<Node*> projections(return_arity, zone()); |
| NodeProperties::CollectValueProjections(node, projections.data(), |
| return_arity); |
| |
| for (size_t old_index = 0, new_index = 0; old_index < return_arity; |
| ++old_index, ++new_index) { |
| Node* use_node = projections[old_index]; |
| DCHECK_EQ(ProjectionIndexOf(use_node->op()), old_index); |
| DCHECK_EQ(GetReturnIndexAfterLowering(call_descriptor, |
| static_cast<int>(old_index)), |
| static_cast<int>(new_index)); |
| if (new_index != old_index) { |
| NodeProperties::ChangeOp(use_node, common()->Projection(new_index)); |
| } |
| if (call_descriptor->GetReturnType(old_index).representation() == |
| MachineRepresentation::kSimd128) { |
| Node* rep_node[kNumLanes32]; |
| for (int i = 0; i < kNumLanes32; ++i) { |
| rep_node[i] = graph()->NewNode(common()->Projection(new_index + i), |
| node, graph()->start()); |
| } |
| ReplaceNode(use_node, rep_node, kNumLanes32); |
| new_index += kNumLanes32 - 1; |
| } |
| } |
| break; |
| } |
| case IrOpcode::kPhi: { |
| MachineRepresentation rep = PhiRepresentationOf(node->op()); |
| if (rep == MachineRepresentation::kSimd128) { |
| // The replacement nodes have already been created, we only have to |
| // replace placeholder nodes. |
| Node** rep_node = GetReplacements(node); |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| Node** rep_input = |
| GetReplacementsWithType(node->InputAt(i), rep_type); |
| for (int j = 0; j < num_lanes; j++) { |
| rep_node[j]->ReplaceInput(i, rep_input[j]); |
| } |
| } |
| } else { |
| DefaultLowering(node); |
| } |
| break; |
| } |
| case IrOpcode::kI64x2Add: { |
| LowerBinaryOp(node, rep_type, machine()->Int64Add()); |
| break; |
| } |
| case IrOpcode::kI64x2Sub: { |
| LowerBinaryOp(node, rep_type, machine()->Int64Sub()); |
| break; |
| } |
| case IrOpcode::kI64x2Mul: { |
| LowerBinaryOp(node, rep_type, machine()->Int64Mul()); |
| break; |
| } |
| #define I32X4_BINOP_CASE(opcode, instruction) \ |
| case IrOpcode::opcode: { \ |
| LowerBinaryOp(node, rep_type, machine()->instruction()); \ |
| break; \ |
| } |
| I32X4_BINOP_CASE(kI32x4Add, Int32Add) |
| I32X4_BINOP_CASE(kI32x4Sub, Int32Sub) |
| I32X4_BINOP_CASE(kI32x4Mul, Int32Mul) |
| I32X4_BINOP_CASE(kS128And, Word32And) |
| I32X4_BINOP_CASE(kS128Or, Word32Or) |
| I32X4_BINOP_CASE(kS128Xor, Word32Xor) |
| #undef I32X4_BINOP_CASE |
| case IrOpcode::kI32x4AddHoriz: { |
| LowerBinaryOp(node, rep_type, machine()->Int32Add(), false); |
| break; |
| } |
| case IrOpcode::kI16x8AddHoriz: { |
| LowerBinaryOpForSmallInt(node, rep_type, machine()->Int32Add(), false); |
| break; |
| } |
| case IrOpcode::kI16x8Add: |
| case IrOpcode::kI8x16Add: { |
| LowerBinaryOpForSmallInt(node, rep_type, machine()->Int32Add()); |
| break; |
| } |
| case IrOpcode::kI16x8Sub: |
| case IrOpcode::kI8x16Sub: { |
| LowerBinaryOpForSmallInt(node, rep_type, machine()->Int32Sub()); |
| break; |
| } |
| case IrOpcode::kI16x8Mul: |
| case IrOpcode::kI8x16Mul: { |
| LowerBinaryOpForSmallInt(node, rep_type, machine()->Int32Mul()); |
| break; |
| } |
| case IrOpcode::kI16x8AddSatS: |
| case IrOpcode::kI8x16AddSatS: { |
| LowerSaturateBinaryOp(node, rep_type, machine()->Int32Add(), true); |
| break; |
| } |
| case IrOpcode::kI16x8SubSatS: |
| case IrOpcode::kI8x16SubSatS: { |
| LowerSaturateBinaryOp(node, rep_type, machine()->Int32Sub(), true); |
| break; |
| } |
| case IrOpcode::kI16x8AddSatU: |
| case IrOpcode::kI8x16AddSatU: { |
| LowerSaturateBinaryOp(node, rep_type, machine()->Int32Add(), false); |
| break; |
| } |
| case IrOpcode::kI16x8SubSatU: |
| case IrOpcode::kI8x16SubSatU: { |
| LowerSaturateBinaryOp(node, rep_type, machine()->Int32Sub(), false); |
| break; |
| } |
| case IrOpcode::kI32x4MaxS: |
| case IrOpcode::kI16x8MaxS: |
| case IrOpcode::kI8x16MaxS: { |
| LowerIntMinMax(node, machine()->Int32LessThan(), true, rep_type); |
| break; |
| } |
| case IrOpcode::kI32x4MinS: |
| case IrOpcode::kI16x8MinS: |
| case IrOpcode::kI8x16MinS: { |
| LowerIntMinMax(node, machine()->Int32LessThan(), false, rep_type); |
| break; |
| } |
| case IrOpcode::kI32x4MaxU: |
| case IrOpcode::kI16x8MaxU: |
| case IrOpcode::kI8x16MaxU: { |
| LowerIntMinMax(node, machine()->Uint32LessThan(), true, rep_type); |
| break; |
| } |
| case IrOpcode::kI32x4MinU: |
| case IrOpcode::kI16x8MinU: |
| case IrOpcode::kI8x16MinU: { |
| LowerIntMinMax(node, machine()->Uint32LessThan(), false, rep_type); |
| break; |
| } |
| case IrOpcode::kI32x4DotI16x8S: { |
| // i32x4.dot_i16x8_s wants the inputs to be i16x8, but outputs to i32x4. |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = |
| GetReplacementsWithType(node->InputAt(0), SimdType::kInt16x8); |
| Node** rep_right = |
| GetReplacementsWithType(node->InputAt(1), SimdType::kInt16x8); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* lo = graph()->NewNode(machine()->Int32Mul(), rep_left[i * 2], |
| rep_right[i * 2]); |
| Node* hi = graph()->NewNode(machine()->Int32Mul(), rep_left[i * 2 + 1], |
| rep_right[i * 2 + 1]); |
| rep_node[i] = graph()->NewNode(machine()->Int32Add(), lo, hi); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kI64x2Neg: { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* zero = graph()->NewNode(common()->Int64Constant(0)); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = graph()->NewNode(machine()->Int64Sub(), zero, rep[i]); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kI32x4Neg: |
| case IrOpcode::kI16x8Neg: |
| case IrOpcode::kI8x16Neg: { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* zero = graph()->NewNode(common()->Int32Constant(0)); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = graph()->NewNode(machine()->Int32Sub(), zero, rep[i]); |
| if (node->opcode() == IrOpcode::kI16x8Neg) { |
| rep_node[i] = FixUpperBits(rep_node[i], kShift16); |
| } else if (node->opcode() == IrOpcode::kI8x16Neg) { |
| rep_node[i] = FixUpperBits(rep_node[i], kShift8); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kI32x4Abs: |
| case IrOpcode::kI16x8Abs: |
| case IrOpcode::kI8x16Abs: { |
| // From https://stackoverflow.com/a/14194764 |
| // abs(x) = (x XOR y) - y |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| // It's fine to shift by 31 even for i8x16 since each node is |
| // effectively expanded to 32 bits. |
| Node* y = graph()->NewNode(machine()->Word32Sar(), rep[i], |
| mcgraph_->Int32Constant(31)); |
| rep_node[i] = graph()->NewNode( |
| machine()->Int32Sub(), |
| graph()->NewNode(machine()->Word32Xor(), rep[i], y), y); |
| if (node->opcode() == IrOpcode::kI16x8Neg) { |
| rep_node[i] = FixUpperBits(rep_node[i], kShift16); |
| } else if (node->opcode() == IrOpcode::kI8x16Neg) { |
| rep_node[i] = FixUpperBits(rep_node[i], kShift8); |
| } |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kS128Zero: { |
| DCHECK_EQ(0, node->InputCount()); |
| Node* rep_node[kNumLanes32]; |
| for (int i = 0; i < kNumLanes32; ++i) { |
| rep_node[i] = mcgraph_->Int32Constant(0); |
| } |
| ReplaceNode(node, rep_node, kNumLanes32); |
| break; |
| } |
| case IrOpcode::kS128Not: { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node* rep_node[kNumLanes32]; |
| Node* mask = graph()->NewNode(common()->Int32Constant(0xFFFFFFFF)); |
| for (int i = 0; i < kNumLanes32; ++i) { |
| rep_node[i] = graph()->NewNode(machine()->Word32Xor(), rep[i], mask); |
| } |
| ReplaceNode(node, rep_node, kNumLanes32); |
| break; |
| } |
| case IrOpcode::kS128AndNot: { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* mask = graph()->NewNode(common()->Int32Constant(0xFFFFFFFF)); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* not_rep_right = |
| graph()->NewNode(machine()->Word32Xor(), rep_right[i], mask); |
| rep_node[i] = graph()->NewNode(machine()->Word32And(), rep_left[i], |
| not_rep_right); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kI32x4SConvertF32x4: { |
| LowerConvertFromFloat(node, true); |
| break; |
| } |
| case IrOpcode::kI32x4UConvertF32x4: { |
| LowerConvertFromFloat(node, false); |
| break; |
| } |
| case IrOpcode::kI32x4SConvertI16x8Low: { |
| LowerConvertFromInt(node, SimdType::kInt16x8, SimdType::kInt32x4, true, |
| 0); |
| break; |
| } |
| case IrOpcode::kI32x4SConvertI16x8High: { |
| LowerConvertFromInt(node, SimdType::kInt16x8, SimdType::kInt32x4, true, |
| 4); |
| break; |
| } |
| case IrOpcode::kI32x4UConvertI16x8Low: { |
| LowerConvertFromInt(node, SimdType::kInt16x8, SimdType::kInt32x4, false, |
| 0); |
| break; |
| } |
| case IrOpcode::kI32x4UConvertI16x8High: { |
| LowerConvertFromInt(node, SimdType::kInt16x8, SimdType::kInt32x4, false, |
| 4); |
| break; |
| } |
| case IrOpcode::kI16x8SConvertI8x16Low: { |
| LowerConvertFromInt(node, SimdType::kInt8x16, SimdType::kInt16x8, true, |
| 0); |
| break; |
| } |
| case IrOpcode::kI16x8SConvertI8x16High: { |
| LowerConvertFromInt(node, SimdType::kInt8x16, SimdType::kInt16x8, true, |
| 8); |
| break; |
| } |
| case IrOpcode::kI16x8UConvertI8x16Low: { |
| LowerConvertFromInt(node, SimdType::kInt8x16, SimdType::kInt16x8, false, |
| 0); |
| break; |
| } |
| case IrOpcode::kI16x8UConvertI8x16High: { |
| LowerConvertFromInt(node, SimdType::kInt8x16, SimdType::kInt16x8, false, |
| 8); |
| break; |
| } |
| case IrOpcode::kI16x8SConvertI32x4: { |
| LowerPack(node, SimdType::kInt32x4, SimdType::kInt16x8, true); |
| break; |
| } |
| case IrOpcode::kI16x8UConvertI32x4: { |
| LowerPack(node, SimdType::kInt32x4, SimdType::kInt16x8, false); |
| break; |
| } |
| case IrOpcode::kI8x16SConvertI16x8: { |
| LowerPack(node, SimdType::kInt16x8, SimdType::kInt8x16, true); |
| break; |
| } |
| case IrOpcode::kI8x16UConvertI16x8: { |
| LowerPack(node, SimdType::kInt16x8, SimdType::kInt8x16, false); |
| break; |
| } |
| case IrOpcode::kI64x2Shl: |
| case IrOpcode::kI32x4Shl: |
| case IrOpcode::kI16x8Shl: |
| case IrOpcode::kI8x16Shl: |
| case IrOpcode::kI64x2ShrS: |
| case IrOpcode::kI32x4ShrS: |
| case IrOpcode::kI16x8ShrS: |
| case IrOpcode::kI8x16ShrS: |
| case IrOpcode::kI64x2ShrU: |
| case IrOpcode::kI32x4ShrU: |
| case IrOpcode::kI16x8ShrU: |
| case IrOpcode::kI8x16ShrU: { |
| LowerShiftOp(node, rep_type); |
| break; |
| } |
| case IrOpcode::kF32x4AddHoriz: { |
| LowerBinaryOp(node, rep_type, machine()->Float32Add(), false); |
| break; |
| } |
| #define F32X4_BINOP_CASE(name) \ |
| case IrOpcode::kF32x4##name: { \ |
| LowerBinaryOp(node, rep_type, machine()->Float32##name()); \ |
| break; \ |
| } |
| F32X4_BINOP_CASE(Add) |
| F32X4_BINOP_CASE(Sub) |
| F32X4_BINOP_CASE(Mul) |
| F32X4_BINOP_CASE(Div) |
| F32X4_BINOP_CASE(Min) |
| F32X4_BINOP_CASE(Max) |
| case IrOpcode::kF32x4Pmin: { |
| LowerFloatPseudoMinMax(node, machine()->Float32LessThan(), false, |
| rep_type); |
| break; |
| } |
| case IrOpcode::kF32x4Pmax: { |
| LowerFloatPseudoMinMax(node, machine()->Float32LessThan(), true, |
| rep_type); |
| break; |
| } |
| #undef F32X4_BINOP_CASE |
| #define F32X4_UNOP_CASE(name) \ |
| case IrOpcode::kF32x4##name: { \ |
| LowerUnaryOp(node, rep_type, machine()->Float32##name()); \ |
| break; \ |
| } |
| F32X4_UNOP_CASE(Abs) |
| F32X4_UNOP_CASE(Neg) |
| F32X4_UNOP_CASE(Sqrt) |
| #undef F32X4_UNOP_CASE |
| case IrOpcode::kF32x4Ceil: { |
| LowerUnaryOp(node, rep_type, machine()->Float32RoundUp().op()); |
| break; |
| } |
| case IrOpcode::kF32x4Floor: { |
| LowerUnaryOp(node, rep_type, machine()->Float32RoundDown().op()); |
| break; |
| } |
| case IrOpcode::kF32x4Trunc: { |
| LowerUnaryOp(node, rep_type, machine()->Float32RoundTruncate().op()); |
| break; |
| } |
| case IrOpcode::kF32x4NearestInt: { |
| LowerUnaryOp(node, rep_type, machine()->Float32RoundTiesEven().op()); |
| break; |
| } |
| case IrOpcode::kF32x4RecipApprox: |
| case IrOpcode::kF32x4RecipSqrtApprox: { |
| DCHECK_EQ(1, node->InputCount()); |
| Node** rep = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* float_one = graph()->NewNode(common()->Float32Constant(1.0)); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* tmp = rep[i]; |
| if (node->opcode() == IrOpcode::kF32x4RecipSqrtApprox) { |
| tmp = graph()->NewNode(machine()->Float32Sqrt(), rep[i]); |
| } |
| rep_node[i] = graph()->NewNode(machine()->Float32Div(), float_one, tmp); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kF32x4SConvertI32x4: { |
| LowerUnaryOp(node, SimdType::kInt32x4, machine()->RoundInt32ToFloat32()); |
| break; |
| } |
| case IrOpcode::kF32x4UConvertI32x4: { |
| LowerUnaryOp(node, SimdType::kInt32x4, machine()->RoundUint32ToFloat32()); |
| break; |
| } |
| case IrOpcode::kF64x2Abs: { |
| LowerUnaryOp(node, rep_type, machine()->Float64Abs()); |
| break; |
| } |
| case IrOpcode::kF64x2Neg: { |
| LowerUnaryOp(node, rep_type, machine()->Float64Neg()); |
| break; |
| } |
| case IrOpcode::kF64x2Sqrt: { |
| LowerUnaryOp(node, rep_type, machine()->Float64Sqrt()); |
| break; |
| } |
| case IrOpcode::kF64x2Add: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Add()); |
| break; |
| } |
| case IrOpcode::kF64x2Sub: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Sub()); |
| break; |
| } |
| case IrOpcode::kF64x2Mul: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Mul()); |
| break; |
| } |
| case IrOpcode::kF64x2Div: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Div()); |
| break; |
| } |
| case IrOpcode::kF64x2Min: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Min()); |
| break; |
| } |
| case IrOpcode::kF64x2Max: { |
| LowerBinaryOp(node, rep_type, machine()->Float64Max()); |
| break; |
| } |
| case IrOpcode::kF64x2Pmin: { |
| LowerFloatPseudoMinMax(node, machine()->Float64LessThan(), false, |
| rep_type); |
| break; |
| } |
| case IrOpcode::kF64x2Pmax: { |
| LowerFloatPseudoMinMax(node, machine()->Float64LessThan(), true, |
| rep_type); |
| break; |
| } |
| case IrOpcode::kF64x2Ceil: { |
| LowerUnaryOp(node, rep_type, machine()->Float64RoundUp().op()); |
| break; |
| } |
| case IrOpcode::kF64x2Floor: { |
| LowerUnaryOp(node, rep_type, machine()->Float64RoundDown().op()); |
| break; |
| } |
| case IrOpcode::kF64x2Trunc: { |
| LowerUnaryOp(node, rep_type, machine()->Float64RoundTruncate().op()); |
| break; |
| } |
| case IrOpcode::kF64x2NearestInt: { |
| LowerUnaryOp(node, rep_type, machine()->Float64RoundTiesEven().op()); |
| break; |
| } |
| case IrOpcode::kF64x2Splat: |
| case IrOpcode::kF32x4Splat: |
| case IrOpcode::kI64x2Splat: |
| case IrOpcode::kI32x4Splat: |
| case IrOpcode::kI16x8Splat: |
| case IrOpcode::kI8x16Splat: { |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| Node* val = (HasReplacement(0, node->InputAt(0))) |
| ? GetReplacements(node->InputAt(0))[0] |
| : node->InputAt(0); |
| |
| // I16 and I8 are placed in Word32 nodes, we need to mask them |
| // accordingly, to account for overflows, then sign extend them. |
| if (node->opcode() == IrOpcode::kI16x8Splat) { |
| val = graph()->NewNode(machine()->SignExtendWord16ToInt32(), |
| Mask(val, kMask16)); |
| } else if (node->opcode() == IrOpcode::kI8x16Splat) { |
| val = graph()->NewNode(machine()->SignExtendWord8ToInt32(), |
| Mask(val, kMask8)); |
| } |
| |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = val; |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kF64x2ExtractLane: |
| case IrOpcode::kF32x4ExtractLane: |
| case IrOpcode::kI64x2ExtractLane: |
| case IrOpcode::kI32x4ExtractLane: |
| case IrOpcode::kI16x8ExtractLaneU: |
| case IrOpcode::kI16x8ExtractLaneS: |
| case IrOpcode::kI8x16ExtractLaneU: |
| case IrOpcode::kI8x16ExtractLaneS: { |
| int32_t lane = OpParameter<int32_t>(node->op()); |
| Node** rep_node = zone()->NewArray<Node*>(1); |
| rep_node[0] = GetReplacementsWithType(node->InputAt(0), rep_type)[lane]; |
| |
| // If unsigned, mask the top bits. |
| if (node->opcode() == IrOpcode::kI16x8ExtractLaneU) { |
| rep_node[0] = Mask(rep_node[0], kMask16); |
| } else if (node->opcode() == IrOpcode::kI8x16ExtractLaneU) { |
| rep_node[0] = Mask(rep_node[0], kMask8); |
| } |
| |
| ReplaceNode(node, rep_node, 1); |
| break; |
| } |
| case IrOpcode::kF64x2ReplaceLane: |
| case IrOpcode::kF32x4ReplaceLane: |
| case IrOpcode::kI64x2ReplaceLane: |
| case IrOpcode::kI32x4ReplaceLane: |
| case IrOpcode::kI16x8ReplaceLane: |
| case IrOpcode::kI8x16ReplaceLane: { |
| DCHECK_EQ(2, node->InputCount()); |
| Node* repNode = node->InputAt(1); |
| int32_t lane = OpParameter<int32_t>(node->op()); |
| Node** old_rep_node = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_node[i] = old_rep_node[i]; |
| } |
| if (HasReplacement(0, repNode)) { |
| rep_node[lane] = GetReplacements(repNode)[0]; |
| } else { |
| rep_node[lane] = repNode; |
| } |
| |
| // The replacement nodes for these opcodes are in Word32, and we always |
| // store nodes in sign extended form (and mask to account for overflows.) |
| if (node->opcode() == IrOpcode::kI16x8ReplaceLane) { |
| rep_node[lane] = graph()->NewNode(machine()->SignExtendWord16ToInt32(), |
| Mask(rep_node[lane], kMask16)); |
| } else if (node->opcode() == IrOpcode::kI8x16ReplaceLane) { |
| rep_node[lane] = graph()->NewNode(machine()->SignExtendWord8ToInt32(), |
| Mask(rep_node[lane], kMask8)); |
| } |
| |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| #define COMPARISON_CASE(type, simd_op, lowering_op, invert) \ |
| case IrOpcode::simd_op: { \ |
| LowerCompareOp(node, SimdType::k##type, machine()->lowering_op(), invert); \ |
| break; \ |
| } |
| COMPARISON_CASE(Float64x2, kF64x2Eq, Float64Equal, false) |
| COMPARISON_CASE(Float64x2, kF64x2Lt, Float64LessThan, false) |
| COMPARISON_CASE(Float64x2, kF64x2Le, Float64LessThanOrEqual, false) |
| COMPARISON_CASE(Float32x4, kF32x4Eq, Float32Equal, false) |
| COMPARISON_CASE(Float32x4, kF32x4Lt, Float32LessThan, false) |
| COMPARISON_CASE(Float32x4, kF32x4Le, Float32LessThanOrEqual, false) |
| COMPARISON_CASE(Float32x4, kF32x4Gt, Float32LessThan, true) |
| COMPARISON_CASE(Float32x4, kF32x4Ge, Float32LessThanOrEqual, true) |
| COMPARISON_CASE(Int32x4, kI32x4Eq, Word32Equal, false) |
| COMPARISON_CASE(Int32x4, kI32x4LtS, Int32LessThan, false) |
| COMPARISON_CASE(Int32x4, kI32x4LeS, Int32LessThanOrEqual, false) |
| COMPARISON_CASE(Int32x4, kI32x4GtS, Int32LessThan, true) |
| COMPARISON_CASE(Int32x4, kI32x4GeS, Int32LessThanOrEqual, true) |
| COMPARISON_CASE(Int32x4, kI32x4LtU, Uint32LessThan, false) |
| COMPARISON_CASE(Int32x4, kI32x4LeU, Uint32LessThanOrEqual, false) |
| COMPARISON_CASE(Int32x4, kI32x4GtU, Uint32LessThan, true) |
| COMPARISON_CASE(Int32x4, kI32x4GeU, Uint32LessThanOrEqual, true) |
| COMPARISON_CASE(Int16x8, kI16x8Eq, Word32Equal, false) |
| COMPARISON_CASE(Int16x8, kI16x8LtS, Int32LessThan, false) |
| COMPARISON_CASE(Int16x8, kI16x8LeS, Int32LessThanOrEqual, false) |
| COMPARISON_CASE(Int16x8, kI16x8GtS, Int32LessThan, true) |
| COMPARISON_CASE(Int16x8, kI16x8GeS, Int32LessThanOrEqual, true) |
| COMPARISON_CASE(Int16x8, kI16x8LtU, Uint32LessThan, false) |
| COMPARISON_CASE(Int16x8, kI16x8LeU, Uint32LessThanOrEqual, false) |
| COMPARISON_CASE(Int16x8, kI16x8GtU, Uint32LessThan, true) |
| COMPARISON_CASE(Int16x8, kI16x8GeU, Uint32LessThanOrEqual, true) |
| COMPARISON_CASE(Int8x16, kI8x16Eq, Word32Equal, false) |
| COMPARISON_CASE(Int8x16, kI8x16LtS, Int32LessThan, false) |
| COMPARISON_CASE(Int8x16, kI8x16LeS, Int32LessThanOrEqual, false) |
| COMPARISON_CASE(Int8x16, kI8x16GtS, Int32LessThan, true) |
| COMPARISON_CASE(Int8x16, kI8x16GeS, Int32LessThanOrEqual, true) |
| COMPARISON_CASE(Int8x16, kI8x16LtU, Uint32LessThan, false) |
| COMPARISON_CASE(Int8x16, kI8x16LeU, Uint32LessThanOrEqual, false) |
| COMPARISON_CASE(Int8x16, kI8x16GtU, Uint32LessThan, true) |
| COMPARISON_CASE(Int8x16, kI8x16GeU, Uint32LessThanOrEqual, true) |
| #undef COMPARISON_CASE |
| case IrOpcode::kF64x2Ne: { |
| LowerNotEqual(node, SimdType::kFloat64x2, machine()->Float64Equal()); |
| break; |
| } |
| case IrOpcode::kF32x4Ne: { |
| LowerNotEqual(node, SimdType::kFloat32x4, machine()->Float32Equal()); |
| break; |
| } |
| case IrOpcode::kI32x4Ne: { |
| LowerNotEqual(node, SimdType::kInt32x4, machine()->Word32Equal()); |
| break; |
| } |
| case IrOpcode::kI16x8Ne: { |
| LowerNotEqual(node, SimdType::kInt16x8, machine()->Word32Equal()); |
| break; |
| } |
| case IrOpcode::kI8x16Ne: { |
| LowerNotEqual(node, SimdType::kInt8x16, machine()->Word32Equal()); |
| break; |
| } |
| case IrOpcode::kS128Select: { |
| DCHECK_EQ(3, node->InputCount()); |
| DCHECK(ReplacementType(node->InputAt(0)) == SimdType::kInt32x4 || |
| ReplacementType(node->InputAt(0)) == SimdType::kInt16x8 || |
| ReplacementType(node->InputAt(0)) == SimdType::kInt8x16); |
| Node** boolean_input = |
| GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(1), rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(2), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* tmp1 = |
| graph()->NewNode(machine()->Word32Xor(), rep_left[i], rep_right[i]); |
| Node* tmp2 = |
| graph()->NewNode(machine()->Word32And(), boolean_input[i], tmp1); |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Xor(), rep_right[i], tmp2); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| case IrOpcode::kI8x16Swizzle: { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** indices = GetReplacementsWithType(node->InputAt(1), rep_type); |
| Node** rep_nodes = zone()->NewArray<Node*>(num_lanes); |
| Node* stack_slot = graph()->NewNode( |
| machine()->StackSlot(MachineRepresentation::kSimd128)); |
| |
| // Push all num_lanes values into stack slot. |
| const Operator* store_op = machine()->Store( |
| StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier)); |
| Node* effect_input = graph()->start(); |
| for (int i = num_lanes - 1; i >= 0; i--) { |
| // We want all the stores to happen first before any of the loads |
| // below, so connect them via effect edge from i-1 to i. |
| Node* store = |
| graph()->NewNode(store_op, stack_slot, mcgraph_->Int32Constant(i), |
| rep_left[i], effect_input, graph()->start()); |
| effect_input = store; |
| } |
| |
| for (int i = num_lanes - 1; i >= 0; i--) { |
| // Only select lane when index is < num_lanes, otherwise write 0 to |
| // lane. Use Uint32 to take care of negative indices. |
| Diamond d(graph(), common(), |
| graph()->NewNode(machine()->Uint32LessThan(), indices[i], |
| mcgraph_->Int32Constant(num_lanes))); |
| |
| Node* load = |
| graph()->NewNode(machine()->Load(LoadRepresentation::Uint8()), |
| stack_slot, indices[i], effect_input, d.if_true); |
| |
| rep_nodes[i] = d.Phi(MachineRepresentation::kWord8, load, |
| mcgraph_->Int32Constant(0)); |
| } |
| |
| ReplaceNode(node, rep_nodes, num_lanes); |
| break; |
| } |
| case IrOpcode::kI8x16Shuffle: { |
| DCHECK_EQ(2, node->InputCount()); |
| S128ImmediateParameter shuffle = S128ImmediateParameterOf(node->op()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(16); |
| for (int i = 0; i < 16; i++) { |
| int lane = shuffle[i]; |
| rep_node[i] = lane < 16 ? rep_left[lane] : rep_right[lane - 16]; |
| } |
| ReplaceNode(node, rep_node, 16); |
| break; |
| } |
| case IrOpcode::kV32x4AnyTrue: |
| case IrOpcode::kV16x8AnyTrue: |
| case IrOpcode::kV8x16AnyTrue: { |
| DCHECK_EQ(1, node->InputCount()); |
| // AnyTrue always returns a I32x4, and can work with inputs of any shape, |
| // but we still need GetReplacementsWithType if input is float. |
| DCHECK_EQ(ReplacementType(node), SimdType::kInt32x4); |
| Node** reps = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(1); |
| Node* true_node = mcgraph_->Int32Constant(1); |
| Node* zero = mcgraph_->Int32Constant(0); |
| Node* tmp_result = zero; |
| for (int i = 0; i < num_lanes; ++i) { |
| Diamond d(graph(), common(), |
| graph()->NewNode(machine()->Word32Equal(), reps[i], zero)); |
| tmp_result = |
| d.Phi(MachineRepresentation::kWord32, tmp_result, true_node); |
| } |
| rep_node[0] = tmp_result; |
| ReplaceNode(node, rep_node, 1); |
| break; |
| } |
| case IrOpcode::kV32x4AllTrue: { |
| LowerAllTrueOp(node, SimdType::kInt32x4); |
| break; |
| } |
| case IrOpcode::kV16x8AllTrue: { |
| LowerAllTrueOp(node, SimdType::kInt16x8); |
| break; |
| } |
| case IrOpcode::kV8x16AllTrue: { |
| LowerAllTrueOp(node, SimdType::kInt8x16); |
| break; |
| } |
| case IrOpcode::kI8x16BitMask: { |
| LowerBitMaskOp(node, rep_type, 7); |
| break; |
| } |
| case IrOpcode::kI16x8BitMask: { |
| LowerBitMaskOp(node, rep_type, 15); |
| break; |
| } |
| case IrOpcode::kI32x4BitMask: { |
| LowerBitMaskOp(node, rep_type, 31); |
| break; |
| } |
| case IrOpcode::kI8x16RoundingAverageU: |
| case IrOpcode::kI16x8RoundingAverageU: { |
| DCHECK_EQ(2, node->InputCount()); |
| Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type); |
| Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type); |
| int num_lanes = NumLanes(rep_type); |
| Node** rep_node = zone()->NewArray<Node*>(num_lanes); |
| // Nodes are stored signed, so mask away the top bits. |
| // rounding_average(left, right) = (left + right + 1) >> 1 |
| const int bit_mask = num_lanes == 16 ? kMask8 : kMask16; |
| for (int i = 0; i < num_lanes; ++i) { |
| Node* mask_left = graph()->NewNode(machine()->Word32And(), rep_left[i], |
| mcgraph_->Int32Constant(bit_mask)); |
| Node* mask_right = |
| graph()->NewNode(machine()->Word32And(), rep_right[i], |
| mcgraph_->Int32Constant(bit_mask)); |
| Node* left_plus_right_plus_one = graph()->NewNode( |
| machine()->Int32Add(), |
| graph()->NewNode(machine()->Int32Add(), mask_left, mask_right), |
| mcgraph_->Int32Constant(1)); |
| rep_node[i] = |
| graph()->NewNode(machine()->Word32Shr(), left_plus_right_plus_one, |
| mcgraph_->Int32Constant(1)); |
| } |
| ReplaceNode(node, rep_node, num_lanes); |
| break; |
| } |
| default: { |
| DefaultLowering(node); |
| } |
| } |
| } |
| |
| bool SimdScalarLowering::DefaultLowering(Node* node) { |
| bool something_changed = false; |
| for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) { |
| Node* input = node->InputAt(i); |
| if (HasReplacement(0, input)) { |
| something_changed = true; |
| node->ReplaceInput(i, GetReplacements(input)[0]); |
| } |
| if (ReplacementCount(input) > 1 && HasReplacement(1, input)) { |
| something_changed = true; |
| for (int j = 1; j < ReplacementCount(input); ++j) { |
| node->InsertInput(zone(), i + j, GetReplacements(input)[j]); |
| } |
| } |
| } |
| return something_changed; |
| } |
| |
| void SimdScalarLowering::ReplaceNode(Node* old, Node** new_nodes, int count) { |
| replacements_[old->id()].node = zone()->NewArray<Node*>(count); |
| for (int i = 0; i < count; ++i) { |
| replacements_[old->id()].node[i] = new_nodes[i]; |
| } |
| replacements_[old->id()].num_replacements = count; |
| } |
| |
| bool SimdScalarLowering::HasReplacement(size_t index, Node* node) { |
| return replacements_[node->id()].node != nullptr && |
| replacements_[node->id()].node[index] != nullptr; |
| } |
| |
| SimdScalarLowering::SimdType SimdScalarLowering::ReplacementType(Node* node) { |
| return replacements_[node->id()].type; |
| } |
| |
| Node** SimdScalarLowering::GetReplacements(Node* node) { |
| Node** result = replacements_[node->id()].node; |
| DCHECK(result); |
| return result; |
| } |
| |
| int SimdScalarLowering::ReplacementCount(Node* node) { |
| return replacements_[node->id()].num_replacements; |
| } |
| |
| void SimdScalarLowering::Int32ToFloat32(Node** replacements, Node** result) { |
| for (int i = 0; i < kNumLanes32; ++i) { |
| if (replacements[i] != nullptr) { |
| result[i] = |
| graph()->NewNode(machine()->BitcastInt32ToFloat32(), replacements[i]); |
| } else { |
| result[i] = nullptr; |
| } |
| } |
| } |
| |
| void SimdScalarLowering::Int64ToFloat64(Node** replacements, Node** result) { |
| for (int i = 0; i < kNumLanes64; ++i) { |
| if (replacements[i] != nullptr) { |
| result[i] = |
| graph()->NewNode(machine()->BitcastInt64ToFloat64(), replacements[i]); |
| } else { |
| result[i] = nullptr; |
| } |
| } |
| } |
| |
| void SimdScalarLowering::Float64ToInt64(Node** replacements, Node** result) { |
| for (int i = 0; i < kNumLanes64; ++i) { |
| if (replacements[i] != nullptr) { |
| result[i] = |
| graph()->NewNode(machine()->BitcastFloat64ToInt64(), replacements[i]); |
| } else { |
| result[i] = nullptr; |
| } |
| } |
| } |
| |
| void SimdScalarLowering::Float32ToInt32(Node** replacements, Node** result) { |
| for (int i = 0; i < kNumLanes32; ++i) { |
| if (replacements[i] != nullptr) { |
| result[i] = |
| graph()->NewNode(machine()->BitcastFloat32ToInt32(), replacements[i]); |
| } else { |
| result[i] = nullptr; |
| } |
| } |
| } |
| |
| void SimdScalarLowering::Int64ToInt32(Node** replacements, Node** result) { |
| const int num_ints = sizeof(int64_t) / sizeof(int32_t); |
| const int bit_size = sizeof(int32_t) * 8; |
| const Operator* truncate = machine()->TruncateInt64ToInt32(); |
| |
| for (int i = 0; i < kNumLanes64; i++) { |
| if (replacements[i] != nullptr) { |
| for (int j = 0; j < num_ints; j++) { |
| result[num_ints * i + j] = graph()->NewNode( |
| truncate, graph()->NewNode(machine()->Word64Sar(), replacements[i], |
| mcgraph_->Int32Constant(j * bit_size))); |
| } |
| } else { |
| for (int j = 0; j < num_ints; j++) { |
| result[num_ints * i + j] = nullptr; |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| void SimdScalarLowering::Int32ToSmallerInt(Node** replacements, Node** result) { |
| const int num_ints = sizeof(int32_t) / sizeof(T); |
| const int bit_size = sizeof(T) * 8; |
| const Operator* sign_extend; |
| switch (sizeof(T)) { |
| case 1: |
| sign_extend = machine()->SignExtendWord8ToInt32(); |
| break; |
| case 2: |
| sign_extend = machine()->SignExtendWord16ToInt32(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| for (int i = 0; i < kNumLanes32; i++) { |
| if (replacements[i] != nullptr) { |
| for (int j = 0; j < num_ints; j++) { |
| result[num_ints * i + j] = graph()->NewNode( |
| sign_extend, |
| graph()->NewNode(machine()->Word32Shr(), replacements[i], |
| mcgraph_->Int32Constant(j * bit_size))); |
| } |
| } else { |
| for (int j = 0; j < num_ints; j++) { |
| result[num_ints * i + j] = nullptr; |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| void SimdScalarLowering::SmallerIntToInt32(Node** replacements, Node** result) { |
| const int num_ints = sizeof(int32_t) / sizeof(T); |
| const int bit_size = sizeof(T) * 8; |
| const int bit_mask = (1 << bit_size) - 1; |
| |
| for (int i = 0; i < kNumLanes32; ++i) { |
| result[i] = mcgraph_->Int32Constant(0); |
| for (int j = 0; j < num_ints; j++) { |
| if (replacements[num_ints * i + j] != nullptr) { |
| Node* clean_bits = graph()->NewNode(machine()->Word32And(), |
| replacements[num_ints * i + j], |
| mcgraph_->Int32Constant(bit_mask)); |
| Node* shift = graph()->NewNode(machine()->Word32Shl(), clean_bits, |
| mcgraph_->Int32Constant(j * bit_size)); |
| result[i] = graph()->NewNode(machine()->Word32Or(), result[i], shift); |
| } |
| } |
| } |
| } |
| |
| void SimdScalarLowering::Int32ToInt64(Node** replacements, Node** result) { |
| const int num_ints = sizeof(int64_t) / sizeof(int32_t); |
| |
| for (int i = 0; i < kNumLanes64; i++) { |
| Node* i64 = graph()->NewNode(machine()->ChangeUint32ToUint64(), |
| replacements[num_ints * i + 1]); |
| Node* high = graph()->NewNode(machine()->Word64Shl(), i64, |
| mcgraph_->Int32Constant(32)); |
| Node* i64_low = graph()->NewNode(machine()->ChangeUint32ToUint64(), |
| replacements[num_ints * i]); |
| result[i] = graph()->NewNode(machine()->Word64Or(), high, i64_low); |
| } |
| } |
| |
| Node** SimdScalarLowering::GetReplacementsWithType(Node* node, SimdType type) { |
| // Operations like extract lane, bitmask, any_true, all_true replaces a SIMD |
| // node with a scalar. Those won't be correctly handled here. They should be |
| // special cased and replaced with the appropriate scalar. |
| DCHECK_LT(1, ReplacementCount(node)); |
| |
| Node** replacements = GetReplacements(node); |
| if (type == ReplacementType(node)) { |
| return replacements; |
| } |
| |
| int num_lanes = NumLanes(type); |
| Node** result = zone()->NewArray<Node*>(num_lanes); |
| |
| switch (type) { |
| case SimdType::kInt64x2: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| UNREACHABLE(); |
| } |
| case SimdType::kInt32x4: { |
| Int32ToInt64(replacements, result); |
| break; |
| } |
| case SimdType::kInt16x8: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| SmallerIntToInt32<int16_t>(replacements, to_int32); |
| Int32ToInt64(to_int32, result); |
| break; |
| } |
| case SimdType::kInt8x16: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| SmallerIntToInt32<int8_t>(replacements, to_int32); |
| Int32ToInt64(to_int32, result); |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| Float64ToInt64(replacements, result); |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float32ToInt32(replacements, to_int32); |
| Int32ToInt64(to_int32, result); |
| break; |
| } |
| } |
| break; |
| } |
| case SimdType::kInt32x4: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| Int64ToInt32(replacements, result); |
| break; |
| } |
| case SimdType::kInt32x4: { |
| UNREACHABLE(); |
| } |
| case SimdType::kInt16x8: { |
| SmallerIntToInt32<int16_t>(replacements, result); |
| break; |
| } |
| case SimdType::kInt8x16: { |
| SmallerIntToInt32<int8_t>(replacements, result); |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| Node** float64_to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Float64ToInt64(replacements, float64_to_int64); |
| Int64ToInt32(float64_to_int64, result); |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| Float32ToInt32(replacements, result); |
| break; |
| } |
| } |
| break; |
| } |
| case SimdType::kInt16x8: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Int64ToInt32(replacements, to_int32); |
| Int32ToSmallerInt<int16_t>(to_int32, result); |
| break; |
| } |
| case SimdType::kInt32x4: { |
| Int32ToSmallerInt<int16_t>(replacements, result); |
| break; |
| } |
| case SimdType::kInt16x8: { |
| UNREACHABLE(); |
| } |
| case SimdType::kInt8x16: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| SmallerIntToInt32<int8_t>(replacements, to_int32); |
| Int32ToSmallerInt<int16_t>(to_int32, result); |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| Node** to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float64ToInt64(replacements, to_int64); |
| Int64ToInt32(to_int64, to_int32); |
| Int32ToSmallerInt<int16_t>(to_int32, result); |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| Node** float32_to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float32ToInt32(replacements, float32_to_int32); |
| Int32ToSmallerInt<int16_t>(float32_to_int32, result); |
| break; |
| } |
| } |
| break; |
| } |
| case SimdType::kInt8x16: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| Node** int64_to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Int64ToInt32(replacements, int64_to_int32); |
| Int32ToSmallerInt<int8_t>(int64_to_int32, result); |
| break; |
| } |
| case SimdType::kInt32x4: { |
| Int32ToSmallerInt<int8_t>(replacements, result); |
| break; |
| } |
| case SimdType::kInt16x8: { |
| Node** int16_to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| SmallerIntToInt32<int16_t>(replacements, int16_to_int32); |
| Int32ToSmallerInt<int8_t>(int16_to_int32, result); |
| break; |
| } |
| case SimdType::kInt8x16: { |
| UNREACHABLE(); |
| } |
| case SimdType::kFloat64x2: { |
| Node** to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float64ToInt64(replacements, to_int64); |
| Int64ToInt32(to_int64, to_int32); |
| Int32ToSmallerInt<int8_t>(to_int32, result); |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| Node** float32_to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float32ToInt32(replacements, float32_to_int32); |
| Int32ToSmallerInt<int8_t>(float32_to_int32, result); |
| break; |
| } |
| } |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| Int64ToFloat64(replacements, result); |
| break; |
| } |
| case SimdType::kInt32x4: { |
| Node** int32_to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Int32ToInt64(replacements, int32_to_int64); |
| Int64ToFloat64(int32_to_int64, result); |
| break; |
| } |
| case SimdType::kInt16x8: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Node** to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| SmallerIntToInt32<int16_t>(replacements, to_int32); |
| Int32ToInt64(to_int32, to_int64); |
| Int64ToFloat64(to_int64, result); |
| break; |
| } |
| case SimdType::kInt8x16: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Node** to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| SmallerIntToInt32<int8_t>(replacements, to_int32); |
| Int32ToInt64(to_int32, to_int64); |
| Int64ToFloat64(to_int64, result); |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| UNREACHABLE(); |
| } |
| case SimdType::kFloat32x4: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Node** to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Float32ToInt32(replacements, to_int32); |
| Int32ToInt64(to_int32, to_int64); |
| Int64ToFloat64(to_int64, result); |
| break; |
| } |
| } |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| switch (ReplacementType(node)) { |
| case SimdType::kInt64x2: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Int64ToInt32(replacements, to_int32); |
| Int32ToFloat32(to_int32, result); |
| break; |
| } |
| case SimdType::kInt32x4: { |
| Int32ToFloat32(replacements, result); |
| break; |
| } |
| case SimdType::kInt16x8: { |
| Node** to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| SmallerIntToInt32<int16_t>(replacements, to_int32); |
| Int32ToFloat32(to_int32, result); |
| break; |
| } |
| case SimdType::kInt8x16: { |
| SmallerIntToInt32<int8_t>(replacements, result); |
| Int32ToFloat32(result, result); |
| break; |
| } |
| case SimdType::kFloat64x2: { |
| Node** float64_to_int64 = zone()->NewArray<Node*>(kNumLanes64); |
| Node** int64_to_int32 = zone()->NewArray<Node*>(kNumLanes32); |
| Float64ToInt64(replacements, float64_to_int64); |
| Int64ToInt32(float64_to_int64, int64_to_int32); |
| Int32ToFloat32(int64_to_int32, result); |
| break; |
| } |
| case SimdType::kFloat32x4: { |
| UNREACHABLE(); |
| } |
| } |
| break; |
| } |
| } |
| return result; |
| } |
| |
| void SimdScalarLowering::PreparePhiReplacement(Node* phi) { |
| MachineRepresentation rep = PhiRepresentationOf(phi->op()); |
| if (rep == MachineRepresentation::kSimd128) { |
| // We have to create the replacements for a phi node before we actually |
| // lower the phi to break potential cycles in the graph. The replacements of |
| // input nodes do not exist yet, so we use a placeholder node to pass the |
| // graph verifier. |
| int value_count = phi->op()->ValueInputCount(); |
| SimdType type = ReplacementType(phi); |
| int num_lanes = NumLanes(type); |
| Node*** inputs_rep = zone()->NewArray<Node**>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| inputs_rep[i] = zone()->NewArray<Node*>(value_count + 1); |
| inputs_rep[i][value_count] = NodeProperties::GetControlInput(phi, 0); |
| } |
| for (int i = 0; i < value_count; ++i) { |
| for (int j = 0; j < num_lanes; ++j) { |
| inputs_rep[j][i] = placeholder_; |
| } |
| } |
| Node** rep_nodes = zone()->NewArray<Node*>(num_lanes); |
| for (int i = 0; i < num_lanes; ++i) { |
| rep_nodes[i] = graph()->NewNode( |
| common()->Phi(MachineTypeFrom(type).representation(), value_count), |
| value_count + 1, inputs_rep[i], false); |
| } |
| ReplaceNode(phi, rep_nodes, num_lanes); |
| } |
| } |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |