From 5899cc8ca7ef43c954ce15957acbf63ccfc66e36 Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Thu, 2 Oct 2014 08:38:37 +0000 Subject: [PATCH] [turbofan] Fix lowering of typed loads/stores. We can only access to external typed arrays; lowering of internal typed arrays would require a map check plus eager deoptimization. Also embed the array buffer reference directly instead of embedding the typed array. TEST=cctest,mjsunit,unittests R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/621863002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24386 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compiler/access-builder.cc | 16 +- src/compiler/js-typed-lowering.cc | 98 +++++----- src/compiler/simplified-operator.cc | 26 +++ src/compiler/simplified-operator.h | 7 +- src/factory.cc | 42 +++- src/factory.h | 5 + src/handles.h | 3 +- test/unittests/compiler/graph-unittest.cc | 216 ++++++++++++++++++++- test/unittests/compiler/graph-unittest.h | 25 +++ .../compiler/js-builtin-reducer-unittest.cc | 5 - .../compiler/js-typed-lowering-unittest.cc | 151 ++++++++++++++ test/unittests/test-utils.cc | 26 +++ test/unittests/test-utils.h | 3 + test/unittests/unittests.gyp | 1 + 14 files changed, 556 insertions(+), 68 deletions(-) create mode 100644 test/unittests/compiler/js-typed-lowering-unittest.cc diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc index 7f3b084..959c60a 100644 --- a/src/compiler/access-builder.cc +++ b/src/compiler/access-builder.cc @@ -11,43 +11,43 @@ namespace compiler { // static FieldAccess AccessBuilder::ForMap() { - return {kTaggedBase, HeapObject::kMapOffset, Handle(), Type::Any(), + return {kTaggedBase, HeapObject::kMapOffset, MaybeHandle(), Type::Any(), kMachAnyTagged}; } // static FieldAccess AccessBuilder::ForJSObjectProperties() { - return {kTaggedBase, JSObject::kPropertiesOffset, Handle(), Type::Any(), - kMachAnyTagged}; + return {kTaggedBase, JSObject::kPropertiesOffset, MaybeHandle(), + Type::Any(), kMachAnyTagged}; } // static FieldAccess AccessBuilder::ForJSObjectElements() { - return {kTaggedBase, JSObject::kElementsOffset, Handle(), + return {kTaggedBase, JSObject::kElementsOffset, MaybeHandle(), Type::Internal(), kMachAnyTagged}; } // static FieldAccess AccessBuilder::ForJSFunctionContext() { - return {kTaggedBase, JSFunction::kContextOffset, Handle(), + return {kTaggedBase, JSFunction::kContextOffset, MaybeHandle(), Type::Internal(), kMachAnyTagged}; } // static FieldAccess AccessBuilder::ForJSArrayBufferBackingStore() { - return {kTaggedBase, JSArrayBuffer::kBackingStoreOffset, Handle(), + return {kTaggedBase, JSArrayBuffer::kBackingStoreOffset, MaybeHandle(), Type::UntaggedPtr(), kMachPtr}; } // static FieldAccess AccessBuilder::ForExternalArrayPointer() { - return {kTaggedBase, ExternalArray::kExternalPointerOffset, Handle(), - Type::UntaggedPtr(), kMachPtr}; + return {kTaggedBase, ExternalArray::kExternalPointerOffset, + MaybeHandle(), Type::UntaggedPtr(), kMachPtr}; } diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index 749fee5..594bfd3 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -533,35 +533,34 @@ Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) { Type* key_type = NodeProperties::GetBounds(key).upper; Type* base_type = NodeProperties::GetBounds(base).upper; // TODO(mstarzinger): This lowering is not correct if: - // a) The typed array turns external (i.e. MaterializeArrayBuffer) - // b) The typed array or it's buffer is neutered. - // c) The index is out of bounds. + // a) The typed array or it's buffer is neutered. + // b) The index is out of bounds. if (base_type->IsConstant() && key_type->Is(Type::Integral32()) && base_type->AsConstant()->Value()->IsJSTypedArray()) { // JSLoadProperty(typed-array, int32) - 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, - NodeProperties::GetEffectInput(node)); - if (IsExternalArrayElementsKind(elements_kind)) { - elements = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForExternalArrayPointer()), - elements, NodeProperties::GetEffectInput(node)); - element_access = AccessBuilder::ForTypedArrayElement(type, true); - } else { - DCHECK(IsFixedTypedArrayElementsKind(elements_kind)); - element_access = AccessBuilder::ForTypedArrayElement(type, false); + Handle array = + Handle::cast(base_type->AsConstant()->Value()); + if (IsExternalArrayElementsKind(array->map()->elements_kind())) { + Handle buffer = + handle(JSArrayBuffer::cast(array->buffer())); + ExternalArrayType type = array->type(); + uint32_t length; + CHECK(array->length()->ToUint32(&length)); + Node* elements = + graph()->NewNode(simplified()->LoadField( + AccessBuilder::ForJSArrayBufferBackingStore()), + jsgraph()->HeapConstant(buffer), graph()->start()); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + node->set_op(simplified()->LoadElement( + AccessBuilder::ForTypedArrayElement(type, true))); + node->ReplaceInput(0, elements); + node->ReplaceInput(2, jsgraph()->Uint32Constant(length)); + node->ReplaceInput(3, effect); + node->ReplaceInput(4, control); + node->TrimInputCount(5); + return Changed(node); } - Node* value = graph()->NewNode( - simplified()->LoadElement(element_access), elements, key, - jsgraph()->Uint32Constant(length), NodeProperties::GetEffectInput(node), - NodeProperties::GetControlInput(node)); - return ReplaceEagerly(node, value); } return NoChange(); } @@ -574,35 +573,34 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { Type* key_type = NodeProperties::GetBounds(key).upper; Type* base_type = NodeProperties::GetBounds(base).upper; // TODO(mstarzinger): This lowering is not correct if: - // a) The typed array turns external (i.e. MaterializeArrayBuffer) - // b) The typed array or its buffer is neutered. + // a) The typed array or its buffer is neutered. if (key_type->Is(Type::Integral32()) && base_type->IsConstant() && base_type->AsConstant()->Value()->IsJSTypedArray()) { // JSStoreProperty(typed-array, int32, value) - 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, - NodeProperties::GetEffectInput(node)); - if (IsExternalArrayElementsKind(elements_kind)) { - elements = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForExternalArrayPointer()), - elements, NodeProperties::GetEffectInput(node)); - element_access = AccessBuilder::ForTypedArrayElement(type, true); - } else { - DCHECK(IsFixedTypedArrayElementsKind(elements_kind)); - element_access = AccessBuilder::ForTypedArrayElement(type, false); + Handle array = + Handle::cast(base_type->AsConstant()->Value()); + if (IsExternalArrayElementsKind(array->map()->elements_kind())) { + Handle buffer = + handle(JSArrayBuffer::cast(array->buffer())); + ExternalArrayType type = array->type(); + uint32_t length; + CHECK(array->length()->ToUint32(&length)); + Node* elements = + graph()->NewNode(simplified()->LoadField( + AccessBuilder::ForJSArrayBufferBackingStore()), + jsgraph()->HeapConstant(buffer), graph()->start()); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + node->set_op(simplified()->StoreElement( + AccessBuilder::ForTypedArrayElement(type, true))); + node->ReplaceInput(0, elements); + node->ReplaceInput(2, jsgraph()->Uint32Constant(length)); + node->ReplaceInput(3, value); + node->ReplaceInput(4, effect); + node->ReplaceInput(5, control); + node->TrimInputCount(6); + return Changed(node); } - Node* store = - graph()->NewNode(simplified()->StoreElement(element_access), elements, - key, jsgraph()->Uint32Constant(length), value, - NodeProperties::GetEffectInput(node), - NodeProperties::GetControlInput(node)); - return ReplaceEagerly(node, store); } return NoChange(); } diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index bb2e76b..d486eb8 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -27,6 +27,32 @@ std::ostream& operator<<(std::ostream& os, BaseTaggedness base_taggedness) { } +bool operator==(FieldAccess const& lhs, FieldAccess const& rhs) { + return lhs.base_is_tagged == rhs.base_is_tagged && lhs.offset == rhs.offset && + lhs.type == rhs.type && lhs.machine_type == rhs.machine_type; +} + + +bool operator!=(FieldAccess const& lhs, FieldAccess const& rhs) { + return !(lhs == rhs); +} + + +std::ostream& operator<<(std::ostream& os, FieldAccess const& access) { + os << "[" << access.base_is_tagged << ", " << access.offset << ", "; +#ifdef OBJECT_PRINT + Handle name; + if (access.name.ToHandle(&name)) { + name->Print(os); + os << ", "; + } +#endif + access.type->PrintTo(os); + os << ", " << access.machine_type << "]"; + return os; +} + + std::ostream& operator<<(std::ostream& os, BoundsCheckMode bounds_check_mode) { switch (bounds_check_mode) { case kNoBoundsCheck: diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index b05cb20..92e8a72 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -38,13 +38,18 @@ std::ostream& operator<<(std::ostream&, BaseTaggedness); struct FieldAccess { BaseTaggedness base_is_tagged; // specifies if the base pointer is tagged. int offset; // offset of the field, without tag. - Handle name; // debugging only. + MaybeHandle name; // debugging only. Type* type; // type of the field. MachineType machine_type; // machine type of the field. int tag() const { return base_is_tagged == kTaggedBase ? kHeapObjectTag : 0; } }; +bool operator==(FieldAccess const& lhs, FieldAccess const& rhs); +bool operator!=(FieldAccess const& lhs, FieldAccess const& rhs); + +std::ostream& operator<<(std::ostream&, FieldAccess const&); + enum BoundsCheckMode { kNoBoundsCheck, kTypedArrayBoundsCheck }; diff --git a/src/factory.cc b/src/factory.cc index 0adc873..25d8cd8 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1716,8 +1716,22 @@ Handle Factory::NewJSDataView() { } -static JSFunction* GetTypedArrayFun(ExternalArrayType type, - Isolate* isolate) { +namespace { + +ElementsKind GetExternalArrayElementsKind(ExternalArrayType type) { + switch (type) { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case kExternal##Type##Array: \ + return EXTERNAL_##TYPE##_ELEMENTS; + TYPED_ARRAYS(TYPED_ARRAY_CASE) + } + UNREACHABLE(); + return FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND; +#undef TYPED_ARRAY_CASE +} + + +JSFunction* GetTypedArrayFun(ExternalArrayType type, Isolate* isolate) { Context* native_context = isolate->context()->native_context(); switch (type) { #define TYPED_ARRAY_FUN(Type, type, TYPE, ctype, size) \ @@ -1733,6 +1747,8 @@ static JSFunction* GetTypedArrayFun(ExternalArrayType type, } } +} // namespace + Handle Factory::NewJSTypedArray(ExternalArrayType type) { Handle typed_array_fun_handle(GetTypedArrayFun(type, isolate())); @@ -1744,6 +1760,28 @@ Handle Factory::NewJSTypedArray(ExternalArrayType type) { } +Handle Factory::NewJSTypedArray(ExternalArrayType type, + Handle buffer, + size_t length) { + DCHECK(length <= static_cast(kMaxInt)); + Handle array = NewJSTypedArray(type); + array->set_buffer(*buffer); + array->set_weak_next(buffer->weak_first_view()); + buffer->set_weak_first_view(*array); + array->set_byte_offset(Smi::FromInt(0)); + array->set_byte_length(buffer->byte_length()); + Handle length_handle = NewNumberFromSize(length); + array->set_length(*length_handle); + Handle elements = + NewExternalArray(static_cast(length), type, buffer->backing_store()); + JSObject::SetMapAndElements(array, + JSObject::GetElementsTransitionMap( + array, GetExternalArrayElementsKind(type)), + elements); + return array; +} + + Handle Factory::NewJSProxy(Handle handler, Handle prototype) { // Allocate map. diff --git a/src/factory.h b/src/factory.h index 981dee0..8df53af 100644 --- a/src/factory.h +++ b/src/factory.h @@ -434,6 +434,11 @@ class Factory FINAL { Handle NewJSTypedArray(ExternalArrayType type); + // Creates a new JSTypedArray with the specified buffer. + Handle NewJSTypedArray(ExternalArrayType type, + Handle buffer, + size_t length); + Handle NewJSDataView(); // Allocates a Harmony proxy. diff --git a/src/handles.h b/src/handles.h index 577e83a..eb57f0e 100644 --- a/src/handles.h +++ b/src/handles.h @@ -53,7 +53,8 @@ class MaybeHandle { } // Convert to a Handle with a type that can be upcasted to. - template INLINE(bool ToHandle(Handle* out)) { + template + V8_INLINE bool ToHandle(Handle* out) const { if (location_ == NULL) { *out = Handle::null(); return false; diff --git a/test/unittests/compiler/graph-unittest.cc b/test/unittests/compiler/graph-unittest.cc index 5160e9a..27f694a 100644 --- a/test/unittests/compiler/graph-unittest.cc +++ b/test/unittests/compiler/graph-unittest.cc @@ -7,6 +7,7 @@ #include // NOLINT(readability/streams) #include "src/compiler/node-properties-inl.h" +#include "src/compiler/simplified-operator.h" using testing::_; using testing::MakeMatcher; @@ -68,8 +69,16 @@ Node* GraphTest::NumberConstant(volatile double value) { } +Node* GraphTest::HeapConstant(const Handle& value) { + return HeapConstant(Unique::CreateUninitialized(value)); +} + + Node* GraphTest::HeapConstant(const Unique& value) { - return graph()->NewNode(common()->HeapConstant(value)); + Node* node = graph()->NewNode(common()->HeapConstant(value)); + Type* type = Type::Constant(value.handle(), zone()); + NodeProperties::SetBounds(node, Bounds(type)); + return node; } @@ -85,6 +94,12 @@ Node* GraphTest::TrueConstant() { } +Node* GraphTest::UndefinedConstant() { + return HeapConstant( + Unique::CreateImmovable(factory()->undefined_value())); +} + + Matcher GraphTest::IsFalseConstant() { return IsHeapConstant( Unique::CreateImmovable(factory()->false_value())); @@ -430,6 +445,172 @@ class IsCallMatcher FINAL : public NodeMatcher { }; +class IsLoadFieldMatcher FINAL : public NodeMatcher { + public: + IsLoadFieldMatcher(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& effect_matcher) + : NodeMatcher(IrOpcode::kLoadField), + access_matcher_(access_matcher), + base_matcher_(base_matcher), + effect_matcher_(effect_matcher) {} + + virtual void DescribeTo(std::ostream* os) const OVERRIDE { + NodeMatcher::DescribeTo(os); + *os << " whose access ("; + access_matcher_.DescribeTo(os); + *os << "), base ("; + base_matcher_.DescribeTo(os); + *os << ") and effect ("; + effect_matcher_.DescribeTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(Node* node, + MatchResultListener* listener) const OVERRIDE { + return (NodeMatcher::MatchAndExplain(node, listener) && + PrintMatchAndExplain(OpParameter(node), "access", + access_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base", + base_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect", + effect_matcher_, listener)); + } + + private: + const Matcher access_matcher_; + const Matcher base_matcher_; + const Matcher effect_matcher_; +}; + + +class IsLoadElementMatcher FINAL : public NodeMatcher { + public: + IsLoadElementMatcher(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher) + : NodeMatcher(IrOpcode::kLoadElement), + access_matcher_(access_matcher), + base_matcher_(base_matcher), + index_matcher_(index_matcher), + length_matcher_(length_matcher), + effect_matcher_(effect_matcher), + control_matcher_(control_matcher) {} + + virtual void DescribeTo(std::ostream* os) const OVERRIDE { + NodeMatcher::DescribeTo(os); + *os << " whose access ("; + access_matcher_.DescribeTo(os); + *os << "), base ("; + base_matcher_.DescribeTo(os); + *os << "), index ("; + index_matcher_.DescribeTo(os); + *os << "), length ("; + length_matcher_.DescribeTo(os); + *os << "), effect ("; + effect_matcher_.DescribeTo(os); + *os << ") and control ("; + control_matcher_.DescribeTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(Node* node, + MatchResultListener* listener) const OVERRIDE { + return (NodeMatcher::MatchAndExplain(node, listener) && + PrintMatchAndExplain(OpParameter(node), "access", + access_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base", + base_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), + "index", index_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2), + "length", length_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect", + effect_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetControlInput(node), + "control", control_matcher_, listener)); + } + + private: + const Matcher access_matcher_; + const Matcher base_matcher_; + const Matcher index_matcher_; + const Matcher length_matcher_; + const Matcher effect_matcher_; + const Matcher control_matcher_; +}; + + +class IsStoreElementMatcher FINAL : public NodeMatcher { + public: + IsStoreElementMatcher(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& value_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher) + : NodeMatcher(IrOpcode::kStoreElement), + access_matcher_(access_matcher), + base_matcher_(base_matcher), + index_matcher_(index_matcher), + length_matcher_(length_matcher), + value_matcher_(value_matcher), + effect_matcher_(effect_matcher), + control_matcher_(control_matcher) {} + + virtual void DescribeTo(std::ostream* os) const OVERRIDE { + NodeMatcher::DescribeTo(os); + *os << " whose access ("; + access_matcher_.DescribeTo(os); + *os << "), base ("; + base_matcher_.DescribeTo(os); + *os << "), index ("; + index_matcher_.DescribeTo(os); + *os << "), length ("; + length_matcher_.DescribeTo(os); + *os << "), value ("; + value_matcher_.DescribeTo(os); + *os << "), effect ("; + effect_matcher_.DescribeTo(os); + *os << ") and control ("; + control_matcher_.DescribeTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(Node* node, + MatchResultListener* listener) const OVERRIDE { + return (NodeMatcher::MatchAndExplain(node, listener) && + PrintMatchAndExplain(OpParameter(node), "access", + access_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base", + base_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), + "index", index_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2), + "length", length_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 3), + "value", value_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect", + effect_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetControlInput(node), + "control", control_matcher_, listener)); + } + + private: + const Matcher access_matcher_; + const Matcher base_matcher_; + const Matcher index_matcher_; + const Matcher length_matcher_; + const Matcher value_matcher_; + const Matcher effect_matcher_; + const Matcher control_matcher_; +}; + + class IsLoadMatcher FINAL : public NodeMatcher { public: IsLoadMatcher(const Matcher& rep_matcher, @@ -715,6 +896,39 @@ Matcher IsCall(const Matcher& descriptor_matcher, } +Matcher IsLoadField(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& effect_matcher) { + return MakeMatcher( + new IsLoadFieldMatcher(access_matcher, base_matcher, effect_matcher)); +} + + +Matcher IsLoadElement(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher) { + return MakeMatcher(new IsLoadElementMatcher(access_matcher, base_matcher, + index_matcher, length_matcher, + effect_matcher, control_matcher)); +} + + +Matcher IsStoreElement(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& value_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher) { + return MakeMatcher(new IsStoreElementMatcher( + access_matcher, base_matcher, index_matcher, length_matcher, + value_matcher, effect_matcher, control_matcher)); +} + + Matcher IsLoad(const Matcher& rep_matcher, const Matcher& base_matcher, const Matcher& index_matcher, diff --git a/test/unittests/compiler/graph-unittest.h b/test/unittests/compiler/graph-unittest.h index 18d3ca5..41a91f8 100644 --- a/test/unittests/compiler/graph-unittest.h +++ b/test/unittests/compiler/graph-unittest.h @@ -15,12 +15,19 @@ namespace v8 { namespace internal { // Forward declarations. +template +class Handle; class HeapObject; template class Unique; namespace compiler { +// Forward declarations. +struct ElementAccess; +struct FieldAccess; + + using ::testing::Matcher; @@ -36,9 +43,11 @@ class GraphTest : public TestWithContext, public TestWithZone { Node* Int32Constant(int32_t value); Node* Int64Constant(int64_t value); Node* NumberConstant(volatile double value); + Node* HeapConstant(const Handle& value); Node* HeapConstant(const Unique& value); Node* FalseConstant(); Node* TrueConstant(); + Node* UndefinedConstant(); Matcher IsFalseConstant(); Matcher IsTrueConstant(); @@ -88,6 +97,22 @@ Matcher IsNumberLessThan(const Matcher& lhs_matcher, const Matcher& rhs_matcher); Matcher IsNumberSubtract(const Matcher& lhs_matcher, const Matcher& rhs_matcher); +Matcher IsLoadField(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& effect_matcher); +Matcher IsLoadElement(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher); +Matcher IsStoreElement(const Matcher& access_matcher, + const Matcher& base_matcher, + const Matcher& index_matcher, + const Matcher& length_matcher, + const Matcher& value_matcher, + const Matcher& effect_matcher, + const Matcher& control_matcher); Matcher IsLoad(const Matcher& rep_matcher, const Matcher& base_matcher, diff --git a/test/unittests/compiler/js-builtin-reducer-unittest.cc b/test/unittests/compiler/js-builtin-reducer-unittest.cc index c72978f..541dd09 100644 --- a/test/unittests/compiler/js-builtin-reducer-unittest.cc +++ b/test/unittests/compiler/js-builtin-reducer-unittest.cc @@ -35,11 +35,6 @@ class JSBuiltinReducerTest : public GraphTest { return n; } - Node* UndefinedConstant() { - return HeapConstant( - Unique::CreateImmovable(factory()->undefined_value())); - } - JSOperatorBuilder* javascript() { return &javascript_; } private: diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc new file mode 100644 index 0000000..c9b0830 --- /dev/null +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -0,0 +1,151 @@ +// 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/access-builder.h" +#include "src/compiler/js-graph.h" +#include "src/compiler/js-operator.h" +#include "src/compiler/js-typed-lowering.h" +#include "src/compiler/machine-operator.h" +#include "src/compiler/node-properties-inl.h" +#include "src/compiler/typer.h" +#include "test/unittests/compiler/compiler-test-utils.h" +#include "test/unittests/compiler/graph-unittest.h" + +namespace v8 { +namespace internal { +namespace compiler { + +namespace { + +const ExternalArrayType kExternalArrayTypes[] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) kExternal##Type##Array, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE +}; + + +const StrictMode kStrictModes[] = {SLOPPY, STRICT}; + +} // namespace + + +class JSTypedLoweringTest : public GraphTest { + public: + JSTypedLoweringTest() : GraphTest(3), javascript_(zone()) {} + virtual ~JSTypedLoweringTest() {} + + protected: + Reduction Reduce(Node* node) { + Typer typer(zone()); + MachineOperatorBuilder machine; + JSGraph jsgraph(graph(), common(), javascript(), &typer, &machine); + JSTypedLowering reducer(&jsgraph); + return reducer.Reduce(node); + } + + Node* Parameter(Type* type, int index = 0) { + Node* node = graph()->NewNode(common()->Parameter(index), graph()->start()); + NodeProperties::SetBounds(node, Bounds(Type::None(), type)); + return node; + } + + Handle NewArrayBuffer(void* bytes, size_t byte_length) { + Handle buffer = factory()->NewJSArrayBuffer(); + Runtime::SetupArrayBuffer(isolate(), buffer, true, bytes, byte_length); + return buffer; + } + + JSOperatorBuilder* javascript() { return &javascript_; } + + private: + JSOperatorBuilder javascript_; +}; + + +// ----------------------------------------------------------------------------- +// JSLoadProperty + + +TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) { + const size_t kLength = 17; + uint8_t backing_store[kLength * 8]; + Handle buffer = + NewArrayBuffer(backing_store, arraysize(backing_store)); + TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) { + Handle array = + factory()->NewJSTypedArray(type, buffer, kLength); + + Node* key = Parameter(Type::Integral32()); + Node* context = UndefinedConstant(); + Node* effect = graph()->start(); + Node* control = graph()->start(); + Node* node = graph()->NewNode(javascript()->LoadProperty(), + HeapConstant(array), key, context); + if (FLAG_turbo_deoptimization) { + node->AppendInput(zone(), UndefinedConstant()); + } + node->AppendInput(zone(), effect); + node->AppendInput(zone(), control); + Reduction r = Reduce(node); + + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsLoadElement( + AccessBuilder::ForTypedArrayElement(type, true), + IsLoadField( + AccessBuilder::ForJSArrayBufferBackingStore(), + IsHeapConstant(Unique::CreateImmovable(buffer)), + effect), + key, IsInt32Constant(static_cast(kLength)), effect, control)); + } +} + + +// ----------------------------------------------------------------------------- +// JSStoreProperty + + +TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) { + const size_t kLength = 17; + uint8_t backing_store[kLength * 8]; + Handle buffer = + NewArrayBuffer(backing_store, arraysize(backing_store)); + TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) { + TRACED_FOREACH(StrictMode, strict_mode, kStrictModes) { + Handle array = + factory()->NewJSTypedArray(type, buffer, kLength); + + Node* key = Parameter(Type::Integral32()); + Node* value = Parameter(Type::Any()); + Node* context = UndefinedConstant(); + Node* effect = graph()->start(); + Node* control = graph()->start(); + Node* node = graph()->NewNode(javascript()->StoreProperty(strict_mode), + HeapConstant(array), key, value, context); + if (FLAG_turbo_deoptimization) { + node->AppendInput(zone(), UndefinedConstant()); + } + node->AppendInput(zone(), effect); + node->AppendInput(zone(), control); + Reduction r = Reduce(node); + + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsStoreElement( + AccessBuilder::ForTypedArrayElement(type, true), + IsLoadField( + AccessBuilder::ForJSArrayBufferBackingStore(), + IsHeapConstant(Unique::CreateImmovable(buffer)), + effect), + key, IsInt32Constant(static_cast(kLength)), value, effect, + control)); + } + } +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/test/unittests/test-utils.cc b/test/unittests/test-utils.cc index a3532dd..7fe0321 100644 --- a/test/unittests/test-utils.cc +++ b/test/unittests/test-utils.cc @@ -8,6 +8,32 @@ namespace v8 { +std::ostream& operator<<(std::ostream& os, ExternalArrayType type) { + switch (type) { + case kExternalInt8Array: + return os << "ExternalInt8Array"; + case kExternalUint8Array: + return os << "ExternalUint8Array"; + case kExternalInt16Array: + return os << "ExternalInt16Array"; + case kExternalUint16Array: + return os << "ExternalUint16Array"; + case kExternalInt32Array: + return os << "ExternalInt32Array"; + case kExternalUint32Array: + return os << "ExternalUint32Array"; + case kExternalFloat32Array: + return os << "ExternalFloat32Array"; + case kExternalFloat64Array: + return os << "ExternalFloat64Array"; + case kExternalUint8ClampedArray: + return os << "ExternalUint8ClampedArray"; + } + UNREACHABLE(); + return os; +} + + // static Isolate* TestWithIsolate::isolate_ = NULL; diff --git a/test/unittests/test-utils.h b/test/unittests/test-utils.h index e08974a..7fb94f3 100644 --- a/test/unittests/test-utils.h +++ b/test/unittests/test-utils.h @@ -12,6 +12,9 @@ namespace v8 { +std::ostream& operator<<(std::ostream&, ExternalArrayType); + + class TestWithIsolate : public ::testing::Test { public: TestWithIsolate(); diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index be34c83..ca1a833 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp @@ -44,6 +44,7 @@ 'compiler/instruction-selector-unittest.h', 'compiler/js-builtin-reducer-unittest.cc', 'compiler/js-operator-unittest.cc', + 'compiler/js-typed-lowering-unittest.cc', 'compiler/machine-operator-reducer-unittest.cc', 'compiler/machine-operator-unittest.cc', 'compiler/simplified-operator-reducer-unittest.cc', -- 2.7.4