Revision: 24463
Author:   [email protected]
Date:     Wed Oct  8 11:16:45 2014 UTC
Log: [turbofan] Properly emit bounds checks for typed array element loads.

Also fix an awfull bug in simplified lowering.

TEST=cctest,mjsunit/asm
[email protected]

Review URL: https://codereview.chromium.org/640603003
https://code.google.com/p/v8/source/detail?r=24463

Added:
 /branches/bleeding_edge/test/mjsunit/asm/float32array-outofbounds.js
 /branches/bleeding_edge/test/mjsunit/asm/float64array-outofbounds.js
 /branches/bleeding_edge/test/mjsunit/asm/int16array-outofbounds.js
 /branches/bleeding_edge/test/mjsunit/asm/int32array-outofbounds.js
 /branches/bleeding_edge/test/mjsunit/asm/uint8array-outofbounds.js
Modified:
 /branches/bleeding_edge/src/compiler/js-typed-lowering.cc
 /branches/bleeding_edge/src/compiler/representation-change.h
 /branches/bleeding_edge/src/compiler/simplified-lowering.cc
 /branches/bleeding_edge/src/compiler/simplified-lowering.h
 /branches/bleeding_edge/test/cctest/compiler/test-representation-change.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/asm/float32array-outofbounds.js Wed Oct 8 11:16:45 2014 UTC
@@ -0,0 +1,30 @@
+// 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.Float32Array(heap);
+  function load(i) {
+    i = i|0;
+    i = +MEM32[i >> 2];
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = +v;
+    MEM32[i >> 2] = v;
+  }
+  return { load: load, store: store };
+}
+
+var m = Module(this, {}, new ArrayBuffer(4));
+
+m.store(0, 42.0);
+for (var i = 1; i < 64; ++i) {
+  m.store(i * 4 * 32 * 1024, i);
+}
+assertEquals(42.0, m.load(0));
+for (var i = 1; i < 64; ++i) {
+  assertEquals(NaN, m.load(i * 4 * 32 * 1024));
+}
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/asm/float64array-outofbounds.js Wed Oct 8 11:16:45 2014 UTC
@@ -0,0 +1,30 @@
+// 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 MEM64 = new stdlib.Float64Array(heap);
+  function load(i) {
+    i = i|0;
+    i = +MEM64[i >> 3];
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = +v;
+    MEM64[i >> 3] = v;
+  }
+  return { load: load, store: store };
+}
+
+var m = Module(this, {}, new ArrayBuffer(8));
+
+m.store(0, 3.12);
+for (var i = 1; i < 64; ++i) {
+  m.store(i * 8 * 32 * 1024, i);
+}
+assertEquals(3.12, m.load(0));
+for (var i = 1; i < 64; ++i) {
+  assertEquals(NaN, m.load(i * 8 * 32 * 1024));
+}
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/asm/int16array-outofbounds.js Wed Oct 8 11:16:45 2014 UTC
@@ -0,0 +1,30 @@
+// 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 MEM16 = new stdlib.Int16Array(heap);
+  function load(i) {
+    i = i|0;
+    i = MEM16[i >> 1] | 0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM16[i >> 1] = v;
+  }
+  return { load: load, store: store };
+}
+
+var m = Module(this, {}, new ArrayBuffer(2));
+
+m.store(0, 32767);
+for (var i = 1; i < 64; ++i) {
+  m.store(i * 2 * 32 * 1024, i);
+}
+assertEquals(32767, m.load(0));
+for (var i = 1; i < 64; ++i) {
+  assertEquals(0, m.load(i * 2 * 32 * 1024));
+}
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/asm/int32array-outofbounds.js Wed Oct 8 11:16:45 2014 UTC
@@ -0,0 +1,30 @@
+// 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 load(i) {
+    i = i|0;
+    i = MEM32[i >> 2] | 0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM32[i >> 2] = v;
+  }
+  return { load: load, store: store };
+}
+
+var m = Module(this, {}, new ArrayBuffer(4));
+
+m.store(0, 0x12345678);
+for (var i = 1; i < 64; ++i) {
+  m.store(i * 4 * 32 * 1024, i);
+}
+assertEquals(0x12345678, m.load(0));
+for (var i = 1; i < 64; ++i) {
+  assertEquals(0, m.load(i * 4 * 32 * 1024));
+}
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/asm/uint8array-outofbounds.js Wed Oct 8 11:16:45 2014 UTC
@@ -0,0 +1,30 @@
+// 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 MEM8 = new stdlib.Uint8Array(heap);
+  function load(i) {
+    i = i|0;
+    i = MEM8[i] | 0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM8[i] = v;
+  }
+  return { load: load, store: store };
+}
+
+var m = Module(this, {}, new ArrayBuffer(1));
+
+m.store(0, 255);
+for (var i = 1; i < 64; ++i) {
+  m.store(i * 1 * 32 * 1024, i);
+}
+assertEquals(255, m.load(0));
+for (var i = 1; i < 64; ++i) {
+  assertEquals(0, m.load(i * 1 * 32 * 1024));
+}
=======================================
--- /branches/bleeding_edge/src/compiler/js-typed-lowering.cc Tue Oct 7 07:36:21 2014 UTC +++ /branches/bleeding_edge/src/compiler/js-typed-lowering.cc Wed Oct 8 11:16:45 2014 UTC
@@ -534,7 +534,6 @@
   Type* base_type = NodeProperties::GetBounds(base).upper;
   // TODO(mstarzinger): This lowering is not correct if:
   //   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)
=======================================
--- /branches/bleeding_edge/src/compiler/representation-change.h Wed Oct 1 10:39:11 2014 UTC +++ /branches/bleeding_edge/src/compiler/representation-change.h Wed Oct 8 11:16:45 2014 UTC
@@ -157,7 +157,7 @@
       node = jsgraph()->graph()->NewNode(op, node);
       op = machine()->TruncateFloat64ToFloat32();
     } else if (output_type & kRepFloat64) {
-      op = machine()->ChangeFloat32ToFloat64();
+      op = machine()->TruncateFloat64ToFloat32();
     } else {
       return TypeError(node, output_type, kRepFloat32);
     }
=======================================
--- /branches/bleeding_edge/src/compiler/simplified-lowering.cc Wed Oct 8 10:53:46 2014 UTC +++ /branches/bleeding_edge/src/compiler/simplified-lowering.cc Wed Oct 8 11:16:45 2014 UTC
@@ -4,6 +4,8 @@

 #include "src/compiler/simplified-lowering.h"

+#include <limits>
+
 #include "src/base/bits.h"
 #include "src/code-factory.h"
 #include "src/compiler/common-operator.h"
@@ -665,8 +667,15 @@
         ProcessInput(node, 1, kMachInt32);  // element index
         ProcessInput(node, 2, kMachInt32);  // length
         ProcessRemainingInputs(node, 3);
-        SetOutput(node, access.machine_type);
-        if (lower()) lowering->DoLoadElement(node);
+ // Tagged overrides everything if we have to do a typed array bounds
+        // check, because we may need to return undefined then.
+        MachineType output_type =
+            (access.bounds_check == kTypedArrayBoundsCheck &&
+             (use & kRepTagged))
+                ? kMachAnyTagged
+                : access.machine_type;
+        SetOutput(node, output_type);
+        if (lower()) lowering->DoLoadElement(node, output_type);
         break;
       }
       case IrOpcode::kStoreElement: {
@@ -957,11 +966,71 @@
 }


-void SimplifiedLowering::DoLoadElement(Node* node) {
+void SimplifiedLowering::DoLoadElement(Node* node, MachineType output_type) {
   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);
+  const Operator* op = machine()->Load(access.machine_type);
+  Node* key = node->InputAt(1);
+  Node* index = ComputeIndex(access, key);
+  if (access.bounds_check == kNoBoundsCheck) {
+    DCHECK_EQ(access.machine_type, output_type);
+    node->set_op(op);
+    node->ReplaceInput(1, index);
+    node->RemoveInput(2);
+  } else {
+    DCHECK_EQ(kTypedArrayBoundsCheck, access.bounds_check);
+
+    Node* base = node->InputAt(0);
+    Node* length = node->InputAt(2);
+    Node* effect = node->InputAt(3);
+    Node* control = node->InputAt(4);
+
+ Node* check = graph()->NewNode(machine()->Uint32LessThan(), key, length);
+    Node* branch = graph()->NewNode(common()->Branch(), check, control);
+
+    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+    Node* load = graph()->NewNode(op, base, index, effect, if_true);
+    Node* result = load;
+    if (output_type & kRepTagged) {
+      // TODO(turbofan): This is ugly as hell!
+      SimplifiedOperatorBuilder simplified(graph()->zone());
+      RepresentationChanger changer(jsgraph(), &simplified,
+                                    graph()->zone()->isolate());
+ result = changer.GetTaggedRepresentationFor(result, access.machine_type);
+    }
+
+    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+    Node* undefined;
+    if (output_type & kRepTagged) {
+      DCHECK(!(access.machine_type & kRepTagged));
+      undefined = jsgraph()->UndefinedConstant();
+    } else if (output_type & kRepFloat32) {
+      undefined =
+ jsgraph()->Float32Constant(std::numeric_limits<float>::quiet_NaN());
+    } else if (output_type & kRepFloat64) {
+      undefined =
+ jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN());
+    } else {
+      undefined = jsgraph()->Int32Constant(0);
+    }
+
+    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ Node* phi = graph()->NewNode(common()->EffectPhi(2), load, effect, merge);
+
+    // Replace effect uses of node with the effect phi.
+    for (UseIter i = node->uses().begin(); i != node->uses().end();) {
+      if (NodeProperties::IsEffectEdge(i.edge())) {
+        i = i.UpdateToAndIncrement(phi);
+      } else {
+        ++i;
+      }
+    }
+
+    node->set_op(common()->Phi(output_type, 2));
+    node->ReplaceInput(0, result);
+    node->ReplaceInput(1, undefined);
+    node->ReplaceInput(2, merge);
+    node->TrimInputCount(3);
+  }
 }


=======================================
--- /branches/bleeding_edge/src/compiler/simplified-lowering.h Tue Sep 16 16:20:10 2014 UTC +++ /branches/bleeding_edge/src/compiler/simplified-lowering.h Wed Oct 8 11:16:45 2014 UTC
@@ -14,17 +14,19 @@
 namespace internal {
 namespace compiler {

-class SimplifiedLowering {
+class SimplifiedLowering FINAL {
  public:
   explicit SimplifiedLowering(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
-  virtual ~SimplifiedLowering() {}
+  ~SimplifiedLowering() {}

   void LowerAllNodes();

// TODO(titzer): These are exposed for direct testing. Use a friend class.
   void DoLoadField(Node* node);
   void DoStoreField(Node* node);
-  void DoLoadElement(Node* node);
+  // TODO(turbofan): The output_type can be removed once the result of the
+  // representation analysis is stored in the node bounds.
+  void DoLoadElement(Node* node, MachineType output_type);
   void DoStoreElement(Node* node);
   void DoStringAdd(Node* node);
   void DoStringEqual(Node* node);
=======================================
--- /branches/bleeding_edge/test/cctest/compiler/test-representation-change.cc Thu Sep 25 05:17:38 2014 UTC +++ /branches/bleeding_edge/test/cctest/compiler/test-representation-change.cc Wed Oct 8 11:16:45 2014 UTC
@@ -429,6 +429,8 @@
   CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
               kRepWord32);

+ CheckChange(IrOpcode::kTruncateFloat64ToFloat32, kRepFloat64, kRepFloat32);
+
   // Int32,Uint32 <-> Float32 require two changes.
   CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeInt32,

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to