}
template <class B, class S>
+void GenericNode<B, S>::RemoveInput(int index) {
+ DCHECK(index >= 0 && index < InputCount());
+ // TODO(turbofan): Optimize this implementation!
+ for (; index < InputCount() - 1; ++index) {
+ ReplaceInput(index, InputAt(index + 1));
+ }
+ TrimInputCount(InputCount() - 1);
+}
+
+template <class B, class S>
void GenericNode<B, S>::AppendUse(Use* use) {
use->next = NULL;
use->prev = last_use_;
inline void ReplaceInput(int index, GenericNode* new_input);
inline void AppendInput(Zone* zone, GenericNode* new_input);
inline void InsertInput(Zone* zone, int index, GenericNode* new_input);
+ inline void RemoveInput(int index);
int UseCount() { return use_count_; }
S* UseAt(int index) {
inline void ReplaceUsesIf(UnaryPredicate pred, GenericNode* replace_to);
inline void RemoveAllInputs();
- void TrimInputCount(int input_count);
+ inline void TrimInputCount(int input_count);
class Inputs {
public:
if (has_frame_state) {
// Remove the frame state from inputs.
- // TODO(jarin) This should use Node::RemoveInput (which does not exist yet).
- int dest = NodeProperties::FirstFrameStateIndex(node);
- for (int i = NodeProperties::PastFrameStateIndex(node);
- i < node->InputCount(); i++) {
- node->ReplaceInput(dest, node->InputAt(i));
- dest++;
- }
- node->TrimInputCount(dest);
+ node->RemoveInput(NodeProperties::FirstFrameStateIndex(node));
}
ReplaceWithRuntimeCall(node, Runtime::kBooleanize);
JSTypedArray* array = JSTypedArray::cast(*base_type->AsConstant()->Value());
ElementsKind elements_kind = array->map()->elements_kind();
ExternalArrayType type = array->type();
+ uint32_t length;
+ CHECK(array->length()->ToUint32(&length));
ElementAccess element_access;
Node* elements = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()), base,
}
Node* value =
graph()->NewNode(simplified()->LoadElement(element_access), elements,
- key, NodeProperties::GetEffectInput(node));
+ key, jsgraph()->Uint32Constant(length),
+ NodeProperties::GetEffectInput(node));
return ReplaceEagerly(node, value);
}
return NoChange();
NodeProperties::GetControlInput(node));
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
- Node* store = graph()->NewNode(
- simplified()->StoreElement(element_access), elements, key, value,
- NodeProperties::GetEffectInput(node), if_true);
+ Node* store =
+ graph()->NewNode(simplified()->StoreElement(element_access), elements,
+ key, jsgraph()->Uint32Constant(length), value,
+ NodeProperties::GetEffectInput(node), if_true);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
ElementAccess access = ElementAccessOf(node->op());
ProcessInput(node, 0, changer_->TypeForBasePointer(access));
ProcessInput(node, 1, kMachInt32); // element index
- ProcessRemainingInputs(node, 2);
+ ProcessInput(node, 2, kMachInt32); // length
+ ProcessRemainingInputs(node, 3);
SetOutput(node, AssumeImplicitFloat32Change(access.machine_type));
if (lower()) lowering->DoLoadElement(node);
break;
ElementAccess access = ElementAccessOf(node->op());
ProcessInput(node, 0, changer_->TypeForBasePointer(access));
ProcessInput(node, 1, kMachInt32); // element index
- ProcessInput(node, 2, AssumeImplicitFloat32Change(access.machine_type));
- ProcessRemainingInputs(node, 3);
+ ProcessInput(node, 2, kMachInt32); // length
+ ProcessInput(node, 3, AssumeImplicitFloat32Change(access.machine_type));
+ ProcessRemainingInputs(node, 4);
SetOutput(node, 0);
if (lower()) lowering->DoStoreElement(node);
break;
const ElementAccess& access = ElementAccessOf(node->op());
node->set_op(machine()->Load(access.machine_type));
node->ReplaceInput(1, ComputeIndex(access, node->InputAt(1)));
+ node->RemoveInput(2);
}
node->set_op(
machine()->Store(StoreRepresentation(access.machine_type, kind)));
node->ReplaceInput(1, ComputeIndex(access, node->InputAt(1)));
+ node->RemoveInput(2);
}
namespace internal {
namespace compiler {
+// TODO(bmeurer): Drop once we use std::ostream instead of our OStream.
+inline std::ostream& operator<<(std::ostream& os, const ElementAccess& access) {
+ OStringStream ost;
+ ost << access;
+ return os << ost.c_str();
+}
+
+
// -----------------------------------------------------------------------------
// Pure operators.
INSTANTIATE_TEST_CASE_P(SimplifiedOperatorTest, SimplifiedPureOperatorTest,
::testing::ValuesIn(kPureOperators));
+
+// -----------------------------------------------------------------------------
+// Element access operators.
+
+namespace {
+
+const ElementAccess kElementAccesses[] = {
+ {kTaggedBase, FixedArray::kHeaderSize, Type::Any(), kMachAnyTagged},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachInt8},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachInt16},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachInt32},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachUint8},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachUint16},
+ {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
+ kMachUint32},
+ {kUntaggedBase, 0, Type::Signed32(), kMachInt8},
+ {kUntaggedBase, 0, Type::Unsigned32(), kMachUint8},
+ {kUntaggedBase, 0, Type::Signed32(), kMachInt16},
+ {kUntaggedBase, 0, Type::Unsigned32(), kMachUint16},
+ {kUntaggedBase, 0, Type::Signed32(), kMachInt32},
+ {kUntaggedBase, 0, Type::Unsigned32(), kMachUint32},
+ {kUntaggedBase, 0, Type::Number(), kRepFloat32},
+ {kUntaggedBase, 0, Type::Number(), kRepFloat64},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Signed32(),
+ kMachInt8},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Unsigned32(),
+ kMachUint8},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Signed32(),
+ kMachInt16},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Unsigned32(),
+ kMachUint16},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Signed32(),
+ kMachInt32},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Unsigned32(),
+ kMachUint32},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Number(),
+ kRepFloat32},
+ {kTaggedBase, FixedTypedArrayBase::kDataOffset, Type::Number(),
+ kRepFloat64}};
+
+} // namespace
+
+
+class SimplifiedElementAccessOperatorTest
+ : public TestWithZone,
+ public ::testing::WithParamInterface<ElementAccess> {};
+
+
+TEST_P(SimplifiedElementAccessOperatorTest, LoadElement) {
+ SimplifiedOperatorBuilder simplified(zone());
+ const ElementAccess& access = GetParam();
+ const Operator* op = simplified.LoadElement(access);
+
+ EXPECT_EQ(IrOpcode::kLoadElement, op->opcode());
+ EXPECT_EQ(Operator::kNoThrow | Operator::kNoWrite, op->properties());
+ EXPECT_EQ(access, ElementAccessOf(op));
+
+ EXPECT_EQ(3, OperatorProperties::GetValueInputCount(op));
+ EXPECT_EQ(1, OperatorProperties::GetEffectInputCount(op));
+ EXPECT_EQ(0, OperatorProperties::GetControlInputCount(op));
+ EXPECT_EQ(4, OperatorProperties::GetTotalInputCount(op));
+
+ EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
+ EXPECT_EQ(1, OperatorProperties::GetEffectOutputCount(op));
+ EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
+}
+
+
+TEST_P(SimplifiedElementAccessOperatorTest, StoreElement) {
+ SimplifiedOperatorBuilder simplified(zone());
+ const ElementAccess& access = GetParam();
+ const Operator* op = simplified.StoreElement(access);
+
+ EXPECT_EQ(IrOpcode::kStoreElement, op->opcode());
+ EXPECT_EQ(Operator::kNoRead | Operator::kNoThrow, op->properties());
+ EXPECT_EQ(access, ElementAccessOf(op));
+
+ EXPECT_EQ(4, OperatorProperties::GetValueInputCount(op));
+ EXPECT_EQ(1, OperatorProperties::GetEffectInputCount(op));
+ EXPECT_EQ(1, OperatorProperties::GetControlInputCount(op));
+ EXPECT_EQ(6, OperatorProperties::GetTotalInputCount(op));
+
+ EXPECT_EQ(0, OperatorProperties::GetValueOutputCount(op));
+ EXPECT_EQ(1, OperatorProperties::GetEffectOutputCount(op));
+ EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
+}
+
+
+INSTANTIATE_TEST_CASE_P(SimplifiedOperatorTest,
+ SimplifiedElementAccessOperatorTest,
+ ::testing::ValuesIn(kElementAccesses));
+
} // namespace compiler
} // namespace internal
} // namespace v8
namespace internal {
namespace compiler {
+OStream& operator<<(OStream& os, BaseTaggedness base_taggedness) {
+ switch (base_taggedness) {
+ case kUntaggedBase:
+ return os << "untagged base";
+ case kTaggedBase:
+ return os << "tagged base";
+ }
+ UNREACHABLE();
+ return os;
+}
+
+
+bool operator==(ElementAccess const& lhs, ElementAccess const& rhs) {
+ return lhs.base_is_tagged == rhs.base_is_tagged &&
+ lhs.header_size == rhs.header_size && lhs.type == rhs.type &&
+ lhs.machine_type == rhs.machine_type;
+}
+
+
+bool operator!=(ElementAccess const& lhs, ElementAccess const& rhs) {
+ return !(lhs == rhs);
+}
+
+
+OStream& operator<<(OStream& os, ElementAccess const& access) {
+ os << "[" << access.base_is_tagged << ", " << access.header_size << ", ";
+ access.type->PrintTo(os);
+ os << ", " << access.machine_type << "]";
+ return os;
+}
+
+
const FieldAccess& FieldAccessOf(const Operator* op) {
DCHECK_NOT_NULL(op);
DCHECK(op->opcode() == IrOpcode::kLoadField ||
// Specialization for static parameters of type {ElementAccess}.
template <>
struct StaticParameterTraits<ElementAccess> {
- static OStream& PrintTo(OStream& os, const ElementAccess& val) {
- return os << val.header_size;
+ static OStream& PrintTo(OStream& os, const ElementAccess& access) {
+ return os << access;
}
- static int HashCode(const ElementAccess& val) {
- return (val.header_size < 16) | (val.machine_type & 0xffff);
+ static int HashCode(const ElementAccess& access) {
+ return (access.header_size < 16) | (access.machine_type & 0xffff);
}
static bool Equals(const ElementAccess& lhs, const ElementAccess& rhs) {
return lhs.base_is_tagged == rhs.base_is_tagged &&
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 0) \
- V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1) \
- V(StoreElement, ElementAccess, Operator::kNoRead, 3, 0)
+ V(LoadElement, ElementAccess, Operator::kNoWrite, 3, 1) \
+ V(StoreElement, ElementAccess, Operator::kNoRead, 4, 0)
struct SimplifiedOperatorBuilderImpl FINAL {
enum BaseTaggedness { kUntaggedBase, kTaggedBase };
+OStream& operator<<(OStream&, BaseTaggedness);
+
// An access descriptor for loads/stores of fixed structures like field
// accesses of heap objects. Accesses from either tagged or untagged base
// pointers are supported; untagging is done automatically during lowering.
int tag() const { return base_is_tagged == kTaggedBase ? kHeapObjectTag : 0; }
};
+bool operator==(ElementAccess const& lhs, ElementAccess const& rhs);
+bool operator!=(ElementAccess const& lhs, ElementAccess const& rhs);
+
+OStream& operator<<(OStream&, ElementAccess const&);
+
// If the accessed object is not a heap object, add this to the header_size.
static const int kNonHeapObjectHeaderSize = kHeapObjectTag;
const Operator* LoadField(const FieldAccess&);
const Operator* StoreField(const FieldAccess&);
- const Operator* LoadElement(const ElementAccess&);
- const Operator* StoreElement(const ElementAccess&);
+
+ // load-element [base + index], length
+ const Operator* LoadElement(ElementAccess const&);
+
+ // store-element [base + index], length, value
+ const Operator* StoreElement(ElementAccess const&);
private:
Zone* zone() const { return zone_; }
Node* StoreField(const FieldAccess& access, Node* object, Node* value) {
return NewNode(simplified()->StoreField(access), object, value);
}
- Node* LoadElement(const ElementAccess& access, Node* object, Node* index) {
- return NewNode(simplified()->LoadElement(access), object, index);
+ Node* LoadElement(const ElementAccess& access, Node* object, Node* index,
+ Node* length) {
+ return NewNode(simplified()->LoadElement(access), object, index, length);
}
Node* StoreElement(const ElementAccess& access, Node* object, Node* index,
- Node* value) {
- return NewNode(simplified()->StoreElement(access), object, index, value);
+ Node* length, Node* value) {
+ return NewNode(simplified()->StoreElement(access), object, index, length,
+ value);
}
protected:
}
+TEST(RemoveInput) {
+ GraphTester graph;
+
+ Node* n0 = graph.NewNode(&dummy_operator);
+ Node* n1 = graph.NewNode(&dummy_operator, n0);
+ Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+
+ n1->RemoveInput(0);
+ CHECK_EQ(0, n1->InputCount());
+ CHECK_EQ(1, n0->UseCount());
+
+ n2->RemoveInput(0);
+ CHECK_EQ(1, n2->InputCount());
+ CHECK_EQ(0, n0->UseCount());
+ CHECK_EQ(1, n1->UseCount());
+
+ n2->RemoveInput(0);
+ CHECK_EQ(0, n2->InputCount());
+}
+
+
TEST(AppendInputsAndIterator) {
GraphTester graph;
TEST(RunLoadStoreFixedArrayIndex) {
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
ElementAccess access = AccessBuilder::ForFixedArrayElement();
- Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0));
- t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), load);
+ Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0),
+ t.Int32Constant(2));
+ t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), t.Int32Constant(2),
+ load);
t.Return(load);
t.LowerAllNodes();
TEST(RunLoadStoreArrayBuffer) {
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
const int index = 12;
+ const int array_length = 2 * index;
ElementAccess buffer_access =
AccessBuilder::ForBackingStoreElement(kMachInt8);
Node* backing_store = t.LoadField(
AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0));
Node* load =
- t.LoadElement(buffer_access, backing_store, t.Int32Constant(index));
+ t.LoadElement(buffer_access, backing_store, t.Int32Constant(index),
+ t.Int32Constant(array_length));
t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1),
- load);
+ t.Int32Constant(array_length), load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
if (Pipeline::SupportedTarget()) {
Handle<JSArrayBuffer> array = t.factory()->NewJSArrayBuffer();
- const int array_length = 2 * index;
Runtime::SetupArrayBufferAllocatingData(t.isolate(), array, array_length);
uint8_t* data = reinterpret_cast<uint8_t*>(array->backing_store());
for (int i = 0; i < array_length; i++) {
kMachAnyTagged};
SimplifiedLoweringTester<Object*> t;
- Node* load = t.LoadElement(access, t.PointerConstant(smis),
- t.Int32Constant(static_cast<int>(j)));
+ Node* load = t.LoadElement(
+ access, t.PointerConstant(smis), t.Int32Constant(static_cast<int>(j)),
+ t.Int32Constant(static_cast<int>(arraysize(smis))));
t.Return(load);
t.LowerAllNodes();
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
Node* p0 = t.Parameter(0);
t.StoreElement(access, t.PointerConstant(smis),
- t.Int32Constant(static_cast<int>(j)), p0);
+ t.Int32Constant(static_cast<int>(j)),
+ t.Int32Constant(static_cast<int>(arraysize(smis))), p0);
t.Return(p0);
t.LowerAllNodes();
SimplifiedLoweringTester<Object*> t;
Node* ptr = GetBaseNode(&t);
- Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index));
- t.StoreElement(access, ptr, t.Int32Constant(to_index), load);
+ Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index),
+ t.Int32Constant(num_elements));
+ t.StoreElement(access, ptr, t.Int32Constant(to_index),
+ t.Int32Constant(num_elements), load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
t.GenerateCode();
JSGraph jsgraph;
Node* p0;
Node* p1;
+ Node* p2;
Node* start;
Node* end;
Node* ret;
- explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None())
+ explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None(),
+ Type* p2_type = Type::None())
: GraphAndBuilders(main_zone()),
typer(main_zone()),
javascript(main_zone()),
graph()->SetEnd(end);
p0 = graph()->NewNode(common()->Parameter(0), start);
p1 = graph()->NewNode(common()->Parameter(1), start);
+ p2 = graph()->NewNode(common()->Parameter(2), start);
NodeProperties::SetBounds(p0, Bounds(p0_type));
NodeProperties::SetBounds(p1, Bounds(p1_type));
+ NodeProperties::SetBounds(p2, Bounds(p2_type));
}
void CheckLoweringBinop(IrOpcode::Value expected, const Operator* op) {
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
Type::Any(), machine_reps[i]};
- Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.start);
+ Node* load =
+ t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, t.p1,
+ t.jsgraph.Int32Constant(1024), t.start);
Node* use = t.Use(load, machine_reps[i]);
t.Return(use);
t.Lower();
Node* val = t.ExampleWithOutput(machine_reps[i]);
Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.p1, val, t.start, t.start);
+ t.p1, t.jsgraph.Int32Constant(1024), val,
+ t.start, t.start);
t.Effect(store);
t.Lower();
CHECK_EQ(IrOpcode::kStore, store->opcode());
TEST(InsertChangeForLoadElementIndex) {
- // LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged) =>
+ // LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length) =>
// Load(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k))
- TestingGraph t(Type::Any(), Type::Signed32());
+ TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.start);
+ t.p1, t.p2, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
TEST(InsertChangeForStoreElementIndex) {
- // StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, val) =>
+ // StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length, val) =>
// Store(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k), val)
- TestingGraph t(Type::Any(), Type::Signed32());
+ TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* store =
- t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1,
+ t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1, t.p2,
t.jsgraph.TrueConstant(), t.start, t.start);
t.Effect(store);
t.Lower();
TEST(InsertChangeForLoadElement) {
// TODO(titzer): test all load/store representation change insertions.
- TestingGraph t(Type::Any(), Type::Signed32());
+ TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachFloat64};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.start);
+ t.p1, t.p1, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
TEST(InsertChangeForStoreElement) {
// TODO(titzer): test all load/store representation change insertions.
- TestingGraph t(Type::Any(), Type::Signed32());
+ TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachFloat64};
- Node* store =
- t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.jsgraph.Int32Constant(0), t.p1, t.start, t.start);
+ Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
+ t.jsgraph.Int32Constant(0), t.p2, t.p1,
+ t.start, t.start);
t.Effect(store);
t.Lower();