[turbofan] Eliminate typed array bounds checks if both key and length are constant.
authorbmeurer@chromium.org <bmeurer@chromium.org>
Fri, 17 Oct 2014 09:35:45 +0000 (09:35 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org>
Fri, 17 Oct 2014 09:35:45 +0000 (09:35 +0000)
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

src/compiler/node-matchers.h
src/compiler/pipeline.cc
src/compiler/simplified-operator-reducer.cc
src/compiler/simplified-operator-reducer.h
test/mjsunit/asm/int32array-constant-key.js [new file with mode: 0644]
test/unittests/compiler/simplified-operator-reducer-unittest.cc

index 718803f..3e64ddd 100644 (file)
@@ -96,6 +96,41 @@ typedef FloatMatcher<double, IrOpcode::kFloat64Constant> Float64Matcher;
 typedef FloatMatcher<double, IrOpcode::kNumberConstant> 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<int32_t>(node);
+        break;
+      case IrOpcode::kFloat32Constant:
+        has_value_ = true;
+        value_ = OpParameter<float>(node);
+        break;
+      case IrOpcode::kFloat64Constant:
+      case IrOpcode::kNumberConstant:
+        has_value_ = true;
+        value_ = OpParameter<double>(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 <typename T>
 struct HeapObjectMatcher FINAL
index 2669b18..354aa9d 100644 (file)
@@ -301,9 +301,13 @@ Handle<Code> 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");
index f6181ea..49b87b2 100644 (file)
@@ -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;
   }
index 32f49ad..88e9311 100644 (file)
@@ -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 (file)
index 0000000..b1fbd26
--- /dev/null
@@ -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());
index ac50241..694bdd8 100644 (file)
@@ -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