[turbofan] Lower JSStoreProperty during JS typed lowering.
authorbmeurer@chromium.org <bmeurer@chromium.org>
Wed, 10 Sep 2014 12:29:48 +0000 (12:29 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org>
Wed, 10 Sep 2014 12:29:48 +0000 (12:29 +0000)
Note that we cannot yet emit a diamond here (patch is ready), because
the scheduler is still broken wrt. free floating control (seems related
although this diamond is not free floating).

TEST=cctest
R=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/557123005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23838 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/compiler/js-typed-lowering.cc
src/compiler/js-typed-lowering.h
test/cctest/compiler/test-run-properties.cc

index a8b4823..c4e7b2b 100644 (file)
@@ -28,9 +28,12 @@ static void RelaxEffects(Node* node) {
 }
 
 
+JSTypedLowering::~JSTypedLowering() {}
+
+
 Reduction JSTypedLowering::ReplaceEagerly(Node* old, Node* node) {
   NodeProperties::ReplaceWithValue(old, node, node);
-  return Reducer::Changed(node);
+  return Changed(node);
 }
 
 
@@ -500,7 +503,7 @@ Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) {
 }
 
 
-Reduction JSTypedLowering::ReduceJSPropertyLoad(Node* node) {
+Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
   Node* key = NodeProperties::GetValueInput(node, 1);
   Node* base = NodeProperties::GetValueInput(node, 0);
   Type* key_type = NodeProperties::GetBounds(key).upper;
@@ -537,6 +540,45 @@ Reduction JSTypedLowering::ReduceJSPropertyLoad(Node* node) {
 }
 
 
+Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
+  Node* key = NodeProperties::GetValueInput(node, 1);
+  Node* base = NodeProperties::GetValueInput(node, 0);
+  Node* value = NodeProperties::GetValueInput(node, 2);
+  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
+  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();
+    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);
+    }
+    Node* store =
+        graph()->NewNode(simplified()->StoreElement(element_access), elements,
+                         key, value, NodeProperties::GetEffectInput(node),
+                         NodeProperties::GetControlInput(node));
+    return ReplaceEagerly(node, store);
+  }
+  return NoChange();
+}
+
+
 static Reduction ReplaceWithReduction(Node* node, Reduction reduction) {
   if (reduction.Changed()) {
     NodeProperties::ReplaceWithValue(node, reduction.replacement());
@@ -612,12 +654,15 @@ Reduction JSTypedLowering::Reduce(Node* node) {
       return ReplaceWithReduction(node,
                                   ReduceJSToStringInput(node->InputAt(0)));
     case IrOpcode::kJSLoadProperty:
-      return ReduceJSPropertyLoad(node);
+      return ReduceJSLoadProperty(node);
+    case IrOpcode::kJSStoreProperty:
+      return ReduceJSStoreProperty(node);
     default:
       break;
   }
   return NoChange();
 }
-}
-}
-}  // namespace v8::internal::compiler
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
index 331347c..e2c464a 100644 (file)
@@ -16,30 +16,28 @@ namespace internal {
 namespace compiler {
 
 // Lowers JS-level operators to simplified operators based on types.
-class JSTypedLowering : public Reducer {
+class JSTypedLowering FINAL : public Reducer {
  public:
   explicit JSTypedLowering(JSGraph* jsgraph)
       : jsgraph_(jsgraph),
         simplified_(jsgraph->zone()),
         machine_(jsgraph->zone()) {}
-  virtual ~JSTypedLowering() {}
+  virtual ~JSTypedLowering();
 
-  virtual Reduction Reduce(Node* node);
+  virtual Reduction Reduce(Node* node) OVERRIDE;
 
   JSGraph* jsgraph() { return jsgraph_; }
   Graph* graph() { return jsgraph_->graph(); }
 
  private:
   friend class JSBinopReduction;
-  JSGraph* jsgraph_;
-  SimplifiedOperatorBuilder simplified_;
-  MachineOperatorBuilder machine_;
 
   Reduction ReplaceEagerly(Node* old, Node* node);
   Reduction ReplaceWith(Node* node) { return Reducer::Replace(node); }
   Reduction ReduceJSAdd(Node* node);
   Reduction ReduceJSComparison(Node* node);
-  Reduction ReduceJSPropertyLoad(Node* node);
+  Reduction ReduceJSLoadProperty(Node* node);
+  Reduction ReduceJSStoreProperty(Node* node);
   Reduction ReduceJSEqual(Node* node, bool invert);
   Reduction ReduceJSStrictEqual(Node* node, bool invert);
   Reduction ReduceJSToNumberInput(Node* input);
@@ -55,9 +53,14 @@ class JSTypedLowering : public Reducer {
   CommonOperatorBuilder* common() { return jsgraph_->common(); }
   SimplifiedOperatorBuilder* simplified() { return &simplified_; }
   MachineOperatorBuilder* machine() { return &machine_; }
+
+  JSGraph* jsgraph_;
+  SimplifiedOperatorBuilder simplified_;
+  MachineOperatorBuilder machine_;
 };
-}
-}
-}  // namespace v8::internal::compiler
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
 
 #endif  // V8_COMPILER_OPERATOR_REDUCERS_H_
index 8bf0fe9..b1f04c5 100644 (file)
@@ -69,3 +69,71 @@ TEST(TypedArrayLoad) {
   // TODO(mstarzinger): Add tests for Float32.
   // TODO(mstarzinger): Add tests for ClampedUint8.
 }
+
+
+template <typename U>
+static void TypedArrayStoreHelper(const char* array_type) {
+  static const uint32_t kValues[] = {
+      0x00000000, 0x00000001, 0x00000023, 0x00000042, 0x12345678, 0x87654321,
+      0x0000003f, 0x0000007f, 0x00003fff, 0x00007fff, 0x3fffffff, 0x7fffffff,
+      0x000000ff, 0x00000080, 0x0000ffff, 0x00008000, 0xffffffff, 0x80000000};
+  EmbeddedVector<char, 1024> values_buffer;
+  StringBuilder values_builder(values_buffer.start(), values_buffer.length());
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    values_builder.AddFormatted("a[%d] = 0x%08x;", i, kValues[i]);
+  }
+
+  // Note that below source creates two different typed arrays with distinct
+  // elements kind to get coverage for both access patterns:
+  // - IsFixedTypedArrayElementsKind(x)
+  // - IsExternalArrayElementsKind(y)
+  const char* source =
+      "(function(a) {"
+      "  var x = (a = new %sArray(%d)); %s;"
+      "  var y = (a = new %sArray(%d)); %s; %%TypedArrayGetBuffer(y);"
+      "  if (!%%HasFixed%sElements(x)) %%AbortJS('x');"
+      "  if (!%%HasExternal%sElements(y)) %%AbortJS('y');"
+      "  function f(a,b) {"
+      "    a = a | 0; b = b | 0;"
+      "    var t = x[a];"
+      "    x[a] = y[b];"
+      "    y[b] = t;"
+      "    t = y[b];"
+      "    y[b] = x[a];"
+      "    x[a] = t;"
+      "    return x[a] + y[b];"
+      "  }"
+      "  return f;"
+      "})()";
+  EmbeddedVector<char, 2048> source_buffer;
+  SNPrintF(source_buffer, source, array_type, arraysize(kValues),
+           values_buffer.start(), array_type, arraysize(kValues),
+           values_buffer.start(), array_type, array_type);
+
+  FunctionTester T(
+      source_buffer.start(),
+      CompilationInfo::kContextSpecializing | CompilationInfo::kTypingEnabled);
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    for (size_t j = 0; j < arraysize(kValues); ++j) {
+      double value_a = static_cast<U>(kValues[i]);
+      double value_b = static_cast<U>(kValues[j]);
+      double expected = value_b + value_a;
+      T.CheckCall(T.Val(expected), T.Val(static_cast<double>(i)),
+                  T.Val(static_cast<double>(j)));
+    }
+  }
+}
+
+
+TEST(TypedArrayStore) {
+  FLAG_typed_array_max_size_in_heap = 256;
+  TypedArrayStoreHelper<int8_t>("Int8");
+  TypedArrayStoreHelper<uint8_t>("Uint8");
+  TypedArrayStoreHelper<int16_t>("Int16");
+  TypedArrayStoreHelper<uint16_t>("Uint16");
+  TypedArrayStoreHelper<int32_t>("Int32");
+  TypedArrayStoreHelper<uint32_t>("Uint32");
+  TypedArrayStoreHelper<double>("Float64");
+  // TODO(mstarzinger): Add tests for Float32.
+  // TODO(mstarzinger): Add tests for ClampedUint8.
+}