Make HCheckPrototypeMaps compatible with parallel recompilation.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 21 Jan 2013 15:49:00 +0000 (15:49 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 21 Jan 2013 15:49:00 +0000 (15:49 +0000)
HCheckPrototypeMaps currently records the prototype and the holder of the
prototype chain (both ends of the chain) and assumes that the chain elements
and their maps did not change in during the entirety of Crankshaft. The actual
traversal of the prototype chain happens in Lithium at code generation.
With parallel compilation, this assumption is not longer correct.

R=mstarzinger@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/11864013

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

src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/hydrogen-instructions.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.h
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.h
test/mjsunit/compiler/parallel-proto-change.js [new file with mode: 0644]

index 41639e7..1ca2562 100644 (file)
@@ -2166,8 +2166,10 @@ class LCheckPrototypeMaps: public LTemplateInstruction<1, 0, 2> {
   DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
   DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
 
-  Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
-  Handle<JSObject> holder() const { return hydrogen()->holder(); }
+  ZoneList<Handle<JSObject> >* prototypes() const {
+    return hydrogen()->prototypes();
+  }
+  ZoneList<Handle<Map> >* maps() const { return hydrogen()->maps(); }
 };
 
 
index 3c8e568..38015b4 100644 (file)
@@ -5318,29 +5318,19 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
   Register prototype_reg = ToRegister(instr->temp());
   Register map_reg = ToRegister(instr->temp2());
 
-  Handle<JSObject> holder = instr->holder();
-  Handle<JSObject> current_prototype = instr->prototype();
+  ZoneList<Handle<JSObject> >* prototypes = instr->prototypes();
+  ZoneList<Handle<Map> >* maps = instr->maps();
 
-  // Load prototype object.
-  __ LoadHeapObject(prototype_reg, current_prototype);
+  ASSERT(prototypes->length() == maps->length());
 
-  // Check prototype maps up to the holder.
-  while (!current_prototype.is_identical_to(holder)) {
+  for (int i = 0; i < prototypes->length(); i++) {
+    __ LoadHeapObject(prototype_reg, prototypes->at(i));
     __ ldr(map_reg, FieldMemOperand(prototype_reg, HeapObject::kMapOffset));
     DoCheckMapCommon(map_reg,
-                     Handle<Map>(current_prototype->map()),
-                     ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment());
-    current_prototype =
-        Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
-    // Load next prototype object.
-    __ LoadHeapObject(prototype_reg, current_prototype);
-  }
-
-  // Check the holder map.
-  __ ldr(map_reg, FieldMemOperand(prototype_reg, HeapObject::kMapOffset));
-  DoCheckMapCommon(map_reg,
-                   Handle<Map>(current_prototype->map()),
-                   ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment());
+                     maps->at(i),
+                     ALLOW_ELEMENT_TRANSITION_MAPS,
+                     instr->environment());
+  }
 }
 
 
index 4bcb498..fc6c3c5 100644 (file)
@@ -1205,7 +1205,8 @@ void HCheckInstanceType::PrintDataTo(StringStream* stream) {
 
 
 void HCheckPrototypeMaps::PrintDataTo(StringStream* stream) {
-  stream->Add("[receiver_prototype=%p,holder=%p]", *prototype(), *holder());
+  stream->Add("[receiver_prototype=%p,holder=%p]",
+              *prototypes_.first(), *prototypes_.last());
 }
 
 
index 88d217a..1a43f0f 100644 (file)
@@ -2446,14 +2446,24 @@ class HCheckNonSmi: public HUnaryOperation {
 
 class HCheckPrototypeMaps: public HTemplateInstruction<0> {
  public:
-  HCheckPrototypeMaps(Handle<JSObject> prototype, Handle<JSObject> holder)
-      : prototype_(prototype), holder_(holder) {
+  HCheckPrototypeMaps(Handle<JSObject> prototype,
+                      Handle<JSObject> holder,
+                      Zone* zone) : prototypes_(2, zone), maps_(2, zone) {
     SetFlag(kUseGVN);
     SetGVNFlag(kDependsOnMaps);
+    // Keep a list of all objects on the prototype chain up to the holder
+    // and the expected maps.
+    while (true) {
+      prototypes_.Add(prototype, zone);
+      maps_.Add(Handle<Map>(prototype->map()), zone);
+      if (prototype.is_identical_to(holder)) break;
+      prototype = Handle<JSObject>(JSObject::cast(prototype->GetPrototype()));
+    }
   }
 
-  Handle<JSObject> prototype() const { return prototype_; }
-  Handle<JSObject> holder() const { return holder_; }
+  ZoneList<Handle<JSObject> >* prototypes() { return &prototypes_; }
+
+  ZoneList<Handle<Map> >* maps() { return &maps_; }
 
   DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps)
 
@@ -2465,21 +2475,33 @@ class HCheckPrototypeMaps: public HTemplateInstruction<0> {
 
   virtual intptr_t Hashcode() {
     ASSERT_ALLOCATION_DISABLED;
-    intptr_t hash = reinterpret_cast<intptr_t>(*prototype());
-    hash = 17 * hash + reinterpret_cast<intptr_t>(*holder());
+    intptr_t hash = 0;
+    for (int i = 0; i < prototypes_.length(); i++) {
+      hash = 17 * hash + reinterpret_cast<intptr_t>(*prototypes_[i]);
+      hash = 17 * hash + reinterpret_cast<intptr_t>(*maps_[i]);
+    }
     return hash;
   }
 
  protected:
   virtual bool DataEquals(HValue* other) {
     HCheckPrototypeMaps* b = HCheckPrototypeMaps::cast(other);
-    return prototype_.is_identical_to(b->prototype()) &&
-        holder_.is_identical_to(b->holder());
+#ifdef DEBUG
+    if (prototypes_.length() != b->prototypes()->length()) return false;
+    for (int i = 0; i < prototypes_.length(); i++) {
+      if (!prototypes_[i].is_identical_to(b->prototypes()->at(i))) return false;
+      if (!maps_[i].is_identical_to(b->maps()->at(i))) return false;
+    }
+    return true;
+#else
+    return prototypes_.first().is_identical_to(b->prototypes()->first()) &&
+           prototypes_.last().is_identical_to(b->prototypes()->last());
+#endif  // DEBUG
   }
 
  private:
-  Handle<JSObject> prototype_;
-  Handle<JSObject> holder_;
+  ZoneList<Handle<JSObject> > prototypes_;
+  ZoneList<Handle<Map> > maps_;
 };
 
 
@@ -3614,9 +3636,9 @@ class HSub: public HArithmeticBinaryOperation {
   virtual HValue* Canonicalize();
 
   static HInstruction* NewHSub(Zone* zone,
-                              HValue* context,
-                              HValue* left,
-                              HValue* right);
+                               HValue* context,
+                               HValue* left,
+                               HValue* right);
 
   DECLARE_CONCRETE_INSTRUCTION(Sub)
 
index 57ba465..e19947b 100644 (file)
@@ -5561,7 +5561,8 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
     ASSERT(proto->IsJSObject());
     AddInstruction(new(zone()) HCheckPrototypeMaps(
         Handle<JSObject>(JSObject::cast(map->prototype())),
-        Handle<JSObject>(JSObject::cast(proto))));
+        Handle<JSObject>(JSObject::cast(proto)),
+        zone()));
   }
 
   int index = ComputeLoadStoreFieldIndex(map, name, lookup);
@@ -6296,8 +6297,8 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
     Handle<JSObject> holder(lookup.holder());
     Handle<Map> holder_map(holder->map());
     AddCheckMapsWithTransitions(object, map);
-    HInstruction* holder_value =
-        AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder));
+    HInstruction* holder_value = AddInstruction(
+        new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
     return BuildLoadNamedField(holder_value, holder_map, &lookup);
   }
 
@@ -6836,8 +6837,9 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
 void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
                                                    Handle<Map> receiver_map) {
   if (!holder.is_null()) {
-    AddInstruction(new(zone()) HCheckPrototypeMaps(
-        Handle<JSObject>(JSObject::cast(receiver_map->prototype())), holder));
+    Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
+    AddInstruction(
+        new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
   }
 }
 
@@ -7469,7 +7471,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
         ASSERT(!expr->holder().is_null());
         AddInstruction(new(zone()) HCheckPrototypeMaps(
             oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
-            expr->holder()));
+            expr->holder(),
+            zone()));
         HStringCharCodeAt* char_code =
             BuildStringCharCodeAt(context, string, index);
         if (id == kStringCharCodeAt) {
index c8fae64..ab2febb 100644 (file)
@@ -5169,26 +5169,15 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
   ASSERT(instr->temp()->Equals(instr->result()));
   Register reg = ToRegister(instr->temp());
 
-  Handle<JSObject> holder = instr->holder();
-  Handle<JSObject> current_prototype = instr->prototype();
+  ZoneList<Handle<JSObject> >* prototypes = instr->prototypes();
+  ZoneList<Handle<Map> >* maps = instr->maps();
 
-  // Load prototype object.
-  __ LoadHeapObject(reg, current_prototype);
+  ASSERT(prototypes->length() == maps->length());
 
-  // Check prototype maps up to the holder.
-  while (!current_prototype.is_identical_to(holder)) {
-    DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()),
-                     ALLOW_ELEMENT_TRANSITION_MAPS, instr);
-
-    current_prototype =
-        Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
-    // Load next prototype object.
-    __ LoadHeapObject(reg, current_prototype);
+  for (int i = 0; i < prototypes->length(); i++) {
+    __ LoadHeapObject(reg, prototypes->at(i));
+    DoCheckMapCommon(reg, maps->at(i), ALLOW_ELEMENT_TRANSITION_MAPS, instr);
   }
-
-  // Check the holder map.
-  DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()),
-                   ALLOW_ELEMENT_TRANSITION_MAPS, instr);
 }
 
 
index 1daf847..2ccfd5d 100644 (file)
@@ -2231,8 +2231,10 @@ class LCheckPrototypeMaps: public LTemplateInstruction<1, 0, 1> {
   DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
   DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
 
-  Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
-  Handle<JSObject> holder() const { return hydrogen()->holder(); }
+  ZoneList<Handle<JSObject> >* prototypes() const {
+    return hydrogen()->prototypes();
+  }
+  ZoneList<Handle<Map> >* maps() const { return hydrogen()->maps(); }
 };
 
 
index 87eb954..77557c5 100644 (file)
@@ -4778,25 +4778,15 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
   ASSERT(instr->temp()->Equals(instr->result()));
   Register reg = ToRegister(instr->temp());
 
-  Handle<JSObject> holder = instr->holder();
-  Handle<JSObject> current_prototype = instr->prototype();
+  ZoneList<Handle<JSObject> >* prototypes = instr->prototypes();
+  ZoneList<Handle<Map> >* maps = instr->maps();
 
-  // Load prototype object.
-  __ LoadHeapObject(reg, current_prototype);
+  ASSERT(prototypes->length() == maps->length());
 
-  // Check prototype maps up to the holder.
-  while (!current_prototype.is_identical_to(holder)) {
-    DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()),
-                     ALLOW_ELEMENT_TRANSITION_MAPS, instr);
-    current_prototype =
-        Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
-    // Load next prototype object.
-    __ LoadHeapObject(reg, current_prototype);
+  for (int i = 0; i < prototypes->length(); i++) {
+    __ LoadHeapObject(reg, prototypes->at(i));
+    DoCheckMapCommon(reg, maps->at(i), ALLOW_ELEMENT_TRANSITION_MAPS, instr);
   }
-
-  // Check the holder map.
-    DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()),
-                     ALLOW_ELEMENT_TRANSITION_MAPS, instr);
 }
 
 
index ffec14a..baa0d98 100644 (file)
@@ -2102,8 +2102,10 @@ class LCheckPrototypeMaps: public LTemplateInstruction<1, 0, 1> {
   DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
   DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
 
-  Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
-  Handle<JSObject> holder() const { return hydrogen()->holder(); }
+  ZoneList<Handle<JSObject> >* prototypes() const {
+    return hydrogen()->prototypes();
+  }
+  ZoneList<Handle<Map> >* maps() const { return hydrogen()->maps(); }
 };
 
 
diff --git a/test/mjsunit/compiler/parallel-proto-change.js b/test/mjsunit/compiler/parallel-proto-change.js
new file mode 100644 (file)
index 0000000..1aa135a
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2013 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
+// Flags: --parallel-recompilation --manual-parallel-recompilation
+
+function f(foo) { return foo.bar(); }
+
+var o = {};
+o.__proto__ = { __proto__: { bar: function() { return 1; } } };
+
+assertEquals(1, f(o));
+assertEquals(1, f(o));
+
+%ForceParallelRecompile(f);
+// Change the prototype chain during optimization.
+o.__proto__.__proto__ = { bar: function() { return 2; } };
+%InstallRecompiledCode(f);
+
+assertEquals(2, f(o));