Bug 991720 part 3 - Enforce recovery of stores before reading object values. r=h4writer
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Fri, 19 Dec 2014 15:28:31 +0100
changeset 220589 f5497ebe2735a639bdd1c135260e9c55338c7015
parent 220588 69c510e5fea23d19e1ba5112a4f2927171672e68
child 220590 b421cdd4b918eeebacdb42debf9690d71bf0d397
push id27990
push userryanvm@gmail.com
push dateFri, 19 Dec 2014 20:08:22 +0000
treeherdermozilla-central@8880b5632e2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs991720
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 991720 part 3 - Enforce recovery of stores before reading object values. r=h4writer
js/src/jit/JitFrameIterator.h
js/src/jit/JitFrames.cpp
js/src/jit/MIR.h
js/src/jit/Snapshots.cpp
js/src/jit/Snapshots.h
js/src/jit/shared/CodeGenerator-shared.cpp
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -388,16 +388,19 @@ class SnapshotIterator
     bool hasStack(int32_t offset) const {
         return true;
     }
     uintptr_t fromStack(int32_t offset) const;
 
     bool hasInstructionResult(uint32_t index) const {
         return instructionResults_;
     }
+    bool hasInstructionResults() const {
+        return instructionResults_;
+    }
     Value fromInstructionResult(uint32_t index) const;
 
     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:
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1746,16 +1746,24 @@ FromTypedPayload(JSValueType type, uintp
       default:
         MOZ_CRASH("unexpected type - needs payload");
     }
 }
 
 bool
 SnapshotIterator::allocationReadable(const RValueAllocation &alloc, ReadMethod rm)
 {
+    // If we have to recover stores, and if we are not interested in the
+    // default value of the instruction, then we have to check if the recover
+    // instruction results are available.
+    if (alloc.needSideEffect() && !(rm & RM_AlwaysDefault)) {
+        if (!hasInstructionResults())
+            return false;
+    }
+
     switch (alloc.mode()) {
       case RValueAllocation::DOUBLE_REG:
         return hasRegister(alloc.fpuReg());
 
       case RValueAllocation::TYPED_REG:
         return hasRegister(alloc.reg2());
 
 #if defined(JS_NUNBOX32)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -96,16 +96,25 @@ MIRType MIRTypeFromValue(const js::Value
                                                                                 \
     /* Marks if the current instruction should go to the bailout paths instead
      * of producing code as part of the control flow.  This flag can only be set
      * on instructions which are only used by ResumePoint or by other flagged
      * instructions.
      */                                                                         \
     _(RecoveredOnBailout)                                                       \
                                                                                 \
+    /* Some instructions might represent an object, but the memory of these
+     * objects might be incomplete if we have not recovered all the stores which
+     * were supposed to happen before. This flag is used to annotate
+     * instructions which might return a pointer to a memory area which is not
+     * yet fully initialized. This flag is used to ensure that stores are
+     * executed before returning the value.
+     */                                                                         \
+    _(IncompleteObject)                                                         \
+                                                                                \
     /* The current instruction got discarded from the MIR Graph. This is useful
      * when we want to iterate over resume points and instructions, while
      * handling instructions which are discarded without reporting to the
      * iterator.
      */                                                                         \
     _(Discarded)
 
 class MDefinition;
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -308,17 +308,17 @@ RValueAllocation::readPayload(CompactBuf
         break;
     }
 }
 
 RValueAllocation
 RValueAllocation::read(CompactBufferReader &reader)
 {
     uint8_t mode = reader.readByte();
-    const Layout &layout = layoutFromMode(Mode(mode));
+    const Layout &layout = layoutFromMode(Mode(mode & MODE_MASK));
     Payload arg1, arg2;
 
     readPayload(reader, layout.type1, &mode, &arg1);
     readPayload(reader, layout.type2, &mode, &arg2);
     return RValueAllocation(Mode(mode), arg1, arg2);
 }
 
 void
--- a/js/src/jit/Snapshots.h
+++ b/js/src/jit/Snapshots.h
@@ -64,16 +64,27 @@ class RValueAllocation
         TYPED_REG_MAX       = 0x1f,
         TYPED_REG = TYPED_REG_MIN,
 
         // The JSValueType is packed in the Mode.
         TYPED_STACK_MIN     = 0x20,
         TYPED_STACK_MAX     = 0x2f,
         TYPED_STACK = TYPED_STACK_MIN,
 
+        // This mask can be used with any other valid mode. When this flag is
+        // set on the mode, this inform the snapshot iterator that even if the
+        // allocation is readable, the content of if might be incomplete unless
+        // all side-effects are executed.
+        RECOVER_SIDE_EFFECT_MASK = 0x80,
+
+        // This mask represents the set of bits which can be used to encode a
+        // value in a snapshot. The mode is used to determine how to interpret
+        // the union of values and how to pack the value in memory.
+        MODE_MASK           = 0x17f,
+
         INVALID = 0x100,
     };
 
     enum { PACKED_TAG_MASK = 0x0f };
 
     // See Payload encoding in Snapshots.cpp
     enum PayloadType {
         PAYLOAD_NONE,
@@ -264,24 +275,32 @@ class RValueAllocation
         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 setNeedSideEffect() {
+        MOZ_ASSERT(!needSideEffect() && mode_ != INVALID);
+        mode_ = Mode(mode_ | RECOVER_SIDE_EFFECT_MASK);
+    }
+
     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 {
-        return mode_;
+        return Mode(mode_ & MODE_MASK);
+    }
+    bool needSideEffect() const {
+        return mode_ & RECOVER_SIDE_EFFECT_MASK;
     }
 
     uint32_t index() const {
         MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_INDEX);
         return arg1_.index;
     }
     int32_t stackOffset() const {
         MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_STACK_OFFSET);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -394,16 +394,22 @@ CodeGeneratorShared::encodeAllocation(LS
             alloc = RValueAllocation::Untyped(ToRegister(payload));
         else
             alloc = RValueAllocation::Untyped(ToStackIndex(payload));
 #endif
         break;
       }
     }
 
+    // This set an extra bit as part of the RValueAllocation, such that we know
+    // that recover instruction have to be executed without wrapping the
+    // instruction in a no-op recover instruction.
+    if (mir->isIncompleteObject())
+        alloc.setNeedSideEffect();
+
     snapshots_.add(alloc);
     *allocIndex += mir->isRecoveredOnBailout() ? 0 : 1;
 }
 
 void
 CodeGeneratorShared::encode(LRecoverInfo *recover)
 {
     if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)