[wasm] simd scalar lowering F32x4Add and I32x4Add
BUG=v8:4124 TEST:test-run-wasm-simd-lowering R=titzer@chromium.org,bradnelson@chromium.org,gdeepti@chromium.org Review-Url: https://chromiumcodereview.appspot.com/2294743003 Cr-Commit-Position: refs/heads/master@{#40448}
This commit is contained in:
parent
71e390faf6
commit
cf9ee0ec6c
2
BUILD.gn
2
BUILD.gn
@ -1170,6 +1170,8 @@ v8_source_set("v8_base") {
|
||||
"src/compiler/scheduler.h",
|
||||
"src/compiler/select-lowering.cc",
|
||||
"src/compiler/select-lowering.h",
|
||||
"src/compiler/simd-scalar-lowering.cc",
|
||||
"src/compiler/simd-scalar-lowering.h",
|
||||
"src/compiler/simplified-lowering.cc",
|
||||
"src/compiler/simplified-lowering.h",
|
||||
"src/compiler/simplified-operator-reducer.cc",
|
||||
|
409
src/compiler/simd-scalar-lowering.cc
Normal file
409
src/compiler/simd-scalar-lowering.cc
Normal file
@ -0,0 +1,409 @@
|
||||
// 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/compiler/diamond.h"
|
||||
#include "src/compiler/linkage.h"
|
||||
#include "src/compiler/node-matchers.h"
|
||||
#include "src/compiler/node-properties.h"
|
||||
|
||||
#include "src/compiler/node.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
SimdScalarLowering::SimdScalarLowering(
|
||||
Graph* graph, MachineOperatorBuilder* machine,
|
||||
CommonOperatorBuilder* common, Zone* zone,
|
||||
Signature<MachineRepresentation>* signature)
|
||||
: zone_(zone),
|
||||
graph_(graph),
|
||||
machine_(machine),
|
||||
common_(common),
|
||||
state_(graph, 3),
|
||||
stack_(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(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::kInt32;
|
||||
|
||||
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 {
|
||||
stack_.push_back({input, 0});
|
||||
}
|
||||
state_.Set(input, State::kOnStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FOREACH_INT32X4_OPCODE(V) \
|
||||
V(Int32x4Add) \
|
||||
V(Int32x4ExtractLane) \
|
||||
V(CreateInt32x4)
|
||||
|
||||
#define FOREACH_FLOAT32X4_OPCODE(V) \
|
||||
V(Float32x4Add) \
|
||||
V(Float32x4ExtractLane) \
|
||||
V(CreateFloat32x4)
|
||||
|
||||
void SimdScalarLowering::SetLoweredType(Node* node, Node* output) {
|
||||
switch (node->opcode()) {
|
||||
#define CASE_STMT(name) case IrOpcode::k##name:
|
||||
FOREACH_INT32X4_OPCODE(CASE_STMT)
|
||||
case IrOpcode::kReturn:
|
||||
case IrOpcode::kParameter:
|
||||
case IrOpcode::kCall: {
|
||||
replacements_[node->id()].type = SimdType::kInt32;
|
||||
break;
|
||||
}
|
||||
FOREACH_FLOAT32X4_OPCODE(CASE_STMT) {
|
||||
replacements_[node->id()].type = SimdType::kFloat32;
|
||||
break;
|
||||
}
|
||||
#undef CASE_STMT
|
||||
default:
|
||||
replacements_[node->id()].type = replacements_[output->id()].type;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetParameterIndexAfterLowering(
|
||||
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) {
|
||||
// GetParameterIndexAfterLowering(parameter_count) returns the parameter
|
||||
// count after lowering.
|
||||
parameter_count_after_lowering_ = GetParameterIndexAfterLowering(
|
||||
signature(), static_cast<int>(signature()->parameter_count()));
|
||||
}
|
||||
return parameter_count_after_lowering_;
|
||||
}
|
||||
|
||||
static int GetReturnCountAfterLowering(
|
||||
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;
|
||||
}
|
||||
|
||||
void SimdScalarLowering::LowerNode(Node* node) {
|
||||
SimdType rep_type = ReplacementType(node);
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kStart: {
|
||||
int parameter_count = GetParameterCountAfterLowering();
|
||||
// Only exchange the node if the parameter count actually changed.
|
||||
if (parameter_count != 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(node->InputCount() == 1);
|
||||
// Only exchange the node if the parameter count actually changed. We do
|
||||
// not even have to do the default lowering because the the start node,
|
||||
// the only input of a parameter node, only changes if the parameter count
|
||||
// changes.
|
||||
if (GetParameterCountAfterLowering() != signature()->parameter_count()) {
|
||||
int old_index = ParameterIndexOf(node->op());
|
||||
int new_index = GetParameterIndexAfterLowering(signature(), old_index);
|
||||
if (old_index == new_index) {
|
||||
NodeProperties::ChangeOp(node, common()->Parameter(new_index));
|
||||
|
||||
Node* new_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
new_node[i] = nullptr;
|
||||
}
|
||||
new_node[0] = node;
|
||||
if (signature()->GetParam(old_index) ==
|
||||
MachineRepresentation::kSimd128) {
|
||||
for (int i = 1; i < kMaxLanes; i++) {
|
||||
new_node[i] = graph()->NewNode(common()->Parameter(new_index + i),
|
||||
graph()->start());
|
||||
}
|
||||
}
|
||||
ReplaceNode(node, new_node);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kReturn: {
|
||||
DefaultLowering(node);
|
||||
int new_return_count = GetReturnCountAfterLowering(signature());
|
||||
if (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.
|
||||
CallDescriptor* descriptor =
|
||||
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
|
||||
if (DefaultLowering(node) ||
|
||||
(descriptor->ReturnCount() == 1 &&
|
||||
descriptor->GetReturnType(0) == MachineType::Simd128())) {
|
||||
// We have to adjust the call descriptor.
|
||||
const Operator* op =
|
||||
common()->Call(wasm::ModuleEnv::GetI32WasmCallDescriptorForSimd(
|
||||
zone(), descriptor));
|
||||
NodeProperties::ChangeOp(node, op);
|
||||
}
|
||||
if (descriptor->ReturnCount() == 1 &&
|
||||
descriptor->GetReturnType(0) == MachineType::Simd128()) {
|
||||
// We access the additional return values through projections.
|
||||
Node* rep_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
rep_node[i] =
|
||||
graph()->NewNode(common()->Projection(i), node, graph()->start());
|
||||
}
|
||||
ReplaceNode(node, rep_node);
|
||||
}
|
||||
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 < kMaxLanes; j++) {
|
||||
rep_node[j]->ReplaceInput(i, rep_input[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DefaultLowering(node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kInt32x4Add: {
|
||||
DCHECK(node->InputCount() == 2);
|
||||
Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
|
||||
Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
|
||||
Node* rep_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
rep_node[i] =
|
||||
graph()->NewNode(machine()->Int32Add(), rep_left[i], rep_right[i]);
|
||||
}
|
||||
ReplaceNode(node, rep_node);
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kCreateInt32x4: {
|
||||
Node* rep_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
DCHECK(!HasReplacement(1, node->InputAt(i)));
|
||||
rep_node[i] = node->InputAt(i);
|
||||
}
|
||||
ReplaceNode(node, rep_node);
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kInt32x4ExtractLane: {
|
||||
Node* laneNode = node->InputAt(1);
|
||||
DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant);
|
||||
int32_t lane = OpParameter<int32_t>(laneNode);
|
||||
Node* rep_node[kMaxLanes] = {
|
||||
GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr,
|
||||
nullptr, nullptr};
|
||||
ReplaceNode(node, rep_node);
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kFloat32x4Add: {
|
||||
DCHECK(node->InputCount() == 2);
|
||||
Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
|
||||
Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
|
||||
Node* rep_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
rep_node[i] = graph()->NewNode(machine()->Float32Add(), rep_left[i],
|
||||
rep_right[i]);
|
||||
}
|
||||
ReplaceNode(node, rep_node);
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kCreateFloat32x4: {
|
||||
Node* rep_node[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
DCHECK(!HasReplacement(1, node->InputAt(i)));
|
||||
rep_node[i] = node->InputAt(i);
|
||||
}
|
||||
ReplaceNode(node, rep_node);
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kFloat32x4ExtractLane: {
|
||||
Node* laneNode = node->InputAt(1);
|
||||
DCHECK_EQ(laneNode->opcode(), IrOpcode::kInt32Constant);
|
||||
int32_t lane = OpParameter<int32_t>(laneNode);
|
||||
Node* rep_node[kMaxLanes] = {
|
||||
GetReplacementsWithType(node->InputAt(0), rep_type)[lane], nullptr,
|
||||
nullptr, nullptr};
|
||||
ReplaceNode(node, rep_node);
|
||||
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 (HasReplacement(1, input)) {
|
||||
something_changed = true;
|
||||
for (int j = 1; j < kMaxLanes; j++) {
|
||||
node->InsertInput(zone(), i + j, GetReplacements(input)[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return something_changed;
|
||||
}
|
||||
|
||||
void SimdScalarLowering::ReplaceNode(Node* old, Node** new_node) {
|
||||
// if new_low == nullptr, then also new_high == nullptr.
|
||||
DCHECK(new_node[0] != nullptr ||
|
||||
(new_node[1] == nullptr && new_node[2] == nullptr &&
|
||||
new_node[3] == nullptr));
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
replacements_[old->id()].node[i] = new_node[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool SimdScalarLowering::HasReplacement(size_t index, Node* node) {
|
||||
return 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;
|
||||
}
|
||||
|
||||
Node** SimdScalarLowering::GetReplacementsWithType(Node* node, SimdType type) {
|
||||
Node** replacements = GetReplacements(node);
|
||||
if (ReplacementType(node) == type) {
|
||||
return GetReplacements(node);
|
||||
}
|
||||
Node** result = zone()->NewArray<Node*>(kMaxLanes);
|
||||
if (ReplacementType(node) == SimdType::kInt32 && type == SimdType::kFloat32) {
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
if (replacements[i] != nullptr) {
|
||||
result[i] = graph()->NewNode(machine()->BitcastInt32ToFloat32(),
|
||||
replacements[i]);
|
||||
} else {
|
||||
result[i] = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
if (replacements[i] != nullptr) {
|
||||
result[i] = graph()->NewNode(machine()->BitcastFloat32ToInt32(),
|
||||
replacements[i]);
|
||||
} else {
|
||||
result[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
Node** inputs_rep[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; 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 < kMaxLanes; j++) {
|
||||
inputs_rep[j][i] = placeholder_;
|
||||
}
|
||||
}
|
||||
Node* rep_nodes[kMaxLanes];
|
||||
for (int i = 0; i < kMaxLanes; i++) {
|
||||
if (type == SimdType::kInt32) {
|
||||
rep_nodes[i] = graph()->NewNode(
|
||||
common()->Phi(MachineRepresentation::kWord32, value_count),
|
||||
value_count + 1, inputs_rep[i], false);
|
||||
} else if (type == SimdType::kFloat32) {
|
||||
rep_nodes[i] = graph()->NewNode(
|
||||
common()->Phi(MachineRepresentation::kFloat32, value_count),
|
||||
value_count + 1, inputs_rep[i], false);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
ReplaceNode(phi, rep_nodes);
|
||||
}
|
||||
}
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
78
src/compiler/simd-scalar-lowering.h
Normal file
78
src/compiler/simd-scalar-lowering.h
Normal file
@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
#ifndef V8_COMPILER_SIMD_SCALAR_LOWERING_H_
|
||||
#define V8_COMPILER_SIMD_SCALAR_LOWERING_H_
|
||||
|
||||
#include "src/compiler/common-operator.h"
|
||||
#include "src/compiler/graph.h"
|
||||
#include "src/compiler/machine-operator.h"
|
||||
#include "src/compiler/node-marker.h"
|
||||
#include "src/zone/zone-containers.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
class SimdScalarLowering {
|
||||
public:
|
||||
SimdScalarLowering(Graph* graph, MachineOperatorBuilder* machine,
|
||||
CommonOperatorBuilder* common, Zone* zone,
|
||||
Signature<MachineRepresentation>* signature);
|
||||
|
||||
void LowerGraph();
|
||||
|
||||
int GetParameterCountAfterLowering();
|
||||
|
||||
private:
|
||||
enum class State : uint8_t { kUnvisited, kOnStack, kVisited };
|
||||
|
||||
enum class SimdType : uint8_t { kInt32, kFloat32 };
|
||||
|
||||
static const size_t kMaxLanes = 4;
|
||||
|
||||
struct Replacement {
|
||||
Node* node[kMaxLanes];
|
||||
SimdType type; // represents what input type is expected
|
||||
};
|
||||
|
||||
Zone* zone() const { return zone_; }
|
||||
Graph* graph() const { return graph_; }
|
||||
MachineOperatorBuilder* machine() const { return machine_; }
|
||||
CommonOperatorBuilder* common() const { return common_; }
|
||||
Signature<MachineRepresentation>* signature() const { return signature_; }
|
||||
|
||||
void LowerNode(Node* node);
|
||||
bool DefaultLowering(Node* node);
|
||||
|
||||
void ReplaceNode(Node* old, Node** new_nodes);
|
||||
bool HasReplacement(size_t index, Node* node);
|
||||
Node** GetReplacements(Node* node);
|
||||
Node** GetReplacementsWithType(Node* node, SimdType type);
|
||||
SimdType ReplacementType(Node* node);
|
||||
void PreparePhiReplacement(Node* phi);
|
||||
void SetLoweredType(Node* node, Node* output);
|
||||
|
||||
struct NodeState {
|
||||
Node* node;
|
||||
int input_index;
|
||||
};
|
||||
|
||||
Zone* zone_;
|
||||
Graph* const graph_;
|
||||
MachineOperatorBuilder* machine_;
|
||||
CommonOperatorBuilder* common_;
|
||||
NodeMarker<State> state_;
|
||||
ZoneDeque<NodeState> stack_;
|
||||
Replacement* replacements_;
|
||||
Signature<MachineRepresentation>* signature_;
|
||||
Node* placeholder_;
|
||||
int parameter_count_after_lowering_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_SIMD_SCALAR_LOWERING_H_
|
@ -24,6 +24,7 @@
|
||||
#include "src/compiler/machine-operator.h"
|
||||
#include "src/compiler/node-matchers.h"
|
||||
#include "src/compiler/pipeline.h"
|
||||
#include "src/compiler/simd-scalar-lowering.h"
|
||||
#include "src/compiler/source-position.h"
|
||||
#include "src/compiler/zone-stats.h"
|
||||
|
||||
@ -3046,6 +3047,13 @@ void WasmGraphBuilder::Int64LoweringForTesting() {
|
||||
}
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::SimdScalarLoweringForTesting() {
|
||||
SimdScalarLowering(jsgraph()->graph(), jsgraph()->machine(),
|
||||
jsgraph()->common(), jsgraph()->zone(),
|
||||
function_signature_)
|
||||
.LowerGraph();
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::SetSourcePosition(Node* node,
|
||||
wasm::WasmCodePosition position) {
|
||||
DCHECK_NE(position, wasm::kNoCodePosition);
|
||||
@ -3068,6 +3076,18 @@ Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode,
|
||||
case wasm::kExprI32x4Splat:
|
||||
return graph()->NewNode(jsgraph()->machine()->CreateInt32x4(), inputs[0],
|
||||
inputs[0], inputs[0], inputs[0]);
|
||||
case wasm::kExprI32x4Add:
|
||||
return graph()->NewNode(jsgraph()->machine()->Int32x4Add(), inputs[0],
|
||||
inputs[1]);
|
||||
case wasm::kExprF32x4ExtractLane:
|
||||
return graph()->NewNode(jsgraph()->machine()->Float32x4ExtractLane(),
|
||||
inputs[0], inputs[1]);
|
||||
case wasm::kExprF32x4Splat:
|
||||
return graph()->NewNode(jsgraph()->machine()->CreateFloat32x4(),
|
||||
inputs[0], inputs[0], inputs[0], inputs[0]);
|
||||
case wasm::kExprF32x4Add:
|
||||
return graph()->NewNode(jsgraph()->machine()->Float32x4Add(), inputs[0],
|
||||
inputs[1]);
|
||||
default:
|
||||
return graph()->NewNode(UnsupportedOpcode(opcode), nullptr);
|
||||
}
|
||||
@ -3079,6 +3099,9 @@ Node* WasmGraphBuilder::SimdExtractLane(wasm::WasmOpcode opcode, uint8_t lane,
|
||||
case wasm::kExprI32x4ExtractLane:
|
||||
return graph()->NewNode(jsgraph()->machine()->Int32x4ExtractLane(), input,
|
||||
Int32Constant(lane));
|
||||
case wasm::kExprF32x4ExtractLane:
|
||||
return graph()->NewNode(jsgraph()->machine()->Float32x4ExtractLane(),
|
||||
input, Int32Constant(lane));
|
||||
default:
|
||||
return graph()->NewNode(UnsupportedOpcode(opcode), nullptr);
|
||||
}
|
||||
@ -3294,6 +3317,9 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
|
||||
r.LowerGraph();
|
||||
}
|
||||
|
||||
SimdScalarLowering(graph, machine, common, jsgraph_->zone(), function_->sig)
|
||||
.LowerGraph();
|
||||
|
||||
int index = static_cast<int>(function_->func_index);
|
||||
|
||||
if (index >= FLAG_trace_wasm_ast_start && index < FLAG_trace_wasm_ast_end) {
|
||||
|
@ -200,6 +200,8 @@ class WasmGraphBuilder {
|
||||
|
||||
void Int64LoweringForTesting();
|
||||
|
||||
void SimdScalarLoweringForTesting();
|
||||
|
||||
void SetSourcePosition(Node* node, wasm::WasmCodePosition position);
|
||||
|
||||
Node* CreateS128Value(int32_t value);
|
||||
|
@ -307,26 +307,23 @@ CallDescriptor* ModuleEnv::GetWasmCallDescriptor(Zone* zone,
|
||||
"wasm-call");
|
||||
}
|
||||
|
||||
CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
|
||||
Zone* zone, CallDescriptor* descriptor) {
|
||||
CallDescriptor* ReplaceTypeInCallDescriptorWith(
|
||||
Zone* zone, CallDescriptor* descriptor, size_t num_replacements,
|
||||
MachineType input_type, MachineRepresentation output_type) {
|
||||
size_t parameter_count = descriptor->ParameterCount();
|
||||
size_t return_count = descriptor->ReturnCount();
|
||||
for (size_t i = 0; i < descriptor->ParameterCount(); i++) {
|
||||
if (descriptor->GetParameterType(i) == MachineType::Int64()) {
|
||||
// For each int64 input we get two int32 inputs.
|
||||
parameter_count++;
|
||||
if (descriptor->GetParameterType(i) == input_type) {
|
||||
parameter_count += num_replacements - 1;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
|
||||
if (descriptor->GetReturnType(i) == MachineType::Int64()) {
|
||||
// For each int64 return we get two int32 returns.
|
||||
return_count++;
|
||||
if (descriptor->GetReturnType(i) == input_type) {
|
||||
return_count += num_replacements - 1;
|
||||
}
|
||||
}
|
||||
if (parameter_count == descriptor->ParameterCount() &&
|
||||
return_count == descriptor->ReturnCount()) {
|
||||
// If there is no int64 parameter or return value, we can just return the
|
||||
// original descriptor.
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@ -335,10 +332,10 @@ CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
|
||||
Allocator rets = return_registers.Get();
|
||||
|
||||
for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
|
||||
if (descriptor->GetReturnType(i) == MachineType::Int64()) {
|
||||
// For each int64 return we get two int32 returns.
|
||||
locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
|
||||
locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
|
||||
if (descriptor->GetReturnType(i) == input_type) {
|
||||
for (size_t j = 0; j < num_replacements; j++) {
|
||||
locations.AddReturn(rets.Next(output_type));
|
||||
}
|
||||
} else {
|
||||
locations.AddReturn(
|
||||
rets.Next(descriptor->GetReturnType(i).representation()));
|
||||
@ -348,10 +345,10 @@ CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
|
||||
Allocator params = parameter_registers.Get();
|
||||
|
||||
for (size_t i = 0; i < descriptor->ParameterCount(); i++) {
|
||||
if (descriptor->GetParameterType(i) == MachineType::Int64()) {
|
||||
// For each int64 input we get two int32 inputs.
|
||||
locations.AddParam(params.Next(MachineRepresentation::kWord32));
|
||||
locations.AddParam(params.Next(MachineRepresentation::kWord32));
|
||||
if (descriptor->GetParameterType(i) == input_type) {
|
||||
for (size_t j = 0; j < num_replacements; j++) {
|
||||
locations.AddParam(params.Next(output_type));
|
||||
}
|
||||
} else {
|
||||
locations.AddParam(
|
||||
params.Next(descriptor->GetParameterType(i).representation()));
|
||||
@ -369,8 +366,20 @@ CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
|
||||
descriptor->CalleeSavedFPRegisters(), // callee-saved fp regs
|
||||
descriptor->flags(), // flags
|
||||
descriptor->debug_name());
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
|
||||
Zone* zone, CallDescriptor* descriptor) {
|
||||
return ReplaceTypeInCallDescriptorWith(zone, descriptor, 2,
|
||||
MachineType::Int64(),
|
||||
MachineRepresentation::kWord32);
|
||||
}
|
||||
|
||||
CallDescriptor* ModuleEnv::GetI32WasmCallDescriptorForSimd(
|
||||
Zone* zone, CallDescriptor* descriptor) {
|
||||
return ReplaceTypeInCallDescriptorWith(zone, descriptor, 4,
|
||||
MachineType::Simd128(),
|
||||
MachineRepresentation::kWord32);
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -701,6 +701,8 @@
|
||||
'compiler/scheduler.h',
|
||||
'compiler/select-lowering.cc',
|
||||
'compiler/select-lowering.h',
|
||||
'compiler/simd-scalar-lowering.cc',
|
||||
'compiler/simd-scalar-lowering.h',
|
||||
'compiler/simplified-lowering.cc',
|
||||
'compiler/simplified-lowering.h',
|
||||
'compiler/simplified-operator-reducer.cc',
|
||||
|
@ -150,6 +150,16 @@ struct Control {
|
||||
(build() ? CheckForException(builder_->func(__VA_ARGS__)) : nullptr)
|
||||
#define BUILD0(func) (build() ? CheckForException(builder_->func()) : nullptr)
|
||||
|
||||
struct LaneOperand {
|
||||
uint8_t lane;
|
||||
unsigned length;
|
||||
|
||||
inline LaneOperand(Decoder* decoder, const byte* pc) {
|
||||
lane = decoder->checked_read_u8(pc, 2, "lane");
|
||||
length = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Generic Wasm bytecode decoder with utilities for decoding operands,
|
||||
// lengths, etc.
|
||||
class WasmDecoder : public Decoder {
|
||||
@ -240,8 +250,17 @@ class WasmDecoder : public Decoder {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Validate(const byte* pc, LaneOperand& operand) {
|
||||
if (operand.lane < 0 || operand.lane > 3) {
|
||||
error(pc_, pc_ + 2, "invalid extract lane value");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned OpcodeLength(const byte* pc) {
|
||||
switch (static_cast<WasmOpcode>(*pc)) {
|
||||
switch (static_cast<byte>(*pc)) {
|
||||
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
|
||||
FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
|
||||
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
|
||||
@ -304,6 +323,27 @@ class WasmDecoder : public Decoder {
|
||||
return 5;
|
||||
case kExprF64Const:
|
||||
return 9;
|
||||
case kSimdPrefix: {
|
||||
byte simd_index = *(pc + 1);
|
||||
WasmOpcode opcode =
|
||||
static_cast<WasmOpcode>(kSimdPrefix << 8 | simd_index);
|
||||
switch (opcode) {
|
||||
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
|
||||
FOREACH_SIMD_0_OPERAND_OPCODE(DECLARE_OPCODE_CASE)
|
||||
#undef DECLARE_OPCODE_CASE
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
|
||||
FOREACH_SIMD_1_OPERAND_OPCODE(DECLARE_OPCODE_CASE)
|
||||
#undef DECLARE_OPCODE_CASE
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
@ -1249,18 +1289,25 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
return 1 + operand.length;
|
||||
}
|
||||
|
||||
unsigned ExtractLane(WasmOpcode opcode, LocalType type) {
|
||||
LaneOperand operand(this, pc_);
|
||||
if (Validate(pc_, operand)) {
|
||||
TFNode* input = Pop(0, LocalType::kSimd128).node;
|
||||
TFNode* node = BUILD(SimdExtractLane, opcode, operand.lane, input);
|
||||
Push(type, node);
|
||||
}
|
||||
return operand.length;
|
||||
}
|
||||
|
||||
unsigned DecodeSimdOpcode(WasmOpcode opcode) {
|
||||
unsigned len = 0;
|
||||
switch (opcode) {
|
||||
case kExprI32x4ExtractLane: {
|
||||
uint8_t lane = this->checked_read_u8(pc_, 2, "lane number");
|
||||
if (lane < 0 || lane > 3) {
|
||||
error(pc_, pc_ + 2, "invalid extract lane value");
|
||||
}
|
||||
TFNode* input = Pop(0, LocalType::kSimd128).node;
|
||||
TFNode* node = BUILD(SimdExtractLane, opcode, lane, input);
|
||||
Push(LocalType::kWord32, node);
|
||||
len++;
|
||||
len = ExtractLane(opcode, LocalType::kWord32);
|
||||
break;
|
||||
}
|
||||
case kExprF32x4ExtractLane: {
|
||||
len = ExtractLane(opcode, LocalType::kFloat32);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -614,6 +614,11 @@ class LocalDeclEncoder {
|
||||
#define WASM_SIMD_I32x4_SPLAT(x) x, kSimdPrefix, kExprI32x4Splat & 0xff
|
||||
#define WASM_SIMD_I32x4_EXTRACT_LANE(lane, x) \
|
||||
x, kSimdPrefix, kExprI32x4ExtractLane & 0xff, static_cast<byte>(lane)
|
||||
#define WASM_SIMD_I32x4_ADD(x, y) x, y, kSimdPrefix, kExprI32x4Add & 0xff
|
||||
#define WASM_SIMD_F32x4_SPLAT(x) x, kSimdPrefix, kExprF32x4Splat & 0xff
|
||||
#define WASM_SIMD_F32x4_EXTRACT_LANE(lane, x) \
|
||||
x, kSimdPrefix, kExprF32x4ExtractLane & 0xff, static_cast<byte>(lane)
|
||||
#define WASM_SIMD_F32x4_ADD(x, y) x, y, kSimdPrefix, kExprF32x4Add & 0xff
|
||||
|
||||
#define SIG_ENTRY_v_v kWasmFunctionTypeForm, 0, 0
|
||||
#define SIZEOF_SIG_ENTRY_v_v 3
|
||||
|
@ -333,6 +333,8 @@ struct V8_EXPORT_PRIVATE ModuleEnv {
|
||||
FunctionSig* sig);
|
||||
static compiler::CallDescriptor* GetI32WasmCallDescriptor(
|
||||
Zone* zone, compiler::CallDescriptor* descriptor);
|
||||
static compiler::CallDescriptor* GetI32WasmCallDescriptorForSimd(
|
||||
Zone* zone, compiler::CallDescriptor* descriptor);
|
||||
};
|
||||
|
||||
// A helper for printing out the names of functions.
|
||||
|
@ -277,6 +277,7 @@ v8_executable("cctest") {
|
||||
"test-log-stack-tracer.cc",
|
||||
"test-macro-assembler-x64.cc",
|
||||
"test-run-wasm-relocation-x64.cc",
|
||||
"wasm/test-run-wasm-simd-lowering.cc",
|
||||
"wasm/test-run-wasm-simd.cc",
|
||||
]
|
||||
} else if (v8_current_cpu == "x87") {
|
||||
|
@ -235,7 +235,8 @@
|
||||
'test-macro-assembler-x64.cc',
|
||||
'test-log-stack-tracer.cc',
|
||||
'test-run-wasm-relocation-x64.cc',
|
||||
'wasm/test-run-wasm-simd.cc'
|
||||
'wasm/test-run-wasm-simd.cc',
|
||||
'wasm/test-run-wasm-simd-lowering.cc',
|
||||
],
|
||||
'cctest_sources_arm': [ ### gcmole(arch:arm) ###
|
||||
'test-assembler-arm.cc',
|
||||
|
96
test/cctest/wasm/test-run-wasm-simd-lowering.cc
Normal file
96
test/cctest/wasm/test-run-wasm-simd-lowering.cc
Normal file
@ -0,0 +1,96 @@
|
||||
// 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/wasm/wasm-macro-gen.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
#include "test/cctest/cctest.h"
|
||||
#include "test/cctest/compiler/value-helper.h"
|
||||
#include "test/cctest/wasm/wasm-run-utils.h"
|
||||
#include "test/common/wasm/test-signatures.h"
|
||||
|
||||
using namespace v8::base;
|
||||
using namespace v8::internal;
|
||||
using namespace v8::internal::compiler;
|
||||
using namespace v8::internal::wasm;
|
||||
|
||||
WASM_EXEC_TEST(Simd_I32x4_Splat) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(5))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(5, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_I32x4_Add) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r, WASM_SIMD_I32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_I32x4_ADD(WASM_SIMD_I32x4_SPLAT(WASM_I32V(5)),
|
||||
WASM_SIMD_I32x4_SPLAT(WASM_I32V(6)))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(11, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_F32x4_Splat) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_IF_ELSE(WASM_F32_EQ(WASM_SIMD_F32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_F32x4_SPLAT(WASM_F32(9.5))),
|
||||
WASM_F32(9.5)),
|
||||
WASM_RETURN1(WASM_I32V(1)), WASM_RETURN1(WASM_I32V(0))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(1, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_I32x4_Extract_With_F32x4) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_IF_ELSE(WASM_I32_EQ(WASM_SIMD_I32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_F32x4_SPLAT(WASM_F32(30.5))),
|
||||
WASM_I32_REINTERPRET_F32(WASM_F32(30.5))),
|
||||
WASM_RETURN1(WASM_I32V(1)), WASM_RETURN1(WASM_I32V(0))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(1, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_F32x4_Extract_With_I32x4) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_IF_ELSE(WASM_F32_EQ(WASM_SIMD_F32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(15))),
|
||||
WASM_F32_REINTERPRET_I32(WASM_I32V(15))),
|
||||
WASM_RETURN1(WASM_I32V(1)), WASM_RETURN1(WASM_I32V(0))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(1, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_F32x4_Add_With_I32x4) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_IF_ELSE(
|
||||
WASM_F32_EQ(WASM_SIMD_F32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_F32x4_ADD(
|
||||
WASM_SIMD_I32x4_SPLAT(WASM_I32V(32)),
|
||||
WASM_SIMD_I32x4_SPLAT(WASM_I32V(19)))),
|
||||
WASM_F32_ADD(WASM_F32_REINTERPRET_I32(WASM_I32V(32)),
|
||||
WASM_F32_REINTERPRET_I32(WASM_I32V(19)))),
|
||||
WASM_RETURN1(WASM_I32V(1)), WASM_RETURN1(WASM_I32V(0))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(1, r.Call()); }
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(Simd_I32x4_Add_With_F32x4) {
|
||||
FLAG_wasm_simd_prototype = true;
|
||||
WasmRunner<int32_t> r(kExecuteCompiled, MachineType::Int32());
|
||||
BUILD(r,
|
||||
WASM_IF_ELSE(
|
||||
WASM_I32_EQ(WASM_SIMD_I32x4_EXTRACT_LANE(
|
||||
0, WASM_SIMD_I32x4_ADD(
|
||||
WASM_SIMD_F32x4_SPLAT(WASM_F32(21.25)),
|
||||
WASM_SIMD_F32x4_SPLAT(WASM_F32(31.5)))),
|
||||
WASM_I32_ADD(WASM_I32_REINTERPRET_F32(WASM_F32(21.25)),
|
||||
WASM_I32_REINTERPRET_F32(WASM_F32(31.5)))),
|
||||
WASM_RETURN1(WASM_I32V(1)), WASM_RETURN1(WASM_I32V(0))));
|
||||
FOR_INT32_INPUTS(i) { CHECK_EQ(1, r.Call()); }
|
||||
}
|
@ -300,6 +300,7 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
|
||||
FATAL(str.str().c_str());
|
||||
}
|
||||
builder.Int64LoweringForTesting();
|
||||
builder.SimdScalarLoweringForTesting();
|
||||
}
|
||||
|
||||
template <typename ReturnType>
|
||||
|
Loading…
Reference in New Issue
Block a user