Commit bfa3884a authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

[turbofan] Machine operators are globally shared singletons.

TEST=compiler-unittests,cctest
R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/547233003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23864 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 11f7584d
......@@ -514,6 +514,7 @@ source_set("v8_base") {
"src/compiler/linkage.h",
"src/compiler/machine-operator-reducer.cc",
"src/compiler/machine-operator-reducer.h",
"src/compiler/machine-operator.cc",
"src/compiler/machine-operator.h",
"src/compiler/machine-type.cc",
"src/compiler/machine-type.h",
......
......@@ -283,8 +283,8 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
MachineType typ = TypeOf(OpParameter<MachineType>(node));
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
ArmOperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
......@@ -330,8 +330,8 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
MachineType rep = RepresentationOf(store_rep.machine_type);
if (store_rep.write_barrier_kind == kFullWriteBarrier) {
MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
......@@ -342,7 +342,7 @@ void InstructionSelector::VisitStore(Node* node) {
temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind);
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode;
switch (rep) {
......
......@@ -136,8 +136,8 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
MachineType typ = TypeOf(OpParameter<MachineType>(node));
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
Arm64OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
......@@ -184,8 +184,8 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
MachineType rep = RepresentationOf(store_rep.machine_type);
if (store_rep.write_barrier_kind == kFullWriteBarrier) {
MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
......@@ -196,7 +196,7 @@ void InstructionSelector::VisitStore(Node* node) {
temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind);
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
......
......@@ -1924,7 +1924,7 @@ Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value,
Node* AstGraphBuilder::BuildLoadObjectField(Node* object, int offset) {
// TODO(sigurds) Use simplified load here once it is ready.
MachineOperatorBuilder machine(zone());
MachineOperatorBuilder machine;
Node* field_load = NewNode(machine.Load(kMachAnyTagged), object,
jsgraph_->Int32Constant(offset - kHeapObjectTag));
return field_load;
......
......@@ -77,7 +77,7 @@ class ChangeLoweringTest : public GraphTest {
JSGraph jsgraph(graph(), common(), &typer);
CompilationInfo info(isolate(), zone());
Linkage linkage(&info);
MachineOperatorBuilder machine(zone(), WordRepresentation());
MachineOperatorBuilder machine(WordRepresentation());
ChangeLowering reducer(&jsgraph, &linkage, &machine);
return reducer.Reduce(node);
}
......
......@@ -44,13 +44,13 @@ Reduction ChangeLowering::Reduce(Node* node) {
Node* ChangeLowering::HeapNumberValueIndexConstant() {
STATIC_ASSERT(HeapNumber::kValueOffset % kPointerSize == 0);
const int heap_number_value_offset =
((HeapNumber::kValueOffset / kPointerSize) * (machine()->is64() ? 8 : 4));
((HeapNumber::kValueOffset / kPointerSize) * (machine()->Is64() ? 8 : 4));
return jsgraph()->Int32Constant(heap_number_value_offset - kHeapObjectTag);
}
Node* ChangeLowering::SmiMaxValueConstant() {
const int smi_value_size = machine()->is32() ? SmiTagging<4>::SmiValueSize()
const int smi_value_size = machine()->Is32() ? SmiTagging<4>::SmiValueSize()
: SmiTagging<8>::SmiValueSize();
return jsgraph()->Int32Constant(
-(static_cast<int>(0xffffffffu << (smi_value_size - 1)) + 1));
......@@ -58,7 +58,7 @@ Node* ChangeLowering::SmiMaxValueConstant() {
Node* ChangeLowering::SmiShiftBitsConstant() {
const int smi_shift_size = machine()->is32() ? SmiTagging<4>::SmiShiftSize()
const int smi_shift_size = machine()->Is32() ? SmiTagging<4>::SmiShiftSize()
: SmiTagging<8>::SmiShiftSize();
return jsgraph()->Int32Constant(smi_shift_size + kSmiTagSize);
}
......@@ -79,15 +79,15 @@ Node* ChangeLowering::AllocateHeapNumberWithValue(Node* value, Node* control) {
jsgraph()->ExternalConstant(ExternalReference(function, isolate())),
jsgraph()->Int32Constant(function->nargs), context, effect, control);
Node* store = graph()->NewNode(
machine()->Store(kMachFloat64, kNoWriteBarrier), heap_number,
HeapNumberValueIndexConstant(), value, heap_number, control);
machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
heap_number, HeapNumberValueIndexConstant(), value, heap_number, control);
return graph()->NewNode(common()->Finish(1), heap_number, store);
}
Node* ChangeLowering::ChangeSmiToInt32(Node* value) {
value = graph()->NewNode(machine()->WordSar(), value, SmiShiftBitsConstant());
if (machine()->is64()) {
if (machine()->Is64()) {
value = graph()->NewNode(machine()->TruncateInt64ToInt32(), value);
}
return value;
......@@ -131,7 +131,7 @@ Reduction ChangeLowering::ChangeFloat64ToTagged(Node* val, Node* control) {
Reduction ChangeLowering::ChangeInt32ToTagged(Node* val, Node* control) {
if (machine()->is64()) {
if (machine()->Is64()) {
return Replace(
graph()->NewNode(machine()->Word64Shl(),
graph()->NewNode(machine()->ChangeInt32ToInt64(), val),
......@@ -219,7 +219,7 @@ Reduction ChangeLowering::ChangeUint32ToTagged(Node* val, Node* control) {
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* smi = graph()->NewNode(
machine()->WordShl(),
machine()->is64()
machine()->Is64()
? graph()->NewNode(machine()->ChangeUint32ToUint64(), val)
: val,
SmiShiftBitsConstant());
......
......@@ -428,20 +428,20 @@ class IsCallMatcher FINAL : public NodeMatcher {
class IsLoadMatcher FINAL : public NodeMatcher {
public:
IsLoadMatcher(const Matcher<MachineType>& type_matcher,
IsLoadMatcher(const Matcher<LoadRepresentation>& rep_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
const Matcher<Node*>& effect_matcher)
: NodeMatcher(IrOpcode::kLoad),
type_matcher_(type_matcher),
rep_matcher_(rep_matcher),
base_matcher_(base_matcher),
index_matcher_(index_matcher),
effect_matcher_(effect_matcher) {}
virtual void DescribeTo(std::ostream* os) const OVERRIDE {
NodeMatcher::DescribeTo(os);
*os << " whose type (";
type_matcher_.DescribeTo(os);
*os << " whose rep (";
rep_matcher_.DescribeTo(os);
*os << "), base (";
base_matcher_.DescribeTo(os);
*os << "), index (";
......@@ -454,8 +454,8 @@ class IsLoadMatcher FINAL : public NodeMatcher {
virtual bool MatchAndExplain(Node* node, MatchResultListener* listener) const
OVERRIDE {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(OpParameter<MachineType>(node), "type",
type_matcher_, listener) &&
PrintMatchAndExplain(OpParameter<LoadRepresentation>(node), "rep",
rep_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base",
base_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
......@@ -465,7 +465,7 @@ class IsLoadMatcher FINAL : public NodeMatcher {
}
private:
const Matcher<MachineType> type_matcher_;
const Matcher<LoadRepresentation> rep_matcher_;
const Matcher<Node*> base_matcher_;
const Matcher<Node*> index_matcher_;
const Matcher<Node*> effect_matcher_;
......@@ -513,10 +513,10 @@ class IsStoreMatcher FINAL : public NodeMatcher {
OVERRIDE {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(
OpParameter<StoreRepresentation>(node).machine_type, "type",
OpParameter<StoreRepresentation>(node).machine_type(), "type",
type_matcher_, listener) &&
PrintMatchAndExplain(
OpParameter<StoreRepresentation>(node).write_barrier_kind,
OpParameter<StoreRepresentation>(node).write_barrier_kind(),
"write barrier", write_barrier_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base",
base_matcher_, listener) &&
......@@ -704,12 +704,12 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
}
Matcher<Node*> IsLoad(const Matcher<MachineType>& type_matcher,
Matcher<Node*> IsLoad(const Matcher<LoadRepresentation>& rep_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
const Matcher<Node*>& effect_matcher) {
return MakeMatcher(new IsLoadMatcher(type_matcher, base_matcher,
index_matcher, effect_matcher));
return MakeMatcher(new IsLoadMatcher(rep_matcher, base_matcher, index_matcher,
effect_matcher));
}
......
......@@ -83,7 +83,7 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsLoad(const Matcher<MachineType>& type_matcher,
Matcher<Node*> IsLoad(const Matcher<LoadRepresentation>& rep_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
const Matcher<Node*>& effect_matcher);
......
......@@ -41,8 +41,8 @@ class IA32OperandGenerator FINAL : public OperandGenerator {
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
MachineType typ = TypeOf(OpParameter<MachineType>(node));
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
IA32OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
......@@ -98,8 +98,8 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
MachineType rep = RepresentationOf(store_rep.machine_type);
if (store_rep.write_barrier_kind == kFullWriteBarrier) {
MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK_EQ(kRepTagged, rep);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
......@@ -110,7 +110,7 @@ void InstructionSelector::VisitStore(Node* node) {
temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind);
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
InstructionOperand* val;
if (g.CanBeImmediate(value)) {
val = g.UseImmediate(value);
......
......@@ -488,8 +488,8 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kStateValues:
return;
case IrOpcode::kLoad: {
MachineType load_rep = OpParameter<MachineType>(node);
MarkAsRepresentation(load_rep, node);
LoadRepresentation rep = OpParameter<LoadRepresentation>(node);
MarkAsRepresentation(rep, node);
return VisitLoad(node);
}
case IrOpcode::kStore:
......
......@@ -490,7 +490,8 @@ Node* JSGenericLowering::LowerJSStoreContext(Node* node) {
}
node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index())));
PatchOperator(node, machine()->Store(kMachAnyTagged, kFullWriteBarrier));
PatchOperator(node, machine()->Store(StoreRepresentation(kMachAnyTagged,
kFullWriteBarrier)));
return node;
}
......
......@@ -19,9 +19,7 @@ namespace compiler {
class JSTypedLowering FINAL : public Reducer {
public:
explicit JSTypedLowering(JSGraph* jsgraph)
: jsgraph_(jsgraph),
simplified_(jsgraph->zone()),
machine_(jsgraph->zone()) {}
: jsgraph_(jsgraph), simplified_(jsgraph->zone()) {}
virtual ~JSTypedLowering();
virtual Reduction Reduce(Node* node) OVERRIDE;
......
......@@ -15,7 +15,7 @@ namespace compiler {
class MachineOperatorReducerTest : public GraphTest {
public:
explicit MachineOperatorReducerTest(int num_parameters = 2)
: GraphTest(num_parameters), machine_(zone()) {}
: GraphTest(num_parameters) {}
protected:
Reduction Reduce(Node* node) {
......
......@@ -15,7 +15,7 @@ namespace internal {
namespace compiler {
MachineOperatorReducer::MachineOperatorReducer(JSGraph* jsgraph)
: jsgraph_(jsgraph), machine_(jsgraph->zone()) {}
: jsgraph_(jsgraph) {}
MachineOperatorReducer::~MachineOperatorReducer() {}
......
......@@ -4,82 +4,320 @@
#include "src/compiler/machine-operator.h"
#include "src/compiler/operator-properties-inl.h"
#include "src/test/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
namespace compiler {
class MachineOperatorCommonTest
: public TestWithZone,
public ::testing::WithParamInterface<MachineType> {
public:
MachineOperatorCommonTest() : machine_(NULL) {}
virtual ~MachineOperatorCommonTest() { EXPECT_EQ(NULL, machine_); }
virtual void SetUp() OVERRIDE {
TestWithZone::SetUp();
EXPECT_EQ(NULL, machine_);
machine_ = new MachineOperatorBuilder(zone(), GetParam());
}
#if GTEST_HAS_COMBINE
// TODO(bmeurer): Find a new home for these.
inline std::ostream& operator<<(std::ostream& os, const MachineType& type) {
OStringStream ost;
ost << type;
return os << ost.c_str();
}
inline std::ostream& operator<<(std::ostream& os,
const WriteBarrierKind& write_barrier_kind) {
OStringStream ost;
ost << write_barrier_kind;
return os << ost.c_str();
}
virtual void TearDown() OVERRIDE {
ASSERT_TRUE(machine_ != NULL);
delete machine_;
machine_ = NULL;
TestWithZone::TearDown();
}
template <typename T>
class MachineOperatorTestWithParam
: public ::testing::TestWithParam< ::testing::tuple<MachineType, T> > {
protected:
MachineOperatorBuilder* machine() const { return machine_; }
MachineType type() const { return ::testing::get<0>(B::GetParam()); }
const T& GetParam() const { return ::testing::get<1>(B::GetParam()); }
private:
MachineOperatorBuilder* machine_;
typedef ::testing::TestWithParam< ::testing::tuple<MachineType, T> > B;
};
TEST_P(MachineOperatorCommonTest, ChangeInt32ToInt64) {
const Operator* op = machine()->ChangeInt32ToInt64();
EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
namespace {
const MachineType kMachineReps[] = {kRepWord32, kRepWord64};
const MachineType kMachineTypes[] = {
kMachFloat32, kMachFloat64, kMachInt8, kMachUint8, kMachInt16,
kMachUint16, kMachInt32, kMachUint32, kMachInt64, kMachUint64,
kMachPtr, kMachAnyTagged, kRepBit, kRepWord8, kRepWord16,
kRepWord32, kRepWord64, kRepFloat32, kRepFloat64, kRepTagged};
} // namespace
// -----------------------------------------------------------------------------
// Load operator.
typedef MachineOperatorTestWithParam<LoadRepresentation>
MachineLoadOperatorTest;
TEST_P(MachineLoadOperatorTest, InstancesAreGloballyShared) {
MachineOperatorBuilder machine1(type());
MachineOperatorBuilder machine2(type());
EXPECT_EQ(machine1.Load(GetParam()), machine2.Load(GetParam()));
}
TEST_P(MachineOperatorCommonTest, ChangeUint32ToUint64) {
const Operator* op = machine()->ChangeUint32ToUint64();
EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
TEST_P(MachineLoadOperatorTest, NumberOfInputsAndOutputs) {
MachineOperatorBuilder machine(type());
const Operator* op = machine.Load(GetParam());
EXPECT_EQ(2, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetEffectInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlInputCount(op));
EXPECT_EQ(3, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
}
TEST_P(MachineOperatorCommonTest, TruncateFloat64ToInt32) {
const Operator* op = machine()->TruncateFloat64ToInt32();
EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
TEST_P(MachineLoadOperatorTest, OpcodeIsCorrect) {
MachineOperatorBuilder machine(type());
EXPECT_EQ(IrOpcode::kLoad, machine.Load(GetParam())->opcode());
}
TEST_P(MachineLoadOperatorTest, ParameterIsCorrect) {
MachineOperatorBuilder machine(type());
EXPECT_EQ(GetParam(),
OpParameter<LoadRepresentation>(machine.Load(GetParam())));
}
TEST_P(MachineOperatorCommonTest, TruncateInt64ToInt32) {
const Operator* op = machine()->TruncateInt64ToInt32();
EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
INSTANTIATE_TEST_CASE_P(MachineOperatorTest, MachineLoadOperatorTest,
::testing::Combine(::testing::ValuesIn(kMachineReps),
::testing::ValuesIn(kMachineTypes)));
// -----------------------------------------------------------------------------
// Store operator.
class MachineStoreOperatorTest
: public MachineOperatorTestWithParam<
::testing::tuple<MachineType, WriteBarrierKind> > {
protected:
StoreRepresentation GetParam() const {
return StoreRepresentation(
::testing::get<0>(MachineOperatorTestWithParam<
::testing::tuple<MachineType, WriteBarrierKind> >::GetParam()),
::testing::get<1>(MachineOperatorTestWithParam<
::testing::tuple<MachineType, WriteBarrierKind> >::GetParam()));
}
};
TEST_P(MachineStoreOperatorTest, InstancesAreGloballyShared) {
MachineOperatorBuilder machine1(type());
MachineOperatorBuilder machine2(type());
EXPECT_EQ(machine1.Store(GetParam()), machine2.Store(GetParam()));
}
TEST_P(MachineStoreOperatorTest, NumberOfInputsAndOutputs) {
MachineOperatorBuilder machine(type());
const Operator* op = machine.Store(GetParam());
EXPECT_EQ(3, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetEffectInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetControlInputCount(op));
EXPECT_EQ(5, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetValueOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
}
TEST_P(MachineStoreOperatorTest, OpcodeIsCorrect) {
MachineOperatorBuilder machine(type());
EXPECT_EQ(IrOpcode::kStore, machine.Store(GetParam())->opcode());
}
TEST_P(MachineStoreOperatorTest, ParameterIsCorrect) {
MachineOperatorBuilder machine(type());
EXPECT_EQ(GetParam(),
OpParameter<StoreRepresentation>(machine.Store(GetParam())));
}
INSTANTIATE_TEST_CASE_P(
MachineOperatorTest, MachineStoreOperatorTest,
::testing::Combine(
::testing::ValuesIn(kMachineReps),
::testing::Combine(::testing::ValuesIn(kMachineTypes),
::testing::Values(kNoWriteBarrier,
kFullWriteBarrier))));
// -----------------------------------------------------------------------------
// Pure operators.
namespace {
struct PureOperator {
const Operator* (MachineOperatorBuilder::*constructor)();
IrOpcode::Value opcode;
int value_input_count;
int value_output_count;
};
std::ostream& operator<<(std::ostream& os, const PureOperator& pop) {
return os << IrOpcode::Mnemonic(pop.opcode);
}
const PureOperator kPureOperators[] = {
#define PURE(Name, input_count, output_count) \
{ \
&MachineOperatorBuilder::Name, IrOpcode::k##Name, input_count, \
output_count \
}
PURE(Word32And, 2, 1), PURE(Word32Or, 2, 1),
PURE(Word32Xor, 2, 1), PURE(Word32Shl, 2, 1),
PURE(Word32Shr, 2, 1), PURE(Word32Sar, 2, 1),
PURE(Word32Ror, 2, 1), PURE(Word32Equal, 2, 1),
PURE(Word64And, 2, 1), PURE(Word64Or, 2, 1),
PURE(Word64Xor, 2, 1), PURE(Word64Shl, 2, 1),
PURE(Word64Shr, 2, 1), PURE(Word64Sar, 2, 1),
PURE(Word64Ror, 2, 1), PURE(Word64Equal, 2, 1),
PURE(Int32Add, 2, 1), PURE(Int32AddWithOverflow, 2, 2),
PURE(Int32Sub, 2, 1), PURE(Int32SubWithOverflow, 2, 2),
PURE(Int32Mul, 2, 1), PURE(Int32Div, 2, 1),
PURE(Int32UDiv, 2, 1), PURE(Int32Mod, 2, 1),
PURE(Int32UMod, 2, 1), PURE(Int32LessThan, 2, 1),
PURE(Int32LessThanOrEqual, 2, 1), PURE(Uint32LessThan, 2, 1),
PURE(Uint32LessThanOrEqual, 2, 1), PURE(Int64Add, 2, 1),
PURE(Int64Sub, 2, 1), PURE(Int64Mul, 2, 1),
PURE(Int64Div, 2, 1), PURE(Int64UDiv, 2, 1),
PURE(Int64Mod, 2, 1), PURE(Int64UMod, 2, 1),
PURE(Int64LessThan, 2, 1), PURE(Int64LessThanOrEqual, 2, 1),
PURE(ChangeFloat64ToInt32, 1, 1), PURE(ChangeFloat64ToUint32, 1, 1),
PURE(ChangeInt32ToInt64, 1, 1), PURE(ChangeUint32ToFloat64, 1, 1),
PURE(ChangeUint32ToUint64, 1, 1), PURE(TruncateFloat64ToInt32, 1, 1),
PURE(TruncateInt64ToInt32, 1, 1), PURE(Float64Add, 2, 1),
PURE(Float64Sub, 2, 1), PURE(Float64Mul, 2, 1),
PURE(Float64Div, 2, 1), PURE(Float64Mod, 2, 1),
PURE(Float64Equal, 2, 1), PURE(Float64LessThan, 2, 1),
PURE(Float64LessThanOrEqual, 2, 1)
#undef PURE
};
typedef MachineOperatorTestWithParam<PureOperator> MachinePureOperatorTest;
} // namespace
TEST_P(MachinePureOperatorTest, InstancesAreGloballyShared) {
const PureOperator& pop = GetParam();
MachineOperatorBuilder machine1(type());
MachineOperatorBuilder machine2(type());
EXPECT_EQ((machine1.*pop.constructor)(), (machine2.*pop.constructor)());
}
TEST_P(MachinePureOperatorTest, NumberOfInputsAndOutputs) {
MachineOperatorBuilder machine(type());
const PureOperator& pop = GetParam();
const Operator* op = (machine.*pop.constructor)();
EXPECT_EQ(pop.value_input_count, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlInputCount(op));
EXPECT_EQ(pop.value_input_count, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(pop.value_output_count,
OperatorProperties::GetValueOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
}
TEST_P(MachinePureOperatorTest, MarkedAsPure) {
MachineOperatorBuilder machine(type());
const PureOperator& pop = GetParam();
const Operator* op = (machine.*pop.constructor)();
EXPECT_TRUE(op->HasProperty(Operator::kPure));
}
INSTANTIATE_TEST_CASE_P(MachineOperatorTest, MachineOperatorCommonTest,
::testing::Values(kRepWord32, kRepWord64));
TEST_P(MachinePureOperatorTest, OpcodeIsCorrect) {
MachineOperatorBuilder machine(type());
const PureOperator& pop = GetParam();
const Operator* op = (machine.*pop.constructor)();
EXPECT_EQ(pop.opcode, op->opcode());
}
INSTANTIATE_TEST_CASE_P(
MachineOperatorTest, MachinePureOperatorTest,
::testing::Combine(::testing::ValuesIn(kMachineReps),
::testing::ValuesIn(kPureOperators)));
#endif // GTEST_HAS_COMBINE
// -----------------------------------------------------------------------------
// Pseudo operators.
TEST(MachineOperatorTest, PseudoOperatorsWhenWordSizeIs32Bit) {
MachineOperatorBuilder machine(kRepWord32);
EXPECT_EQ(machine.Word32And(), machine.WordAnd());
EXPECT_EQ(machine.Word32Or(), machine.WordOr());
EXPECT_EQ(machine.Word32Xor(), machine.WordXor());
EXPECT_EQ(machine.Word32Shl(), machine.WordShl());
EXPECT_EQ(machine.Word32Shr(), machine.WordShr());
EXPECT_EQ(machine.Word32Sar(), machine.WordSar());
EXPECT_EQ(machine.Word32Ror(), machine.WordRor());
EXPECT_EQ(machine.Word32Equal(), machine.WordEqual());
EXPECT_EQ(machine.Int32Add(), machine.IntAdd());
EXPECT_EQ(machine.Int32Sub(), machine.IntSub());
EXPECT_EQ(machine.Int32Mul(), machine.IntMul());
EXPECT_EQ(machine.Int32Div(), machine.IntDiv());
EXPECT_EQ(machine.Int32UDiv(), machine.IntUDiv());
EXPECT_EQ(machine.Int32Mod(), machine.IntMod());
EXPECT_EQ(machine.Int32UMod(), machine.IntUMod());
EXPECT_EQ(machine.Int32LessThan(), machine.IntLessThan());
EXPECT_EQ(machine.Int32LessThanOrEqual(), machine.IntLessThanOrEqual());
}
TEST(MachineOperatorTest, PseudoOperatorsWhenWordSizeIs64Bit) {
MachineOperatorBuilder machine(kRepWord64);
EXPECT_EQ(machine.Word64And(), machine.WordAnd());
EXPECT_EQ(machine.Word64Or(), machine.WordOr());
EXPECT_EQ(machine.Word64Xor(), machine.WordXor());
EXPECT_EQ(machine.Word64Shl(), machine.WordShl());
EXPECT_EQ(machine.Word64Shr(), machine.WordShr());
EXPECT_EQ(machine.Word64Sar(), machine.WordSar());
EXPECT_EQ(machine.Word64Ror(), machine.WordRor());
EXPECT_EQ(machine.Word64Equal(), machine.WordEqual());
EXPECT_EQ(machine.Int64Add(), machine.IntAdd());
EXPECT_EQ(machine.Int64Sub(), machine.IntSub());
EXPECT_EQ(machine.Int64Mul(), machine.IntMul());
EXPECT_EQ(machine.Int64Div(), machine.IntDiv());
EXPECT_EQ(machine.Int64UDiv(), machine.IntUDiv());
EXPECT_EQ(machine.Int64Mod(), machine.IntMod());
EXPECT_EQ(machine.Int64UMod(), machine.IntUMod());
EXPECT_EQ(machine.Int64LessThan(), machine.IntLessThan());
EXPECT_EQ(machine.Int64LessThanOrEqual(), machine.IntLessThanOrEqual());
}
} // namespace compiler
} // namespace internal
......
// Copyright 2014 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/machine-operator.h"
#include "src/base/lazy-instance.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
namespace v8 {
namespace internal {
namespace compiler {
OStream& operator<<(OStream& os, const WriteBarrierKind& write_barrier_kind) {
switch (write_barrier_kind) {
case kNoWriteBarrier:
return os << "NoWriteBarrier";
case kFullWriteBarrier:
return os << "FullWriteBarrier";
}
UNREACHABLE();
return os;
}
OStream& operator<<(OStream& os, const StoreRepresentation& rep) {
return os << "(" << rep.machine_type() << " : " << rep.write_barrier_kind()
<< ")";
}
template <>
struct StaticParameterTraits<StoreRepresentation> {
static OStream& PrintTo(OStream& os, const StoreRepresentation& rep) {
return os << rep;
}
static int HashCode(const StoreRepresentation& rep) {
return rep.machine_type() + rep.write_barrier_kind();
}
static bool Equals(const StoreRepresentation& rep1,
const StoreRepresentation& rep2) {
return rep1 == rep2;
}
};
template <>
struct StaticParameterTraits<LoadRepresentation> {
static OStream& PrintTo(OStream& os, LoadRepresentation type) { // NOLINT
return os << type;
}
static int HashCode(LoadRepresentation type) { return type; }
static bool Equals(LoadRepresentation lhs, LoadRepresentation rhs) {
return lhs == rhs;
}
};
#define PURE_OP_LIST(V) \
V(Word32And, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word32Or, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word32Xor, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word32Shl, Operator::kNoProperties, 2, 1) \
V(Word32Shr, Operator::kNoProperties, 2, 1) \
V(Word32Sar, Operator::kNoProperties, 2, 1) \
V(Word32Ror, Operator::kNoProperties, 2, 1) \
V(Word32Equal, Operator::kCommutative, 2, 1) \
V(Word64And, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word64Or, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word64Xor, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Word64Shl, Operator::kNoProperties, 2, 1) \
V(Word64Shr, Operator::kNoProperties, 2, 1) \
V(Word64Sar, Operator::kNoProperties, 2, 1) \
V(Word64Ror, Operator::kNoProperties, 2, 1) \
V(Word64Equal, Operator::kCommutative, 2, 1) \
V(Int32Add, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Int32AddWithOverflow, Operator::kAssociative | Operator::kCommutative, 2, \
2) \
V(Int32Sub, Operator::kNoProperties, 2, 1) \
V(Int32SubWithOverflow, Operator::kNoProperties, 2, 2) \
V(Int32Mul, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Int32Div, Operator::kNoProperties, 2, 1) \
V(Int32UDiv, Operator::kNoProperties, 2, 1) \
V(Int32Mod, Operator::kNoProperties, 2, 1) \
V(Int32UMod, Operator::kNoProperties, 2, 1) \
V(Int32LessThan, Operator::kNoProperties, 2, 1) \
V(Int32LessThanOrEqual, Operator::kNoProperties, 2, 1) \
V(Uint32LessThan, Operator::kNoProperties, 2, 1) \
V(Uint32LessThanOrEqual, Operator::kNoProperties, 2, 1) \
V(Int64Add, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Int64Sub, Operator::kNoProperties, 2, 1) \
V(Int64Mul, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Int64Div, Operator::kNoProperties, 2, 1) \
V(Int64UDiv, Operator::kNoProperties, 2, 1) \
V(Int64Mod, Operator::kNoProperties, 2, 1) \
V(Int64UMod, Operator::kNoProperties, 2, 1) \
V(Int64LessThan, Operator::kNoProperties, 2, 1) \
V(Int64LessThanOrEqual, Operator::kNoProperties, 2, 1) \
V(ChangeFloat64ToInt32, Operator::kNoProperties, 1, 1) \
V(ChangeFloat64ToUint32, Operator::kNoProperties, 1, 1) \
V(ChangeInt32ToFloat64, Operator::kNoProperties, 1, 1) \
V(ChangeInt32ToInt64, Operator::kNoProperties, 1, 1) \
V(ChangeUint32ToFloat64, Operator::kNoProperties, 1, 1) \
V(ChangeUint32ToUint64, Operator::kNoProperties, 1, 1) \
V(TruncateFloat64ToInt32, Operator::kNoProperties, 1, 1) \
V(TruncateInt64ToInt32, Operator::kNoProperties, 1, 1) \
V(Float64Add, Operator::kCommutative, 2, 1) \
V(Float64Sub, Operator::kNoProperties, 2, 1) \
V(Float64Mul, Operator::kCommutative, 2, 1) \
V(Float64Div, Operator::kNoProperties, 2, 1) \
V(Float64Mod, Operator::kNoProperties, 2, 1) \
V(Float64Equal, Operator::kCommutative, 2, 1) \
V(Float64LessThan, Operator::kNoProperties, 2, 1) \
V(Float64LessThanOrEqual, Operator::kNoProperties, 2, 1)
#define MACHINE_TYPE_LIST(V) \
V(MachFloat32) \
V(MachFloat64) \
V(MachInt8) \
V(MachUint8) \
V(MachInt16) \
V(MachUint16) \
V(MachInt32) \
V(MachUint32) \
V(MachInt64) \
V(MachUint64) \
V(MachAnyTagged) \
V(RepBit) \
V(RepWord8) \
V(RepWord16) \
V(RepWord32) \
V(RepWord64) \
V(RepFloat32) \
V(RepFloat64) \
V(RepTagged)
struct MachineOperatorBuilderImpl {
#define PURE(Name, properties, input_count, output_count) \
struct Name##Operator FINAL : public SimpleOperator { \
Name##Operator() \
: SimpleOperator(IrOpcode::k##Name, Operator::kPure | properties, \
input_count, output_count, #Name) {} \
}; \
Name##Operator k##Name;
PURE_OP_LIST(PURE)
#undef PURE
#define LOAD(Type) \
struct Load##Type##Operator FINAL : public Operator1<LoadRepresentation> { \
Load##Type##Operator() \
: Operator1<LoadRepresentation>( \
IrOpcode::kLoad, Operator::kNoThrow | Operator::kNoWrite, 2, 1, \
"Load", k##Type) {} \
}; \
Load##Type##Operator k##Load##Type;
MACHINE_TYPE_LIST(LOAD)
#undef LOAD
#define STORE(Type) \
struct Store##Type##Operator : public Operator1<StoreRepresentation> { \
explicit Store##Type##Operator(WriteBarrierKind write_barrier_kind) \
: Operator1<StoreRepresentation>( \
IrOpcode::kStore, Operator::kNoRead | Operator::kNoThrow, 3, 0, \
"Store", StoreRepresentation(k##Type, write_barrier_kind)) {} \
}; \
struct Store##Type##NoWriteBarrier##Operator FINAL \
: public Store##Type##Operator { \
Store##Type##NoWriteBarrier##Operator() \
: Store##Type##Operator(kNoWriteBarrier) {} \
}; \
struct Store##Type##FullWriteBarrier##Operator FINAL \
: public Store##Type##Operator { \
Store##Type##FullWriteBarrier##Operator() \
: Store##Type##Operator(kFullWriteBarrier) {} \
}; \
Store##Type##NoWriteBarrier##Operator k##Store##Type##NoWriteBarrier; \
Store##Type##FullWriteBarrier##Operator k##Store##Type##FullWriteBarrier;
MACHINE_TYPE_LIST(STORE)
#undef STORE
};
static base::LazyInstance<MachineOperatorBuilderImpl>::type kImpl =
LAZY_INSTANCE_INITIALIZER;
MachineOperatorBuilder::MachineOperatorBuilder(MachineType word)
: impl_(kImpl.Get()), word_(word) {
DCHECK(word == kRepWord32 || word == kRepWord64);
}
#define PURE(Name, properties, input_count, output_count) \
const Operator* MachineOperatorBuilder::Name() { return &impl_.k##Name; }
PURE_OP_LIST(PURE)
#undef PURE
const Operator* MachineOperatorBuilder::Load(LoadRepresentation rep) {
switch (rep) {
#define LOAD(Type) \
case k##Type: \
return &impl_.k##Load##Type;
MACHINE_TYPE_LIST(LOAD)
#undef LOAD
default:
break;
}
UNREACHABLE();
return NULL;
}
const Operator* MachineOperatorBuilder::Store(StoreRepresentation rep) {
switch (rep.machine_type()) {
#define STORE(Type) \
case k##Type: \
switch (rep.write_barrier_kind()) { \
case kNoWriteBarrier: \
return &impl_.k##Store##Type##NoWriteBarrier; \
case kFullWriteBarrier: \
return &impl_.k##Store##Type##FullWriteBarrier; \
} \
break;
MACHINE_TYPE_LIST(STORE)
#undef STORE
default:
break;
}
UNREACHABLE();
return NULL;
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -6,183 +6,177 @@
#define V8_COMPILER_MACHINE_OPERATOR_H_
#include "src/compiler/machine-type.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
// TODO(turbofan): other write barriers are possible based on type
// Forward declarations.
struct MachineOperatorBuilderImpl;
class Operator;
// Supported write barrier modes.
enum WriteBarrierKind { kNoWriteBarrier, kFullWriteBarrier };
OStream& operator<<(OStream& os, const WriteBarrierKind& write_barrier_kind);
typedef MachineType LoadRepresentation;
// A Store needs a MachineType and a WriteBarrierKind
// in order to emit the correct write barrier.
struct StoreRepresentation {
MachineType machine_type;
WriteBarrierKind write_barrier_kind;
};
class StoreRepresentation FINAL {
public:
StoreRepresentation(MachineType machine_type,
WriteBarrierKind write_barrier_kind)
: machine_type_(machine_type), write_barrier_kind_(write_barrier_kind) {}
MachineType machine_type() const { return machine_type_; }
WriteBarrierKind write_barrier_kind() const { return write_barrier_kind_; }
// TODO(bmeurer): Phi will probably also need this in the future.
template <>
struct StaticParameterTraits<MachineType> {
static OStream& PrintTo(OStream& os, MachineType type) { // NOLINT
return os << type;
}
static int HashCode(MachineType type) { return type; }
static bool Equals(MachineType lhs, MachineType rhs) { return lhs == rhs; }
private:
MachineType machine_type_;
WriteBarrierKind write_barrier_kind_;
};
inline bool operator==(const StoreRepresentation& rep1,
const StoreRepresentation& rep2) {
return rep1.machine_type() == rep2.machine_type() &&
rep1.write_barrier_kind() == rep2.write_barrier_kind();
}
inline bool operator!=(const StoreRepresentation& rep1,
const StoreRepresentation& rep2) {
return !(rep1 == rep2);
}
OStream& operator<<(OStream& os, const StoreRepresentation& rep);
// Interface for building machine-level operators. These operators are
// machine-level but machine-independent and thus define a language suitable
// for generating code to run on architectures such as ia32, x64, arm, etc.
class MachineOperatorBuilder {
class MachineOperatorBuilder FINAL {
public:
explicit MachineOperatorBuilder(Zone* zone, MachineType word = kMachPtr)
: zone_(zone), word_(word) {
CHECK(word == kRepWord32 || word == kRepWord64);
}
#define SIMPLE(name, properties, inputs, outputs) \
return new (zone_) \
SimpleOperator(IrOpcode::k##name, properties, inputs, outputs, #name);
#define OP1(name, ptype, pname, properties, inputs, outputs) \
return new (zone_) \
Operator1<ptype>(IrOpcode::k##name, properties | Operator::kNoThrow, \
inputs, outputs, #name, pname)
#define BINOP(name) SIMPLE(name, Operator::kPure, 2, 1)
#define BINOP_O(name) SIMPLE(name, Operator::kPure, 2, 2)
#define BINOP_C(name) \
SIMPLE(name, Operator::kCommutative | Operator::kPure, 2, 1)
#define BINOP_AC(name) \
SIMPLE(name, \
Operator::kAssociative | Operator::kCommutative | Operator::kPure, 2, \
1)
#define BINOP_ACO(name) \
SIMPLE(name, \
Operator::kAssociative | Operator::kCommutative | Operator::kPure, 2, \
2)
#define UNOP(name) SIMPLE(name, Operator::kPure, 1, 1)
#define WORD_SIZE(x) return is64() ? Word64##x() : Word32##x()
#define INT_SIZE(x) return is64() ? Int64##x() : Int32##x()
const Operator* Load(MachineType rep) { // load [base + index]
OP1(Load, MachineType, rep, Operator::kNoWrite, 2, 1);
}
// store [base + index], value
const Operator* Store(MachineType rep, WriteBarrierKind kind) {
StoreRepresentation store_rep = {rep, kind};
OP1(Store, StoreRepresentation, store_rep, Operator::kNoRead, 3, 0);
}
const Operator* WordAnd() { WORD_SIZE(And); }
const Operator* WordOr() { WORD_SIZE(Or); }
const Operator* WordXor() { WORD_SIZE(Xor); }
const Operator* WordShl() { WORD_SIZE(Shl); }
const Operator* WordShr() { WORD_SIZE(Shr); }
const Operator* WordSar() { WORD_SIZE(Sar); }
const Operator* WordRor() { WORD_SIZE(Ror); }
const Operator* WordEqual() { WORD_SIZE(Equal); }
const Operator* Word32And() { BINOP_AC(Word32And); }
const Operator* Word32Or() { BINOP_AC(Word32Or); }
const Operator* Word32Xor() { BINOP_AC(Word32Xor); }
const Operator* Word32Shl() { BINOP(Word32Shl); }
const Operator* Word32Shr() { BINOP(Word32Shr); }
const Operator* Word32Sar() { BINOP(Word32Sar); }
const Operator* Word32Ror() { BINOP(Word32Ror); }
const Operator* Word32Equal() { BINOP_C(Word32Equal); }
const Operator* Word64And() { BINOP_AC(Word64And); }
const Operator* Word64Or() { BINOP_AC(Word64Or); }
const Operator* Word64Xor() { BINOP_AC(Word64Xor); }
const Operator* Word64Shl() { BINOP(Word64Shl); }
const Operator* Word64Shr() { BINOP(Word64Shr); }
const Operator* Word64Sar() { BINOP(Word64Sar); }
const Operator* Word64Ror() { BINOP(Word64Ror); }
const Operator* Word64Equal() { BINOP_C(Word64Equal); }
const Operator* Int32Add() { BINOP_AC(Int32Add); }
const Operator* Int32AddWithOverflow() { BINOP_ACO(Int32AddWithOverflow); }
const Operator* Int32Sub() { BINOP(Int32Sub); }
const Operator* Int32SubWithOverflow() { BINOP_O(Int32SubWithOverflow); }
const Operator* Int32Mul() { BINOP_AC(Int32Mul); }
const Operator* Int32Div() { BINOP(Int32Div); }
const Operator* Int32UDiv() { BINOP(Int32UDiv); }
const Operator* Int32Mod() { BINOP(Int32Mod); }
const Operator* Int32UMod() { BINOP(Int32UMod); }
const Operator* Int32LessThan() { BINOP(Int32LessThan); }
const Operator* Int32LessThanOrEqual() { BINOP(Int32LessThanOrEqual); }
const Operator* Uint32LessThan() { BINOP(Uint32LessThan); }
const Operator* Uint32LessThanOrEqual() { BINOP(Uint32LessThanOrEqual); }
const Operator* Int64Add() { BINOP_AC(Int64Add); }
const Operator* Int64Sub() { BINOP(Int64Sub); }
const Operator* Int64Mul() { BINOP_AC(Int64Mul); }
const Operator* Int64Div() { BINOP(Int64Div); }
const Operator* Int64UDiv() { BINOP(Int64UDiv); }
const Operator* Int64Mod() { BINOP(Int64Mod); }
const Operator* Int64UMod() { BINOP(Int64UMod); }
const Operator* Int64LessThan() { BINOP(Int64LessThan); }
const Operator* Int64LessThanOrEqual() { BINOP(Int64LessThanOrEqual); }
// Signed comparison of word-sized integer values, translates to int32/int64
// comparisons depending on the word-size of the machine.
const Operator* IntLessThan() { INT_SIZE(LessThan); }
const Operator* IntLessThanOrEqual() { INT_SIZE(LessThanOrEqual); }
explicit MachineOperatorBuilder(MachineType word = kMachPtr);
const Operator* Word32And() WARN_UNUSED_RESULT;
const Operator* Word32Or() WARN_UNUSED_RESULT;
const Operator* Word32Xor() WARN_UNUSED_RESULT;
const Operator* Word32Shl() WARN_UNUSED_RESULT;
const Operator* Word32Shr() WARN_UNUSED_RESULT;
const Operator* Word32Sar() WARN_UNUSED_RESULT;
const Operator* Word32Ror() WARN_UNUSED_RESULT;
const Operator* Word32Equal() WARN_UNUSED_RESULT;
const Operator* Word64And() WARN_UNUSED_RESULT;
const Operator* Word64Or() WARN_UNUSED_RESULT;
const Operator* Word64Xor() WARN_UNUSED_RESULT;
const Operator* Word64Shl() WARN_UNUSED_RESULT;
const Operator* Word64Shr() WARN_UNUSED_RESULT;
const Operator* Word64Sar() WARN_UNUSED_RESULT;
const Operator* Word64Ror() WARN_UNUSED_RESULT;
const Operator* Word64Equal() WARN_UNUSED_RESULT;
const Operator* Int32Add() WARN_UNUSED_RESULT;
const Operator* Int32AddWithOverflow() WARN_UNUSED_RESULT;
const Operator* Int32Sub() WARN_UNUSED_RESULT;
const Operator* Int32SubWithOverflow() WARN_UNUSED_RESULT;
const Operator* Int32Mul() WARN_UNUSED_RESULT;
const Operator* Int32Div() WARN_UNUSED_RESULT;
const Operator* Int32UDiv() WARN_UNUSED_RESULT;
const Operator* Int32Mod() WARN_UNUSED_RESULT;
const Operator* Int32UMod() WARN_UNUSED_RESULT;
const Operator* Int32LessThan() WARN_UNUSED_RESULT;
const Operator* Int32LessThanOrEqual() WARN_UNUSED_RESULT;
const Operator* Uint32LessThan() WARN_UNUSED_RESULT;
const Operator* Uint32LessThanOrEqual() WARN_UNUSED_RESULT;
const Operator* Int64Add() WARN_UNUSED_RESULT;
const Operator* Int64Sub() WARN_UNUSED_RESULT;
const Operator* Int64Mul() WARN_UNUSED_RESULT;
const Operator* Int64Div() WARN_UNUSED_RESULT;
const Operator* Int64UDiv() WARN_UNUSED_RESULT;
const Operator* Int64Mod() WARN_UNUSED_RESULT;
const Operator* Int64UMod() WARN_UNUSED_RESULT;
const Operator* Int64LessThan() WARN_UNUSED_RESULT;
const Operator* Int64LessThanOrEqual() WARN_UNUSED_RESULT;
// Convert representation of integers between float64 and int32/uint32.
// The precise rounding mode and handling of out of range inputs are *not*
// defined for these operators, since they are intended only for use with
// integers.
const Operator* ChangeInt32ToFloat64() { UNOP(ChangeInt32ToFloat64); }
const Operator* ChangeUint32ToFloat64() { UNOP(ChangeUint32ToFloat64); }
const Operator* ChangeFloat64ToInt32() { UNOP(ChangeFloat64ToInt32); }
const Operator* ChangeFloat64ToUint32() { UNOP(ChangeFloat64ToUint32); }
const Operator* ChangeInt32ToFloat64() WARN_UNUSED_RESULT;
const Operator* ChangeUint32ToFloat64() WARN_UNUSED_RESULT;
const Operator* ChangeFloat64ToInt32() WARN_UNUSED_RESULT;
const Operator* ChangeFloat64ToUint32() WARN_UNUSED_RESULT;
// Sign/zero extend int32/uint32 to int64/uint64.
const Operator* ChangeInt32ToInt64() { UNOP(ChangeInt32ToInt64); }
const Operator* ChangeUint32ToUint64() { UNOP(ChangeUint32ToUint64); }
const Operator* ChangeInt32ToInt64() WARN_UNUSED_RESULT;
const Operator* ChangeUint32ToUint64() WARN_UNUSED_RESULT;
// Truncate double to int32 using JavaScript semantics.
const Operator* TruncateFloat64ToInt32() { UNOP(TruncateFloat64ToInt32); }
const Operator* TruncateFloat64ToInt32() WARN_UNUSED_RESULT;
// Truncate the high order bits and convert the remaining bits to int32.
const Operator* TruncateInt64ToInt32() { UNOP(TruncateInt64ToInt32); }
const Operator* TruncateInt64ToInt32() WARN_UNUSED_RESULT;
// Floating point operators always operate with IEEE 754 round-to-nearest.
const Operator* Float64Add() { BINOP_C(Float64Add); }
const Operator* Float64Sub() { BINOP(Float64Sub); }
const Operator* Float64Mul() { BINOP_C(Float64Mul); }
const Operator* Float64Div() { BINOP(Float64Div); }
const Operator* Float64Mod() { BINOP(Float64Mod); }
const Operator* Float64Add() WARN_UNUSED_RESULT;
const Operator* Float64Sub() WARN_UNUSED_RESULT;
const Operator* Float64Mul() WARN_UNUSED_RESULT;
const Operator* Float64Div() WARN_UNUSED_RESULT;
const Operator* Float64Mod() WARN_UNUSED_RESULT;
// Floating point comparisons complying to IEEE 754.
const Operator* Float64Equal() { BINOP_C(Float64Equal); }
const Operator* Float64LessThan() { BINOP(Float64LessThan); }
const Operator* Float64LessThanOrEqual() { BINOP(Float64LessThanOrEqual); }
const Operator* Float64Equal() WARN_UNUSED_RESULT;
const Operator* Float64LessThan() WARN_UNUSED_RESULT;
const Operator* Float64LessThanOrEqual() WARN_UNUSED_RESULT;
inline bool is32() const { return word_ == kRepWord32; }
inline bool is64() const { return word_ == kRepWord64; }
inline MachineType word() const { return word_; }
// load [base + index]
const Operator* Load(LoadRepresentation rep) WARN_UNUSED_RESULT;
#undef WORD_SIZE
#undef INT_SIZE
#undef UNOP
#undef BINOP
#undef OP1
#undef SIMPLE
// store [base + index], value
const Operator* Store(StoreRepresentation rep) WARN_UNUSED_RESULT;
// Target machine word-size assumed by this builder.
bool Is32() const { return word() == kRepWord32; }
bool Is64() const { return word() == kRepWord64; }
MachineType word() const { return word_; }
// Pseudo operators that translate to 32/64-bit operators depending on the
// word-size of the target machine assumed by this builder.
#define PSEUDO_OP_LIST(V) \
V(Word, And) \
V(Word, Or) \
V(Word, Xor) \
V(Word, Shl) \
V(Word, Shr) \
V(Word, Sar) \
V(Word, Ror) \
V(Word, Equal) \
V(Int, Add) \
V(Int, Sub) \
V(Int, Mul) \
V(Int, Div) \
V(Int, UDiv) \
V(Int, Mod) \
V(Int, UMod) \
V(Int, LessThan) \
V(Int, LessThanOrEqual)
#define PSEUDO_OP(Prefix, Suffix) \
const Operator* Prefix##Suffix() { \
return Is32() ? Prefix##32##Suffix() : Prefix##64##Suffix(); \
}
PSEUDO_OP_LIST(PSEUDO_OP)
#undef PSEUDO_OP
#undef PSEUDO_OP_LIST
private:
Zone* zone_;
MachineType word_;
const MachineOperatorBuilderImpl& impl_;
const MachineType word_;
};
} // namespace compiler
......
......@@ -36,7 +36,22 @@ enum MachineType {
kTypeInt64 = 1 << 11,
kTypeUint64 = 1 << 12,
kTypeNumber = 1 << 13,
kTypeAny = 1 << 14
kTypeAny = 1 << 14,
// Machine types.
kMachNone = 0,
kMachFloat32 = kRepFloat32 | kTypeNumber,
kMachFloat64 = kRepFloat64 | kTypeNumber,
kMachInt8 = kRepWord8 | kTypeInt32,
kMachUint8 = kRepWord8 | kTypeUint32,
kMachInt16 = kRepWord16 | kTypeInt32,
kMachUint16 = kRepWord16 | kTypeUint32,
kMachInt32 = kRepWord32 | kTypeInt32,
kMachUint32 = kRepWord32 | kTypeUint32,
kMachInt64 = kRepWord64 | kTypeInt64,
kMachUint64 = kRepWord64 | kTypeUint64,
kMachPtr = (kPointerSize == 4) ? kRepWord32 : kRepWord64,
kMachAnyTagged = kRepTagged | kTypeAny
};
OStream& operator<<(OStream& os, const MachineType& type);
......@@ -51,30 +66,6 @@ const MachineTypeUnion kTypeMask = kTypeBool | kTypeInt32 | kTypeUint32 |
kTypeInt64 | kTypeUint64 | kTypeNumber |
kTypeAny;
const MachineType kMachNone = static_cast<MachineType>(0);
const MachineType kMachFloat32 =
static_cast<MachineType>(kRepFloat32 | kTypeNumber);
const MachineType kMachFloat64 =
static_cast<MachineType>(kRepFloat64 | kTypeNumber);
const MachineType kMachInt8 = static_cast<MachineType>(kRepWord8 | kTypeInt32);
const MachineType kMachUint8 =
static_cast<MachineType>(kRepWord8 | kTypeUint32);
const MachineType kMachInt16 =
static_cast<MachineType>(kRepWord16 | kTypeInt32);
const MachineType kMachUint16 =
static_cast<MachineType>(kRepWord16 | kTypeUint32);
const MachineType kMachInt32 =
static_cast<MachineType>(kRepWord32 | kTypeInt32);
const MachineType kMachUint32 =
static_cast<MachineType>(kRepWord32 | kTypeUint32);
const MachineType kMachInt64 =
static_cast<MachineType>(kRepWord64 | kTypeInt64);
const MachineType kMachUint64 =
static_cast<MachineType>(kRepWord64 | kTypeUint64);
const MachineType kMachPtr = kPointerSize == 4 ? kRepWord32 : kRepWord64;
const MachineType kMachAnyTagged =
static_cast<MachineType>(kRepTagged | kTypeAny);
// Gets only the type of the given type.
inline MachineType TypeOf(MachineType machine_type) {
int result = machine_type & kTypeMask;
......
......@@ -110,21 +110,21 @@ OStream& operator<<(OStream& os, const Operator& op);
// An implementation of Operator that has no static parameters. Such operators
// have just a name, an opcode, and a fixed number of inputs and outputs.
// They can represented by singletons and shared globally.
class SimpleOperator FINAL : public Operator {
class SimpleOperator : public Operator {
public:
SimpleOperator(Opcode opcode, Properties properties, int input_count,
int output_count, const char* mnemonic);
~SimpleOperator();
virtual bool Equals(const Operator* that) const OVERRIDE {
virtual bool Equals(const Operator* that) const FINAL {
return opcode() == that->opcode();
}
virtual int HashCode() const OVERRIDE { return opcode(); }
virtual int InputCount() const OVERRIDE { return input_count_; }
virtual int OutputCount() const OVERRIDE { return output_count_; }
virtual int HashCode() const FINAL { return opcode(); }
virtual int InputCount() const FINAL { return input_count_; }
virtual int OutputCount() const FINAL { return output_count_; }
private:
virtual OStream& PrintTo(OStream& os) const OVERRIDE { // NOLINT
virtual OStream& PrintTo(OStream& os) const FINAL { // NOLINT
return os << mnemonic();
}
......@@ -260,9 +260,6 @@ class Operator1 : public Operator {
T parameter_;
};
// Type definitions for operators with specific types of parameters.
typedef Operator1<Unique<Name> > NameOperator;
// Helper to extract parameters from Operator1<*> operator.
template <typename T>
......
......@@ -169,6 +169,7 @@ Handle<Code> Pipeline::GenerateCode() {
// construction. This is currently only needed for the node cache, which the
// typer could sweep over later.
Typer typer(zone());
MachineOperatorBuilder machine;
CommonOperatorBuilder common(zone());
JSGraph jsgraph(&graph, &common, &typer);
Node* context_node;
......@@ -256,7 +257,6 @@ Handle<Code> Pipeline::GenerateCode() {
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
Linkage linkage(info());
MachineOperatorBuilder machine(zone());
ValueNumberingReducer vn_reducer(zone());
SimplifiedOperatorReducer simple_reducer(&jsgraph, &machine);
ChangeLowering lowering(&jsgraph, &linkage, &machine);
......@@ -281,7 +281,6 @@ Handle<Code> Pipeline::GenerateCode() {
"generic lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
MachineOperatorBuilder machine(zone());
JSGenericLowering lowering(info(), &jsgraph, &machine);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&lowering);
......
......@@ -15,7 +15,7 @@ RawMachineAssembler::RawMachineAssembler(Graph* graph,
MachineType word)
: GraphBuilder(graph),
schedule_(new (zone()) Schedule(zone())),
machine_(zone(), word),
machine_(word),
common_(zone()),
machine_sig_(machine_sig),
call_descriptor_(
......
......@@ -108,7 +108,8 @@ class RawMachineAssembler : public GraphBuilder {
Store(rep, base, Int32Constant(0), value);
}
void Store(MachineType rep, Node* base, Node* index, Node* value) {
NewNode(machine()->Store(rep, kNoWriteBarrier), base, index, value);
NewNode(machine()->Store(StoreRepresentation(rep, kNoWriteBarrier)), base,
index, value);
}
// Arithmetic Operations.
Node* WordAnd(Node* a, Node* b) {
......@@ -137,14 +138,14 @@ class RawMachineAssembler : public GraphBuilder {
return WordBinaryNot(WordEqual(a, b));
}
Node* WordNot(Node* a) {
if (machine()->is32()) {
if (machine()->Is32()) {
return Word32Not(a);
} else {
return Word64Not(a);
}
}
Node* WordBinaryNot(Node* a) {
if (machine()->is32()) {
if (machine()->Is32()) {
return Word32BinaryNot(a);
} else {
return Word64BinaryNot(a);
......
......@@ -590,11 +590,11 @@ class RepresentationSelector {
case IrOpcode::kLoad: {
// TODO(titzer): machine loads/stores need to know BaseTaggedness!?
MachineType tBase = kRepTagged;
MachineType machine_type = OpParameter<MachineType>(node);
LoadRepresentation rep = OpParameter<LoadRepresentation>(node);
ProcessInput(node, 0, tBase); // pointer or object
ProcessInput(node, 1, kMachInt32); // index
ProcessRemainingInputs(node, 2);
SetOutput(node, machine_type);
SetOutput(node, rep);
break;
}
case IrOpcode::kStore: {
......@@ -603,7 +603,7 @@ class RepresentationSelector {
StoreRepresentation rep = OpParameter<StoreRepresentation>(node);
ProcessInput(node, 0, tBase); // pointer or object
ProcessInput(node, 1, kMachInt32); // index
ProcessInput(node, 2, rep.machine_type);
ProcessInput(node, 2, rep.machine_type());
ProcessRemainingInputs(node, 3);
SetOutput(node, 0);
break;
......@@ -814,7 +814,7 @@ void SimplifiedLowering::DoStoreField(Node* node) {
const FieldAccess& access = FieldAccessOf(node->op());
WriteBarrierKind kind = ComputeWriteBarrierKind(
access.base_is_tagged, access.machine_type, access.type);
node->set_op(machine_.Store(access.machine_type, kind));
node->set_op(machine_.Store(StoreRepresentation(access.machine_type, kind)));
Node* offset = jsgraph()->Int32Constant(access.offset - access.tag());
node->InsertInput(zone(), 1, offset);
}
......@@ -845,7 +845,7 @@ void SimplifiedLowering::DoStoreElement(Node* node) {
const ElementAccess& access = ElementAccessOf(node->op());
WriteBarrierKind kind = ComputeWriteBarrierKind(
access.base_is_tagged, access.machine_type, access.type);
node->set_op(machine_.Store(access.machine_type, kind));
node->set_op(machine_.Store(StoreRepresentation(access.machine_type, kind)));
node->ReplaceInput(1, ComputeIndex(access, node->InputAt(1)));
}
......
......@@ -16,8 +16,7 @@ namespace compiler {
class SimplifiedLowering {
public:
explicit SimplifiedLowering(JSGraph* jsgraph)
: jsgraph_(jsgraph), machine_(jsgraph->zone()) {}
explicit SimplifiedLowering(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
virtual ~SimplifiedLowering() {}
void LowerAllNodes();
......
......@@ -22,7 +22,7 @@ class SimplifiedOperatorReducerTest : public GraphTest {
protected:
Reduction Reduce(Node* node) {
Typer typer(zone());
MachineOperatorBuilder machine(zone());
MachineOperatorBuilder machine;
JSGraph jsgraph(graph(), common(), &typer);
SimplifiedOperatorReducer reducer(&jsgraph, &machine);
return reducer.Reduce(node);
......
......@@ -7,6 +7,8 @@
#include "src/compiler/machine-operator.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/handles.h"
#include "src/zone.h"
namespace v8 {
......
......@@ -56,8 +56,8 @@ class X64OperandGenerator FINAL : public OperandGenerator {
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
MachineType typ = TypeOf(OpParameter<MachineType>(node));
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
X64OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
......@@ -111,8 +111,8 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
MachineType rep = RepresentationOf(store_rep.machine_type);
if (store_rep.write_barrier_kind == kFullWriteBarrier) {
MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
......@@ -123,7 +123,7 @@ void InstructionSelector::VisitStore(Node* node) {
temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind);
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
InstructionOperand* val;
if (g.CanBeImmediate(value)) {
val = g.UseImmediate(value);
......
......@@ -5,8 +5,8 @@
#ifndef V8_HYDROGEN_UNIQUE_H_
#define V8_HYDROGEN_UNIQUE_H_
#include "src/handles.h"
#include "src/objects.h"
#include "src/handles-inl.h" // TODO(everyone): Fix our inl.h crap
#include "src/objects-inl.h" // TODO(everyone): Fix our inl.h crap
#include "src/string-stream.h"
#include "src/utils.h"
#include "src/zone.h"
......
......@@ -61,7 +61,6 @@ class GraphAndBuilders {
explicit GraphAndBuilders(Zone* zone)
: main_graph_(new (zone) Graph(zone)),
main_common_(zone),
main_machine_(zone),
main_simplified_(zone) {}
protected:
......
......@@ -222,9 +222,10 @@ TEST(RunChangeTaggedToFloat64) {
ChangesLoweringTester<int32_t> t(kMachAnyTagged);
double result;
t.BuildStoreAndLower(t.simplified()->ChangeTaggedToFloat64(),
t.machine()->Store(kMachFloat64, kNoWriteBarrier),
&result);
t.BuildStoreAndLower(
t.simplified()->ChangeTaggedToFloat64(),
t.machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
&result);
if (Pipeline::SupportedTarget()) {
FOR_INT32_INPUTS(i) {
......
......@@ -32,7 +32,6 @@ class InstructionTester : public HandleAndZoneScope {
info(static_cast<HydrogenCodeStub*>(NULL), main_isolate()),
linkage(&info),
common(zone()),
machine(zone()),
code(NULL) {}
~InstructionTester() { delete code; }
......
......@@ -21,7 +21,6 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
binop(NULL),
unop(NULL),
javascript(main_zone()),
machine(main_zone()),
simplified(main_zone()),
common(main_zone()),
graph(main_zone()),
......
......@@ -53,7 +53,6 @@ class ReducerTester : public HandleAndZoneScope {
: isolate(main_isolate()),
binop(NULL),
unop(NULL),
machine(main_zone()),
common(main_zone()),
graph(main_zone()),
typer(main_zone()),
......@@ -667,8 +666,9 @@ TEST(ReduceLoadStore) {
}
{
Node* store = R.graph.NewNode(R.machine.Store(kMachInt32, kNoWriteBarrier),
base, index, load);
Node* store = R.graph.NewNode(
R.machine.Store(StoreRepresentation(kMachInt32, kNoWriteBarrier)), base,
index, load);
MachineOperatorReducer reducer(&R.jsgraph);
Reduction reduction = reducer.Reduce(store);
CHECK(!reduction.Changed()); // stores should not be reduced.
......
......@@ -131,7 +131,7 @@ TEST(BuildMulNodeGraph) {
Schedule schedule(scope.main_zone());
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
MachineOperatorBuilder machine(scope.main_zone());
MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(0));
graph.SetStart(start);
......
......@@ -1509,7 +1509,7 @@ TEST(BuildScheduleSimpleLoopWithCodeMotion) {
Graph graph(scope.main_zone());
CommonOperatorBuilder common_builder(scope.main_zone());
JSOperatorBuilder js_builder(scope.main_zone());
MachineOperatorBuilder machine_builder(scope.main_zone());
MachineOperatorBuilder machine_builder;
const Operator* op;
Handle<Object> object =
......@@ -1668,7 +1668,7 @@ TEST(FloatingDiamond2) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
MachineOperatorBuilder machine(scope.main_zone());
MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
......@@ -1691,7 +1691,7 @@ TEST(FloatingDiamond3) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
MachineOperatorBuilder machine(scope.main_zone());
MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
......
......@@ -1265,9 +1265,9 @@ TEST(LowerStoreField_to_store) {
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
if (machine_reps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind);
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
CHECK_EQ(machine_reps[i], rep.machine_type);
CHECK_EQ(machine_reps[i], rep.machine_type());
}
}
......@@ -1312,9 +1312,9 @@ TEST(LowerStoreElement_to_store) {
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
if (machine_reps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind);
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
CHECK_EQ(machine_reps[i], rep.machine_type);
CHECK_EQ(machine_reps[i], rep.machine_type());
}
}
......
......@@ -426,6 +426,7 @@
'../../src/compiler/linkage.h',
'../../src/compiler/machine-operator-reducer.cc',
'../../src/compiler/machine-operator-reducer.h',
'../../src/compiler/machine-operator.cc',
'../../src/compiler/machine-operator.h',
'../../src/compiler/machine-type.cc',
'../../src/compiler/machine-type.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment