From: bmeurer@chromium.org Date: Fri, 17 Oct 2014 09:35:45 +0000 (+0000) Subject: [turbofan] Eliminate typed array bounds checks if both key and length are constant. X-Git-Tag: upstream/4.7.83~6296 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a77915026062ce48cf02c046092c761fbf83356c;p=platform%2Fupstream%2Fv8.git [turbofan] Eliminate typed array bounds checks if both key and length are constant. TEST=mjsunit,unittests R=dcarney@chromium.org Review URL: https://codereview.chromium.org/638853004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24685 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/compiler/node-matchers.h b/src/compiler/node-matchers.h index 718803f..3e64ddd 100644 --- a/src/compiler/node-matchers.h +++ b/src/compiler/node-matchers.h @@ -96,6 +96,41 @@ typedef FloatMatcher Float64Matcher; typedef FloatMatcher NumberMatcher; +// A pattern matcher for any numberic constant. +struct NumericValueMatcher : public NodeMatcher { + explicit NumericValueMatcher(Node* const node) : NodeMatcher(node) { + switch (opcode()) { + case IrOpcode::kInt32Constant: + has_value_ = true; + value_ = OpParameter(node); + break; + case IrOpcode::kFloat32Constant: + has_value_ = true; + value_ = OpParameter(node); + break; + case IrOpcode::kFloat64Constant: + case IrOpcode::kNumberConstant: + has_value_ = true; + value_ = OpParameter(node); + break; + default: + has_value_ = false; + break; + } + } + + bool HasValue() const { return has_value_; } + double Value() const { + DCHECK(HasValue()); + return value_; + } + + private: + double value_; + bool has_value_; +}; + + // A pattern matcher for heap object constants. template struct HeapObjectMatcher FINAL diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 2669b18..354aa9d 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -301,9 +301,13 @@ Handle Pipeline::GenerateCode() { "typed lowering"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); + ValueNumberingReducer vn_reducer(zone()); JSTypedLowering lowering(&jsgraph); + SimplifiedOperatorReducer simple_reducer(&jsgraph); GraphReducer graph_reducer(&graph); + graph_reducer.AddReducer(&vn_reducer); graph_reducer.AddReducer(&lowering); + graph_reducer.AddReducer(&simple_reducer); graph_reducer.ReduceGraph(); VerifyAndPrintGraph(&graph, "Lowered typed"); diff --git a/src/compiler/simplified-operator-reducer.cc b/src/compiler/simplified-operator-reducer.cc index f6181ea..49b87b2 100644 --- a/src/compiler/simplified-operator-reducer.cc +++ b/src/compiler/simplified-operator-reducer.cc @@ -12,6 +12,10 @@ namespace v8 { namespace internal { namespace compiler { +SimplifiedOperatorReducer::SimplifiedOperatorReducer(JSGraph* jsgraph) + : jsgraph_(jsgraph), simplified_(jsgraph->zone()) {} + + SimplifiedOperatorReducer::~SimplifiedOperatorReducer() {} @@ -95,6 +99,38 @@ Reduction SimplifiedOperatorReducer::Reduce(Node* node) { if (m.HasValue()) return ReplaceNumber(FastUI2D(m.Value())); break; } + case IrOpcode::kLoadElement: { + ElementAccess access = ElementAccessOf(node->op()); + if (access.bounds_check == kTypedArrayBoundsCheck) { + NumericValueMatcher mkey(node->InputAt(1)); + NumericValueMatcher mlength(node->InputAt(2)); + if (mkey.HasValue() && mlength.HasValue()) { + // Skip the typed array bounds check if key and length are constant. + if (mkey.Value() < mlength.Value()) { + access.bounds_check = kNoBoundsCheck; + node->set_op(simplified()->LoadElement(access)); + return Changed(node); + } + } + } + break; + } + case IrOpcode::kStoreElement: { + ElementAccess access = ElementAccessOf(node->op()); + if (access.bounds_check == kTypedArrayBoundsCheck) { + NumericValueMatcher mkey(node->InputAt(1)); + NumericValueMatcher mlength(node->InputAt(2)); + if (mkey.HasValue() && mlength.HasValue()) { + // Skip the typed array bounds check if key and length are constant. + if (mkey.Value() < mlength.Value()) { + access.bounds_check = kNoBoundsCheck; + node->set_op(simplified()->StoreElement(access)); + return Changed(node); + } + } + } + break; + } default: break; } diff --git a/src/compiler/simplified-operator-reducer.h b/src/compiler/simplified-operator-reducer.h index 32f49ad..88e9311 100644 --- a/src/compiler/simplified-operator-reducer.h +++ b/src/compiler/simplified-operator-reducer.h @@ -6,6 +6,7 @@ #define V8_COMPILER_SIMPLIFIED_OPERATOR_REDUCER_H_ #include "src/compiler/graph-reducer.h" +#include "src/compiler/simplified-operator.h" namespace v8 { namespace internal { @@ -21,7 +22,7 @@ class MachineOperatorBuilder; class SimplifiedOperatorReducer FINAL : public Reducer { public: - explicit SimplifiedOperatorReducer(JSGraph* jsgraph) : jsgraph_(jsgraph) {} + explicit SimplifiedOperatorReducer(JSGraph* jsgraph); virtual ~SimplifiedOperatorReducer(); virtual Reduction Reduce(Node* node) OVERRIDE; @@ -40,8 +41,10 @@ class SimplifiedOperatorReducer FINAL : public Reducer { Factory* factory() const; JSGraph* jsgraph() const { return jsgraph_; } MachineOperatorBuilder* machine() const; + SimplifiedOperatorBuilder* simplified() { return &simplified_; } JSGraph* jsgraph_; + SimplifiedOperatorBuilder simplified_; DISALLOW_COPY_AND_ASSIGN(SimplifiedOperatorReducer); }; diff --git a/test/mjsunit/asm/int32array-constant-key.js b/test/mjsunit/asm/int32array-constant-key.js new file mode 100644 index 0000000..b1fbd26 --- /dev/null +++ b/test/mjsunit/asm/int32array-constant-key.js @@ -0,0 +1,32 @@ +// 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. + +function Module(stdlib, foreign, heap) { + "use asm"; + var MEM32 = new stdlib.Int32Array(heap); + function load0() { + return MEM32[0]; + } + function load4() { + return MEM32[4]; + } + function store0(v) { + MEM32[0] = v; + } + function store4(v) { + MEM32[4] = v; + } + return { load0: load0, store0: store0, load4: load4, store4: store4 }; +} + +var m = Module(this, {}, new ArrayBuffer(4)); + +assertEquals(0, m.load0()); +assertEquals(undefined, m.load4()); +m.store0(0x12345678); +assertEquals(0x12345678, m.load0()); +assertEquals(undefined, m.load4()); +m.store4(43); +assertEquals(0x12345678, m.load0()); +assertEquals(undefined, m.load4()); diff --git a/test/unittests/compiler/simplified-operator-reducer-unittest.cc b/test/unittests/compiler/simplified-operator-reducer-unittest.cc index ac50241..694bdd8 100644 --- a/test/unittests/compiler/simplified-operator-reducer-unittest.cc +++ b/test/unittests/compiler/simplified-operator-reducer-unittest.cc @@ -6,6 +6,7 @@ #include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator-reducer.h" #include "src/conversions.h" +#include "src/types.h" #include "test/unittests/compiler/graph-unittest.h" namespace v8 { @@ -476,6 +477,64 @@ TEST_F(SimplifiedOperatorReducerTest, ChangeUint32ToTagged) { } } + +// ----------------------------------------------------------------------------- +// LoadElement + + +TEST_F(SimplifiedOperatorReducerTest, LoadElementWithConstantKeyAndLength) { + ElementAccess const access = {kTypedArrayBoundsCheck, kUntaggedBase, 0, + Type::Any(), kMachAnyTagged}; + ElementAccess access_nocheck = access; + access_nocheck.bounds_check = kNoBoundsCheck; + Node* const base = Parameter(0); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + TRACED_FOREACH(double, key, kFloat64Values) { + TRACED_FOREACH(int32_t, length, kInt32Values) { + if (key < length) { + Reduction r = Reduce(graph()->NewNode( + simplified()->LoadElement(access), base, NumberConstant(key), + Int32Constant(length), effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsLoadElement(access_nocheck, base, IsNumberConstant(key), + IsInt32Constant(length), effect, control)); + } + } + } +} + + +// ----------------------------------------------------------------------------- +// StoreElement + + +TEST_F(SimplifiedOperatorReducerTest, StoreElementWithConstantKeyAndLength) { + ElementAccess const access = {kTypedArrayBoundsCheck, kUntaggedBase, 0, + Type::Any(), kMachAnyTagged}; + ElementAccess access_nocheck = access; + access_nocheck.bounds_check = kNoBoundsCheck; + Node* const base = Parameter(0); + Node* const value = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + TRACED_FOREACH(int32_t, key, kInt32Values) { + TRACED_FOREACH(double, length, kFloat64Values) { + if (key < length) { + Reduction r = Reduce(graph()->NewNode( + simplified()->StoreElement(access), base, Int32Constant(key), + NumberConstant(length), value, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsStoreElement(access_nocheck, base, IsInt32Constant(key), + IsNumberConstant(length), value, effect, control)); + } + } + } +} + } // namespace compiler } // namespace internal } // namespace v8