Bug 1073033 part 2.0 - Snapshot: Add Recover instruction with default value. r=bbouvier
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Fri, 19 Dec 2014 15:28:28 +0100
changeset 220598 0adb9a599879d6546da03e0950b1957555adcc1a
parent 220597 c687eceec200de3f8ddc660b4f65adec96fcaf6c
child 220599 c7733d2b62712ed0b0e3e3597bb9db9f8036041c
push id10503
push userryanvm@gmail.com
push dateFri, 19 Dec 2014 20:13:42 +0000
treeherderfx-team@98ee95ac6be5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1073033
milestone37.0a1
Bug 1073033 part 2.0 - Snapshot: Add Recover instruction with default value. r=bbouvier
js/src/jit/JitFrameIterator.h
js/src/jit/JitFrames.cpp
js/src/jit/Snapshots.cpp
js/src/jit/Snapshots.h
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -351,16 +351,28 @@ class SnapshotIterator
   protected:
     SnapshotReader snapshot_;
     RecoverReader recover_;
     JitFrameLayout *fp_;
     MachineState machine_;
     IonScript *ionScript_;
     RInstructionResults *instructionResults_;
 
+    enum ReadMethod {
+        // Read the normal value.
+        RM_Normal          = 1 << 0,
+
+        // Read the default value, or the normal value if there is no default.
+        RM_AlwaysDefault   = 1 << 1,
+
+        // Try to read the normal value if it is readable, otherwise default to
+        // the Default value.
+        RM_NormalOrDefault = RM_Normal | RM_AlwaysDefault,
+    };
+
   private:
     // Read a spilled register from the machine state.
     bool hasRegister(Register reg) const {
         return machine_.has(reg);
     }
     uintptr_t fromRegister(Register reg) const {
         return machine_.read(reg);
     }
@@ -378,18 +390,18 @@ class SnapshotIterator
     }
     uintptr_t fromStack(int32_t offset) const;
 
     bool hasInstructionResult(uint32_t index) const {
         return instructionResults_;
     }
     Value fromInstructionResult(uint32_t index) const;
 
-    Value allocationValue(const RValueAllocation &a);
-    bool allocationReadable(const RValueAllocation &a);
+    Value allocationValue(const RValueAllocation &a, ReadMethod rm = RM_Normal);
+    bool allocationReadable(const RValueAllocation &a, ReadMethod rm = RM_Normal);
     void writeAllocationValuePayload(const RValueAllocation &a, Value v);
     void warnUnreadableAllocation();
 
   public:
     // Handle iterating over RValueAllocations of the snapshots.
     inline RValueAllocation readAllocation() {
         MOZ_ASSERT(moreAllocations());
         return snapshot_.readAllocation();
@@ -477,16 +489,24 @@ class SnapshotIterator
                      JitFrameLayout *fp, const MachineState &machine);
     explicit SnapshotIterator(const JitFrameIterator &iter);
     SnapshotIterator();
 
     Value read() {
         return allocationValue(readAllocation());
     }
 
+    // Read the |Normal| value unless it is not available and that the snapshot
+    // provides a |Default| value. This is useful to avoid invalidations of the
+    // frame while we are only interested in a few properties which are provided
+    // by the |Default| value.
+    Value readWithDefault() {
+        return allocationValue(readAllocation(), RM_NormalOrDefault);
+    }
+
     Value maybeRead(MaybeReadFallback &fallback) {
         RValueAllocation a = readAllocation();
         if (allocationReadable(a))
             return allocationValue(a);
 
         if (fallback.canRecoverResults()) {
             if (!initInstructionResults(fallback))
                 js::CrashAtUnhandlableOOM("Unable to recover allocations.");
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1744,17 +1744,17 @@ FromTypedPayload(JSValueType type, uintp
       case JSVAL_TYPE_OBJECT:
         return FromObjectPayload(payload);
       default:
         MOZ_CRASH("unexpected type - needs payload");
     }
 }
 
 bool
-SnapshotIterator::allocationReadable(const RValueAllocation &alloc)
+SnapshotIterator::allocationReadable(const RValueAllocation &alloc, ReadMethod rm)
 {
     switch (alloc.mode()) {
       case RValueAllocation::DOUBLE_REG:
         return hasRegister(alloc.fpuReg());
 
       case RValueAllocation::TYPED_REG:
         return hasRegister(alloc.reg2());
 
@@ -1771,24 +1771,26 @@ SnapshotIterator::allocationReadable(con
       case RValueAllocation::UNTYPED_REG:
         return hasRegister(alloc.reg());
       case RValueAllocation::UNTYPED_STACK:
         return hasStack(alloc.stackOffset());
 #endif
 
       case RValueAllocation::RECOVER_INSTRUCTION:
         return hasInstructionResult(alloc.index());
+      case RValueAllocation::RI_WITH_DEFAULT_CST:
+        return rm & RM_AlwaysDefault || hasInstructionResult(alloc.index());
 
       default:
         return true;
     }
 }
 
 Value
-SnapshotIterator::allocationValue(const RValueAllocation &alloc)
+SnapshotIterator::allocationValue(const RValueAllocation &alloc, ReadMethod rm)
 {
     switch (alloc.mode()) {
       case RValueAllocation::CONSTANT:
         return ionScript_->getConstant(alloc.index());
 
       case RValueAllocation::CST_UNDEFINED:
         return UndefinedValue();
 
@@ -1882,16 +1884,22 @@ SnapshotIterator::allocationValue(const 
         layout.asBits = fromStack(alloc.stackOffset());
         return IMPL_TO_JSVAL(layout);
       }
 #endif
 
       case RValueAllocation::RECOVER_INSTRUCTION:
         return fromInstructionResult(alloc.index());
 
+      case RValueAllocation::RI_WITH_DEFAULT_CST:
+        if (rm & RM_Normal && hasInstructionResult(alloc.index()))
+            return fromInstructionResult(alloc.index());
+        MOZ_ASSERT(rm & RM_AlwaysDefault);
+        return ionScript_->getConstant(alloc.index2());
+
       default:
         MOZ_CRASH("huh?");
     }
 }
 
 void
 SnapshotIterator::writeAllocationValuePayload(const RValueAllocation &alloc, Value v)
 {
@@ -1951,29 +1959,35 @@ SnapshotIterator::writeAllocationValuePa
         WriteFrameSlot(fp_, alloc.stackOffset(), v.asRawBits());
         break;
 #endif
 
       case RValueAllocation::RECOVER_INSTRUCTION:
         MOZ_CRASH("Recover instructions are handled by the JitActivation.");
         break;
 
+      case RValueAllocation::RI_WITH_DEFAULT_CST:
+        // Assume that we are always going to be writing on the default value
+        // while tracing.
+        ionScript_->getConstant(alloc.index2()) = v;
+        break;
+
       default:
         MOZ_CRASH("huh?");
     }
 }
 
 void
 SnapshotIterator::traceAllocation(JSTracer *trc)
 {
     RValueAllocation alloc = readAllocation();
-    if (!allocationReadable(alloc))
+    if (!allocationReadable(alloc, RM_AlwaysDefault))
         return;
 
-    Value v = allocationValue(alloc);
+    Value v = allocationValue(alloc, RM_AlwaysDefault);
     if (!v.isMarkable())
         return;
 
     Value copy = v;
     gc::MarkValueRoot(trc, &v, "ion-typed-reg");
     if (v != copy) {
         MOZ_ASSERT(SameType(v, copy));
         writeAllocationValuePayload(alloc, v);
@@ -2308,18 +2322,22 @@ InlineFrameIterator::findNextFrame()
         if (numActualArgs_ == 0xbadbad)
             MOZ_CRASH("Couldn't deduce the number of arguments of an ionmonkey frame");
 
         // Skip over non-argument slots, as well as |this|.
         unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1;
         for (unsigned j = 0; j < skipCount; j++)
             si_.skip();
 
-        // The JSFunction is a constant, otherwise we would not have inlined it.
-        Value funval = si_.read();
+        // This value should correspond to the function which is being inlined.
+        // The value must be readable to iterate over the inline frame. Most of
+        // the time, these functions are stored as JSFunction constants,
+        // register which are holding the JSFunction pointer, or recover
+        // instruction with Default value.
+        Value funval = si_.readWithDefault();
 
         // Skip extra value allocations.
         while (si_.moreAllocations())
             si_.skip();
 
         si_.nextFrame();
 
         callee_ = &funval.toObject().as<JSFunction>();
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -74,16 +74,21 @@ using namespace js::jit;
 //         UNTYPED_STACK_STACK [STACK_OFFSET, STACK_OFFSET]
 //           Value with dynamically known type. On 32 bits architecture, the
 //           first register/stack-offset correspond to the holder of the type,
 //           and the second correspond to the payload of the JS Value.
 //
 //         RECOVER_INSTRUCTION [INDEX]
 //           Index into the list of recovered instruction results.
 //
+//         RI_WITH_DEFAULT_CST [INDEX] [INDEX]
+//           The first payload is the index into the list of recovered
+//           instruction results.  The second payload is the index in the
+//           constant pool.
+//
 //         TYPED_REG [PACKED_TAG, GPR_REG]:
 //           Value with statically known type, which payload is stored in a
 //           register.
 //
 //         TYPED_STACK [PACKED_TAG, STACK_OFFSET]:
 //           Value with statically known type, which payload is stored at an
 //           offset on the stack.
 //
@@ -226,16 +231,24 @@ RValueAllocation::layoutFromMode(Mode mo
       case RECOVER_INSTRUCTION: {
         static const RValueAllocation::Layout layout = {
             PAYLOAD_INDEX,
             PAYLOAD_NONE,
             "instruction"
         };
         return layout;
       }
+      case RI_WITH_DEFAULT_CST: {
+        static const RValueAllocation::Layout layout = {
+            PAYLOAD_INDEX,
+            PAYLOAD_INDEX,
+            "instruction with default"
+        };
+        return layout;
+      }
 
       default: {
         static const RValueAllocation::Layout regLayout = {
             PAYLOAD_PACKED_TAG,
             PAYLOAD_GPR,
             "typed value"
         };
 
--- a/js/src/jit/Snapshots.h
+++ b/js/src/jit/Snapshots.h
@@ -49,17 +49,20 @@ class RValueAllocation
         UNTYPED_REG_REG     = 0x06,
         UNTYPED_REG_STACK   = 0x07,
         UNTYPED_STACK_REG   = 0x08,
         UNTYPED_STACK_STACK = 0x09,
 #elif defined(JS_PUNBOX64)
         UNTYPED_REG         = 0x06,
         UNTYPED_STACK       = 0x07,
 #endif
+
+        // Recover instructions.
         RECOVER_INSTRUCTION = 0x0a,
+        RI_WITH_DEFAULT_CST = 0x0b,
 
         // The JSValueType is packed in the Mode.
         TYPED_REG_MIN       = 0x10,
         TYPED_REG_MAX       = 0x1f,
         TYPED_REG = TYPED_REG_MIN,
 
         // The JSValueType is packed in the Mode.
         TYPED_STACK_MIN     = 0x20,
@@ -255,16 +258,21 @@ class RValueAllocation
     static RValueAllocation ConstantPool(uint32_t index) {
         return RValueAllocation(CONSTANT, payloadOfIndex(index));
     }
 
     // Recover instruction's index
     static RValueAllocation RecoverInstruction(uint32_t index) {
         return RValueAllocation(RECOVER_INSTRUCTION, payloadOfIndex(index));
     }
+    static RValueAllocation RecoverInstruction(uint32_t riIndex, uint32_t cstIndex) {
+        return RValueAllocation(RI_WITH_DEFAULT_CST,
+                                payloadOfIndex(riIndex),
+                                payloadOfIndex(cstIndex));
+    }
 
     void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const;
   public:
     static RValueAllocation read(CompactBufferReader &reader);
     void write(CompactBufferWriter &writer) const;
 
   public:
     Mode mode() const {
@@ -288,16 +296,20 @@ class RValueAllocation
         FloatRegisterBits b = arg1_.fpu;
         return FloatRegister::FromCode(b.data);
     }
     JSValueType knownType() const {
         MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG);
         return arg1_.type;
     }
 
+    uint32_t index2() const {
+        MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_INDEX);
+        return arg2_.index;
+    }
     int32_t stackOffset2() const {
         MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET);
         return arg2_.stackOffset;
     }
     Register reg2() const {
         MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_GPR);
         return arg2_.gpr;
     }