Hydrogen: fix keyed loads with string keys
authorfedor.indutny <fedor.indutny@gmail.com>
Mon, 15 Dec 2014 13:36:13 +0000 (05:36 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 15 Dec 2014 13:36:23 +0000 (13:36 +0000)
Keyed loads should not unconditionally be compiled to element loads. Update KeyedLoadICs to keep track of the key type, so that Hydrogen can emit ICs for string-keyed loads it doesn't have inline support for.

BUG=v8:3167
R=jkummerow@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#25817}

src/ast.h
src/hydrogen.cc
src/ic/ic-compiler.cc
src/ic/ic.h
src/type-info.cc
src/type-info.h
src/typing.cc
test/mjsunit/keyed-load-with-string-key.js [new file with mode: 0644]

index 368ff81c733996ba36c0a5ab3e5bde7d1ec5f60a..43bde6ad25cc736bd89aab639ebb7398ccf6a947 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1748,8 +1748,7 @@ class Property FINAL : public Expression {
   SmallMapList* GetReceiverTypes() OVERRIDE { return &receiver_types_; }
   KeyedAccessStoreMode GetStoreMode() const OVERRIDE { return STANDARD_STORE; }
   IcCheckType GetKeyType() const OVERRIDE {
-    // PROPERTY key types currently aren't implemented for KeyedLoadICs.
-    return ELEMENT;
+    return KeyTypeField::decode(bit_field_);
   }
   bool IsUninitialized() const {
     return !is_for_call() && HasNoTypeInformation();
@@ -1763,6 +1762,9 @@ class Property FINAL : public Expression {
   void set_is_string_access(bool b) {
     bit_field_ = IsStringAccessField::update(bit_field_, b);
   }
+  void set_key_type(IcCheckType key_type) {
+    bit_field_ = KeyTypeField::update(bit_field_, key_type);
+  }
   void mark_for_call() {
     bit_field_ = IsForCallField::update(bit_field_, true);
   }
@@ -1805,6 +1807,7 @@ class Property FINAL : public Expression {
   class IsForCallField : public BitField8<bool, 0, 1> {};
   class IsUninitializedField : public BitField8<bool, 1, 1> {};
   class IsStringAccessField : public BitField8<bool, 2, 1> {};
+  class KeyTypeField : public BitField8<IcCheckType, 3, 1> {};
   uint8_t bit_field_;
   FeedbackVectorICSlot property_feedback_slot_;
   Expression* obj_;
index 77fa1cb07019061363af868bb813eff2fbeaa6c6..987d60b334115809200ccec79c35c489da3ffaa6 100644 (file)
@@ -7215,7 +7215,7 @@ HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
   bool monomorphic = ComputeReceiverTypes(expr, obj, &types, zone());
 
   bool force_generic = false;
-  if (access_type == STORE && expr->GetKeyType() == PROPERTY) {
+  if (expr->GetKeyType() == PROPERTY) {
     // Non-Generic accesses assume that elements are being accessed, and will
     // deopt for non-index keys, which the IC knows will occur.
     // TODO(jkummerow): Consider adding proper support for property accesses.
index 1f6eb4e0794777b90dfc239c930c3519335d3f2b..69d707b03554ff3ecf66e41cec8f812ef8109881 100644 (file)
@@ -63,6 +63,9 @@ Handle<Code> PropertyICCompiler::ComputeMonomorphic(
         KeyedStoreIC::IcCheckTypeField::update(extra_ic_state, PROPERTY);
     DCHECK(STANDARD_STORE ==
            KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state));
+  } else if (kind == Code::KEYED_LOAD_IC) {
+    extra_ic_state = KeyedLoadIC::IcCheckTypeField::update(extra_ic_state,
+                                                           PROPERTY);
   }
 
   Handle<Code> ic;
@@ -86,6 +89,7 @@ Handle<Code> PropertyICCompiler::ComputeMonomorphic(
 Handle<Code> PropertyICCompiler::ComputeKeyedLoadMonomorphic(
     Handle<Map> receiver_map) {
   Isolate* isolate = receiver_map->GetIsolate();
+  DCHECK(KeyedLoadIC::GetKeyType(kNoExtraICState) == ELEMENT);
   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC);
   Handle<Name> name = isolate->factory()->KeyedLoadMonomorphic_string();
 
@@ -255,6 +259,7 @@ Handle<Code> PropertyICCompiler::ComputeCompareNil(Handle<Map> receiver_map,
 Handle<Code> PropertyICCompiler::ComputeKeyedLoadPolymorphic(
     MapHandleList* receiver_maps) {
   Isolate* isolate = receiver_maps->at(0)->GetIsolate();
+  DCHECK(KeyedLoadIC::GetKeyType(kNoExtraICState) == ELEMENT);
   Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC);
   Handle<PolymorphicCodeCache> cache =
       isolate->factory()->polymorphic_code_cache();
index 5f1600021779acd3545f83b7d8c2ddff613f63fb..761810659af9091e52d6a22b2792cfbf8c015597 100644 (file)
@@ -459,6 +459,19 @@ class LoadIC : public IC {
 
 class KeyedLoadIC : public LoadIC {
  public:
+  // ExtraICState bits (building on IC)
+  class IcCheckTypeField : public BitField<IcCheckType, 1, 1> {};
+
+  static ExtraICState ComputeExtraICState(ContextualMode contextual_mode,
+                                          IcCheckType key_type) {
+    return LoadICState(contextual_mode).GetExtraICState() |
+           IcCheckTypeField::encode(key_type);
+  }
+
+  static IcCheckType GetKeyType(ExtraICState extra_state) {
+    return IcCheckTypeField::decode(extra_state);
+  }
+
   KeyedLoadIC(FrameDepth depth, Isolate* isolate,
               KeyedLoadICNexus* nexus = NULL)
       : LoadIC(depth, isolate, nexus) {
index ed6d7b74e1385c4ad3a72a57569e44aac1687f3c..a18c65b12a76b680fdb86bf0b4f3f796b9e2aae6 100644 (file)
@@ -156,6 +156,21 @@ void TypeFeedbackOracle::GetStoreModeAndKeyType(
 }
 
 
+void TypeFeedbackOracle::GetLoadKeyType(
+    TypeFeedbackId ast_id, IcCheckType* key_type) {
+  Handle<Object> maybe_code = GetInfo(ast_id);
+  if (maybe_code->IsCode()) {
+    Handle<Code> code = Handle<Code>::cast(maybe_code);
+    if (code->kind() == Code::KEYED_LOAD_IC) {
+      ExtraICState extra_ic_state = code->extra_ic_state();
+      *key_type = KeyedLoadIC::GetKeyType(extra_ic_state);
+      return;
+    }
+  }
+  *key_type = ELEMENT;
+}
+
+
 Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(
     FeedbackVectorICSlot slot) {
   Handle<Object> info = GetInfo(slot);
@@ -305,10 +320,14 @@ bool TypeFeedbackOracle::HasOnlyStringMaps(SmallMapList* receiver_types) {
 
 
 void TypeFeedbackOracle::KeyedPropertyReceiverTypes(
-    TypeFeedbackId id, SmallMapList* receiver_types, bool* is_string) {
+    TypeFeedbackId id,
+    SmallMapList* receiver_types,
+    bool* is_string,
+    IcCheckType* key_type) {
   receiver_types->Clear();
   CollectReceiverTypes(id, receiver_types);
   *is_string = HasOnlyStringMaps(receiver_types);
+  GetLoadKeyType(id, key_type);
 }
 
 
index e0f7394c7c605d10f35bd827ecb39e6d182513cc..9753ff06d9a519c44e912cb116864d92a5f40e6b 100644 (file)
@@ -40,6 +40,7 @@ class TypeFeedbackOracle: public ZoneObject {
   void GetStoreModeAndKeyType(TypeFeedbackId id,
                               KeyedAccessStoreMode* store_mode,
                               IcCheckType* key_type);
+  void GetLoadKeyType(TypeFeedbackId id, IcCheckType* key_type);
 
   void PropertyReceiverTypes(TypeFeedbackId id, Handle<String> name,
                              SmallMapList* receiver_types);
@@ -47,7 +48,8 @@ class TypeFeedbackOracle: public ZoneObject {
                              SmallMapList* receiver_types);
   void KeyedPropertyReceiverTypes(TypeFeedbackId id,
                                   SmallMapList* receiver_types,
-                                  bool* is_string);
+                                  bool* is_string,
+                                  IcCheckType* key_type);
   void KeyedPropertyReceiverTypes(FeedbackVectorICSlot slot,
                                   SmallMapList* receiver_types,
                                   bool* is_string);
index 0bfe3af997d023779078bdb5a6d4451af9a80d82..88c530eeee7bca2a38d78c187b8384fb1f5ed5f9 100644 (file)
@@ -507,14 +507,17 @@ void AstTyper::VisitProperty(Property* expr) {
       }
     } else {
       bool is_string;
+      IcCheckType key_type;
       if (FLAG_vector_ics) {
         oracle()->KeyedPropertyReceiverTypes(slot, expr->GetReceiverTypes(),
                                              &is_string);
+        key_type = ELEMENT;
       } else {
         oracle()->KeyedPropertyReceiverTypes(id, expr->GetReceiverTypes(),
-                                             &is_string);
+                                             &is_string, &key_type);
       }
       expr->set_is_string_access(is_string);
+      expr->set_key_type(key_type);
     }
   }
 
diff --git a/test/mjsunit/keyed-load-with-string-key.js b/test/mjsunit/keyed-load-with-string-key.js
new file mode 100644 (file)
index 0000000..4388946
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax
+
+
+var o = {
+  "foo": "bar",
+}
+
+function get(obj, key) {
+  return obj[key];
+}
+
+get(o, "foo");
+get(o, "foo");
+get(o, "foo");
+
+%OptimizeFunctionOnNextCall(get);
+get(o, "foo");
+
+assertOptimized(get);