Bug 827872 - Move prebarrier offsets from IonScript to IonCode. r=dvander
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 10 Jan 2013 17:21:42 +0100
changeset 118428 78ac084b80f57054f744446e3eddfe6bd575175f
parent 118427 dcaee859dcf5df9d64072c6708fcf67bbbcbd5e5
child 118429 901864570d8e2d7553f59b584a3bd00df39b0919
push id24166
push userMs2ger@gmail.com
push dateFri, 11 Jan 2013 13:57:41 +0000
treeherdermozilla-central@63c4b0f66a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs827872
milestone21.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 827872 - Move prebarrier offsets from IonScript to IonCode. r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/Ion.cpp
js/src/ion/IonCode.h
js/src/ion/IonMacroAssembler.h
js/src/ion/arm/Assembler-arm.cpp
js/src/ion/arm/Assembler-arm.h
js/src/ion/shared/Assembler-x86-shared.cpp
js/src/ion/shared/Assembler-x86-shared.h
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/ion/shared/CodeGenerator-shared.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -3584,18 +3584,18 @@ CodeGenerator::link()
     // will trickle to ion::Compile() and return Method_Skipped.
     if (cx->compartment->types.compiledInfo.compilerOutput(cx)->isInvalidated())
         return true;
 
     IonScript *ionScript =
       IonScript::New(cx, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
-                     cacheList_.length(), barrierOffsets_.length(),
-                     safepoints_.size(), graph.mir().numScripts());
+                     cacheList_.length(), safepoints_.size(),
+                     graph.mir().numScripts());
     SetIonScript(script, executionMode, ionScript);
 
     if (!ionScript)
         return false;
     invalidateEpilogueData_.fixup(&masm);
     Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
                                        ImmWord(uintptr_t(ionScript)),
                                        ImmWord(uintptr_t(-1)));
@@ -3618,18 +3618,16 @@ CodeGenerator::link()
     if (graph.numConstants())
         ionScript->copyConstants(graph.constantPool());
     if (safepointIndices_.length())
         ionScript->copySafepointIndices(&safepointIndices_[0], masm);
     if (osiIndices_.length())
         ionScript->copyOsiIndices(&osiIndices_[0], masm);
     if (cacheList_.length())
         ionScript->copyCacheEntries(&cacheList_[0], masm);
-    if (barrierOffsets_.length())
-        ionScript->copyPrebarrierEntries(&barrierOffsets_[0], masm);
     if (safepoints_.size())
         ionScript->copySafepoints(&safepoints_);
 
     JS_ASSERT(graph.mir().numScripts() > 0);
     ionScript->copyScriptEntries(graph.mir().scripts());
 
     linkAbsoluteLabels();
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -342,19 +342,23 @@ IonCode::copyFrom(MacroAssembler &masm)
     insnSize_ = masm.instructionsSize();
     masm.executableCopy(code_);
 
     dataSize_ = masm.dataSize();
     masm.processDeferredData(this, code_ + dataOffset());
 
     jumpRelocTableBytes_ = masm.jumpRelocationTableBytes();
     masm.copyJumpRelocationTable(code_ + jumpRelocTableOffset());
+
     dataRelocTableBytes_ = masm.dataRelocationTableBytes();
     masm.copyDataRelocationTable(code_ + dataRelocTableOffset());
 
+    preBarrierTableBytes_ = masm.preBarrierTableBytes();
+    masm.copyPreBarrierTable(code_ + preBarrierTableOffset());
+
     masm.processCodeLabels(this);
 }
 
 void
 IonCode::trace(JSTracer *trc)
 {
     // Note that we cannot mark invalidated scripts, since we've basically
     // corrupted the code stream by injecting bailouts.
@@ -381,16 +385,32 @@ IonCode::finalize(FreeOp *fop)
 
     // Code buffers are stored inside JSC pools.
     // Pools are refcounted. Releasing the pool may free it.
     if (pool_)
         pool_->release();
 }
 
 void
+IonCode::togglePreBarriers(bool enabled)
+{
+    uint8_t *start = code_ + preBarrierTableOffset();
+    CompactBufferReader reader(start, start + preBarrierTableBytes_);
+
+    while (reader.more()) {
+        size_t offset = reader.readUnsigned();
+        CodeLocationLabel loc(this, offset);
+        if (enabled)
+            Assembler::ToggleToCmp(loc);
+        else
+            Assembler::ToggleToJmp(loc);
+    }
+}
+
+void
 IonCode::readBarrier(IonCode *code)
 {
 #ifdef JSGC_INCREMENTAL
     if (!code)
         return;
 
     JSCompartment *comp = code->compartment();
     if (comp->needsBarrier())
@@ -436,35 +456,33 @@ IonScript::IonScript()
     safepointIndexOffset_(0),
     safepointIndexEntries_(0),
     frameSlots_(0),
     frameSize_(0),
     osiIndexOffset_(0),
     osiIndexEntries_(0),
     cacheList_(0),
     cacheEntries_(0),
-    prebarrierList_(0),
-    prebarrierEntries_(0),
     safepointsStart_(0),
     safepointsSize_(0),
     scriptList_(0),
     scriptEntries_(0),
     refcount_(0),
     recompileInfo_(),
     slowCallCount(0)
 {
 }
 
 static const int DataAlignment = 4;
 
 IonScript *
 IonScript::New(JSContext *cx, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
                size_t bailoutEntries, size_t constants, size_t safepointIndices,
-               size_t osiIndices, size_t cacheEntries, size_t prebarrierEntries,
-               size_t safepointsSize, size_t scriptEntries)
+               size_t osiIndices, size_t cacheEntries, size_t safepointsSize,
+               size_t scriptEntries)
 {
     if (snapshotsSize >= MAX_BUFFER_SIZE ||
         (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t)))
     {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
@@ -472,27 +490,24 @@ IonScript::New(JSContext *cx, uint32_t f
     // *somewhere* and if their total overflowed there would be no memory left
     // at all.
     size_t paddedSnapshotsSize = AlignBytes(snapshotsSize, DataAlignment);
     size_t paddedBailoutSize = AlignBytes(bailoutEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment);
     size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
     size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
     size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(IonCache), DataAlignment);
-    size_t paddedPrebarrierEntriesSize =
-        AlignBytes(prebarrierEntries * sizeof(CodeOffsetLabel), DataAlignment);
     size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
     size_t paddedScriptSize = AlignBytes(scriptEntries * sizeof(RawScript), DataAlignment);
     size_t bytes = paddedSnapshotsSize +
                    paddedBailoutSize +
                    paddedConstantsSize +
                    paddedSafepointIndicesSize+
                    paddedOsiIndicesSize +
                    paddedCacheEntriesSize +
-                   paddedPrebarrierEntriesSize +
                    paddedSafepointSize +
                    paddedScriptSize;
     uint8_t *buffer = (uint8_t *)cx->malloc_(sizeof(IonScript) + bytes);
     if (!buffer)
         return NULL;
 
     IonScript *script = reinterpret_cast<IonScript *>(buffer);
     new (script) IonScript();
@@ -518,20 +533,16 @@ IonScript::New(JSContext *cx, uint32_t f
     script->osiIndexOffset_ = offsetCursor;
     script->osiIndexEntries_ = osiIndices;
     offsetCursor += paddedOsiIndicesSize;
 
     script->cacheList_ = offsetCursor;
     script->cacheEntries_ = cacheEntries;
     offsetCursor += paddedCacheEntriesSize;
 
-    script->prebarrierList_ = offsetCursor;
-    script->prebarrierEntries_ = prebarrierEntries;
-    offsetCursor += paddedPrebarrierEntriesSize;
-
     script->safepointsStart_ = offsetCursor;
     script->safepointsSize_ = safepointsSize;
     offsetCursor += paddedSafepointSize;
 
     script->scriptList_ = offsetCursor;
     script->scriptEntries_ = scriptEntries;
     offsetCursor += paddedScriptSize;
 
@@ -621,33 +632,16 @@ IonScript::copyCacheEntries(const IonCac
      * Jumps in the caches reflect the offset of those jumps in the compiled
      * code, not the absolute positions of the jumps. Update according to the
      * final code address now.
      */
     for (size_t i = 0; i < numCaches(); i++)
         getCache(i).updateBaseAddress(method_, masm);
 }
 
-inline CodeOffsetLabel &
-IonScript::getPrebarrier(size_t index)
-{
-    JS_ASSERT(index < numPrebarriers());
-    return prebarrierList()[index];
-}
-
-void
-IonScript::copyPrebarrierEntries(const CodeOffsetLabel *barriers, MacroAssembler &masm)
-{
-    memcpy(prebarrierList(), barriers, numPrebarriers() * sizeof(CodeOffsetLabel));
-
-    // On ARM, the saved offset may be wrong due to shuffling code buffers. Correct it.
-    for (size_t i = 0; i < numPrebarriers(); i++)
-        getPrebarrier(i).fixup(&masm);
-}
-
 const SafepointIndex *
 IonScript::getSafepointIndex(uint32_t disp) const
 {
     JS_ASSERT(safepointIndexEntries_ > 0);
 
     const SafepointIndex *table = safepointIndices();
     if (safepointIndexEntries_ == 1) {
         JS_ASSERT(disp == table[0].displacement());
@@ -730,24 +724,17 @@ void
 IonScript::Destroy(FreeOp *fop, IonScript *script)
 {
     fop->free_(script);
 }
 
 void
 IonScript::toggleBarriers(bool enabled)
 {
-    for (size_t i = 0; i < numPrebarriers(); i++) {
-        CodeLocationLabel loc(method(), getPrebarrier(i));
-
-        if (enabled)
-            Assembler::ToggleToCmp(loc);
-        else
-            Assembler::ToggleToJmp(loc);
-    }
+    method()->togglePreBarriers(enabled);
 }
 
 void
 IonScript::purgeCaches(JSCompartment *c)
 {
     // Don't reset any ICs if we're invalidated, otherwise, repointing the
     // inline jump could overwrite an invalidation marker. These ICs can
     // no longer run, however, the IC slow paths may be active on the stack.
--- a/js/src/ion/IonCode.h
+++ b/js/src/ion/IonCode.h
@@ -40,57 +40,69 @@ class IonCode : public gc::Cell
   protected:
     uint8_t *code_;
     JSC::ExecutablePool *pool_;
     uint32_t bufferSize_;             // Total buffer size.
     uint32_t insnSize_;               // Instruction stream size.
     uint32_t dataSize_;               // Size of the read-only data area.
     uint32_t jumpRelocTableBytes_;    // Size of the jump relocation table.
     uint32_t dataRelocTableBytes_;    // Size of the data relocation table.
+    uint32_t preBarrierTableBytes_;   // Size of the prebarrier table.
     JSBool invalidated_;            // Whether the code object has been invalidated.
                                     // This is necessary to prevent GC tracing.
 
+#if JS_BITS_PER_WORD == 32
+    // Ensure IonCode is gc::Cell aligned.
+    uint32_t padding_;
+#endif
+
     IonCode()
       : code_(NULL),
         pool_(NULL)
     { }
     IonCode(uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
       : code_(code),
         pool_(pool),
         bufferSize_(bufferSize),
         insnSize_(0),
         dataSize_(0),
         jumpRelocTableBytes_(0),
         dataRelocTableBytes_(0),
+        preBarrierTableBytes_(0),
         invalidated_(false)
     { }
 
     uint32_t dataOffset() const {
         return insnSize_;
     }
     uint32_t jumpRelocTableOffset() const {
         return dataOffset() + dataSize_;
     }
     uint32_t dataRelocTableOffset() const {
         return jumpRelocTableOffset() + jumpRelocTableBytes_;
     }
+    uint32_t preBarrierTableOffset() const {
+        return dataRelocTableOffset() + dataRelocTableBytes_;
+    }
 
   public:
     uint8_t *raw() const {
         return code_;
     }
     size_t instructionsSize() const {
         return insnSize_;
     }
     void trace(JSTracer *trc);
     void finalize(FreeOp *fop);
     void setInvalidated() {
         invalidated_ = true;
     }
 
+    void togglePreBarriers(bool enabled);
+
     // If this IonCode object has been, effectively, corrupted due to
     // invalidation patching, then we have to remember this so we don't try and
     // trace relocation entries that may now be corrupt.
     bool invalidated() const {
         return !!invalidated_;
     }
 
     template <typename T> T as() const {
@@ -186,20 +198,16 @@ struct IonScript
     // Map OSI-point displacement to snapshot.
     uint32_t osiIndexOffset_;
     uint32_t osiIndexEntries_;
 
     // State for polymorphic caches in the compiled code.
     uint32_t cacheList_;
     uint32_t cacheEntries_;
 
-    // Offset list for patchable pre-barriers.
-    uint32_t prebarrierList_;
-    uint32_t prebarrierEntries_;
-
     // Offset to and length of the safepoint table in bytes.
     uint32_t safepointsStart_;
     uint32_t safepointsSize_;
 
     // List of compiled/inlined JSScript's.
     uint32_t scriptList_;
     uint32_t scriptEntries_;
 
@@ -228,35 +236,31 @@ struct IonScript
         return const_cast<IonScript *>(this)->osiIndices();
     }
     OsiIndex *osiIndices() {
         return (OsiIndex *)(reinterpret_cast<uint8_t *>(this) + osiIndexOffset_);
     }
     IonCache *cacheList() {
         return (IonCache *)(reinterpret_cast<uint8_t *>(this) + cacheList_);
     }
-    CodeOffsetLabel *prebarrierList() {
-        return (CodeOffsetLabel *)(reinterpret_cast<uint8_t *>(this) + prebarrierList_);
-    }
     JSScript **scriptList() const {
         return (JSScript **)(reinterpret_cast<const uint8_t *>(this) + scriptList_);
     }
 
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
     static IonScript *New(JSContext *cx, uint32_t frameLocals, uint32_t frameSize,
                           size_t snapshotsSize, size_t snapshotEntries,
                           size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
-                          size_t cacheEntries, size_t prebarrierEntries, size_t safepointsSize,
-                          size_t scriptEntries);
+                          size_t cacheEntries, size_t safepointsSize, size_t scriptEntries);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
 
     static inline size_t offsetOfMethod() {
         return offsetof(IonScript, method_);
     }
     static inline size_t offsetOfOsrEntryOffset() {
         return offsetof(IonScript, osrEntryOffset_);
@@ -361,29 +365,24 @@ struct IonScript
         return getSafepointIndex(retAddr - method()->raw());
     }
     const OsiIndex *getOsiIndex(uint32_t disp) const;
     const OsiIndex *getOsiIndex(uint8_t *retAddr) const;
     inline IonCache &getCache(size_t index);
     size_t numCaches() const {
         return cacheEntries_;
     }
-    inline CodeOffsetLabel &getPrebarrier(size_t index);
-    size_t numPrebarriers() const {
-        return prebarrierEntries_;
-    }
     void toggleBarriers(bool enabled);
     void purgeCaches(JSCompartment *c);
     void copySnapshots(const SnapshotWriter *writer);
     void copyBailoutTable(const SnapshotOffset *table);
     void copyConstants(const HeapValue *vp);
     void copySafepointIndices(const SafepointIndex *firstSafepointIndex, MacroAssembler &masm);
     void copyOsiIndices(const OsiIndex *firstOsiIndex, MacroAssembler &masm);
     void copyCacheEntries(const IonCache *caches, MacroAssembler &masm);
-    void copyPrebarrierEntries(const CodeOffsetLabel *barriers, MacroAssembler &masm);
     void copySafepoints(const SafepointWriter *writer);
     void copyScriptEntries(JSScript **scripts);
 
     bool invalidated() const {
         return refcount_ != 0;
     }
     size_t refcount() const {
         return refcount_;
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -411,31 +411,31 @@ class MacroAssembler : public MacroAssem
 
         call(preBarrier);
         Pop(PreBarrierReg);
 
         bind(&done);
     }
 
     template <typename T>
-    CodeOffsetLabel patchableCallPreBarrier(const T &address, MIRType type) {
+    void patchableCallPreBarrier(const T &address, MIRType type) {
         JS_ASSERT(type == MIRType_Value || type == MIRType_String || type == MIRType_Object);
 
         Label done;
 
         // All barriers are off by default.
         // They are enabled if necessary at the end of CodeGenerator::generate().
         CodeOffsetLabel nopJump = toggledJump(&done);
+        writePrebarrierOffset(nopJump);
 
         callPreBarrier(address, type);
         jump(&done);
 
         align(8);
         bind(&done);
-        return nopJump;
     }
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
                             Label *fail);
--- a/js/src/ion/arm/Assembler-arm.cpp
+++ b/js/src/ion/arm/Assembler-arm.cpp
@@ -456,16 +456,22 @@ Assembler::finish()
         dataRelocations_.writeUnsigned(real_offset);
     }
 
     for (unsigned int i = 0; i < tmpJumpRelocations_.length(); i++) {
         int offset = tmpJumpRelocations_[i].getOffset();
         int real_offset = offset + m_buffer.poolSizeBefore(offset);
         jumpRelocations_.writeUnsigned(real_offset);
     }
+
+    for (unsigned int i = 0; i < tmpPreBarriers_.length(); i++) {
+        int offset = tmpPreBarriers_[i].getOffset();
+        int real_offset = offset + m_buffer.poolSizeBefore(offset);
+        preBarriers_.writeUnsigned(real_offset);
+    }
 }
 
 void
 Assembler::executableCopy(uint8_t *buffer)
 {
     JS_ASSERT(isFinished);
     m_buffer.executableCopy(buffer);
     AutoFlushCache::updateTop((uintptr_t)buffer, m_buffer.size());
@@ -697,16 +703,23 @@ Assembler::copyJumpRelocationTable(uint8
 void
 Assembler::copyDataRelocationTable(uint8_t *dest)
 {
     if (dataRelocations_.length())
         memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
 }
 
 void
+Assembler::copyPreBarrierTable(uint8_t *dest)
+{
+    if (preBarriers_.length())
+        memcpy(dest, preBarriers_.buffer(), preBarriers_.length());
+}
+
+void
 Assembler::trace(JSTracer *trc)
 {
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch &rp = jumps_[i];
         if (rp.kind == Relocation::IONCODE) {
             IonCode *code = IonCode::FromExecutable((uint8_t*)rp.target);
             MarkIonCodeUnbarriered(trc, &code, "masmrel32");
             JS_ASSERT(code == IonCode::FromExecutable((uint8_t*)rp.target));
@@ -1102,17 +1115,21 @@ VFPRegister::isMissing()
     JS_ASSERT(!_isInvalid);
     return _isMissing;
 }
 
 
 bool
 Assembler::oom() const
 {
-    return m_buffer.oom() || !enoughMemory_ || jumpRelocations_.oom();
+    return m_buffer.oom() ||
+        !enoughMemory_ ||
+        jumpRelocations_.oom() ||
+        dataRelocations_.oom() ||
+        preBarriers_.oom();
 }
 
 bool
 Assembler::addDeferredData(DeferredData *data, size_t bytes)
 {
     data->setOffset(dataBytesNeeded_);
     dataBytesNeeded_ += bytes;
     if (dataBytesNeeded_ >= MAX_BUFFER_SIZE)
@@ -1141,30 +1158,38 @@ Assembler::jumpRelocationTableBytes() co
     return jumpRelocations_.length();
 }
 size_t
 Assembler::dataRelocationTableBytes() const
 {
     return dataRelocations_.length();
 }
 
+size_t
+Assembler::preBarrierTableBytes() const
+{
+    return preBarriers_.length();
+}
+
 // Size of the data table, in bytes.
 size_t
 Assembler::dataSize() const
 {
     return dataBytesNeeded_;
 }
 size_t
 Assembler::bytesNeeded() const
 {
     return size() +
         dataSize() +
         jumpRelocationTableBytes() +
-        dataRelocationTableBytes();
+        dataRelocationTableBytes() +
+        preBarrierTableBytes();
 }
+
 // write a blob of binary into the instruction stream
 BufferOffset
 Assembler::writeInst(uint32_t x, uint32_t *dest)
 {
     if (dest == NULL)
         return m_buffer.putInt(x);
 
     writeInstStatic(x, dest);
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -1173,26 +1173,28 @@ class Assembler
     //       It is currently a big hack, and probably shouldn't exist
     class JumpPool;
     js::Vector<DeferredData *, 0, SystemAllocPolicy> data_;
     js::Vector<CodeLabel *, 0, SystemAllocPolicy> codeLabels_;
     js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
     js::Vector<JumpPool *, 0, SystemAllocPolicy> jumpPools_;
     js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpJumpRelocations_;
     js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpDataRelocations_;
+    js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpPreBarriers_;
     class JumpPool : TempObject
     {
         BufferOffset start;
         uint32_t size;
         bool fixup(IonCode *code, uint8_t *data);
     };
 
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
     CompactBufferWriter relocations_;
+    CompactBufferWriter preBarriers_;
     size_t dataBytesNeeded_;
 
     bool enoughMemory_;
 
     //typedef JSC::AssemblerBufferWithConstantPool<1024, 4, 4, js::ion::Assembler> ARMBuffer;
     ARMBuffer m_buffer;
 
     // There is now a semi-unified interface for instruction generation.
@@ -1244,16 +1246,19 @@ class Assembler
     }
 
     // As opposed to x86/x64 version, the data relocation has to be executed
     // before to recover the pointer, and not after.
     void writeDataRelocation(const ImmGCPtr &ptr) {
         if (ptr.value)
             tmpDataRelocations_.append(nextOffset());
     }
+    void writePrebarrierOffset(CodeOffsetLabel label) {
+        tmpPreBarriers_.append(BufferOffset(label.offset()));
+    }
 
     enum RelocBranchStyle {
         B_MOVWT,
         B_LDR_BX,
         B_LDR,
         B_MOVW_ADD
     };
 
@@ -1280,28 +1285,31 @@ class Assembler
 
   private:
     bool isFinished;
   public:
     void finish();
     void executableCopy(void *buffer);
     void processDeferredData(IonCode *code, uint8_t *data);
     void processCodeLabels(IonCode *code);
-    void copyJumpRelocationTable(uint8_t *buffer);
-    void copyDataRelocationTable(uint8_t *buffer);
+    void copyJumpRelocationTable(uint8_t *dest);
+    void copyDataRelocationTable(uint8_t *dest);
+    void copyPreBarrierTable(uint8_t *dest);
 
     bool addDeferredData(DeferredData *data, size_t bytes);
 
     bool addCodeLabel(CodeLabel *label);
 
     // Size of the instruction stream, in bytes.
     size_t size() const;
     // Size of the jump relocation table, in bytes.
     size_t jumpRelocationTableBytes() const;
     size_t dataRelocationTableBytes() const;
+    size_t preBarrierTableBytes() const;
+
     // Size of the data table, in bytes.
     size_t dataSize() const;
     size_t bytesNeeded() const;
 
     // Write a blob of binary into the instruction stream *OR*
     // into a destination address. If dest is NULL (the default), then the
     // instruction gets written into the instruction stream. If dest is not null
     // it is interpreted as a pointer to the location that we want the
--- a/js/src/ion/shared/Assembler-x86-shared.cpp
+++ b/js/src/ion/shared/Assembler-x86-shared.cpp
@@ -22,16 +22,23 @@ AssemblerX86Shared::copyJumpRelocationTa
 
 void
 AssemblerX86Shared::copyDataRelocationTable(uint8_t *dest)
 {
     if (dataRelocations_.length())
         memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
 }
 
+void
+AssemblerX86Shared::copyPreBarrierTable(uint8_t *dest)
+{
+    if (preBarriers_.length())
+        memcpy(dest, preBarriers_.buffer(), preBarriers_.length());
+}
+
 static void
 TraceDataRelocations(JSTracer *trc, uint8_t *buffer, CompactBufferReader &reader)
 {
     while (reader.more()) {
         size_t offset = reader.readUnsigned();
         void **ptr = JSC::X86Assembler::getPointerRef(buffer + offset);
 
 #ifdef JS_PUNBOX64
@@ -48,16 +55,17 @@ TraceDataRelocations(JSTracer *trc, uint
         }
 #endif
 
         // No barrier needed since these are constants.
         gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(ptr), "ion-masm-ptr");
     }
 }
 
+
 void
 AssemblerX86Shared::TraceDataRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
 
 void
 AssemblerX86Shared::trace(JSTracer *trc)
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -28,27 +28,31 @@ class AssemblerX86Shared
         { }
     };
 
     js::Vector<DeferredData *, 0, SystemAllocPolicy> data_;
     js::Vector<CodeLabel *, 0, SystemAllocPolicy> codeLabels_;
     js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
+    CompactBufferWriter preBarriers_;
     size_t dataBytesNeeded_;
     bool enoughMemory_;
 
     void writeDataRelocation(const Value &val) {
         if (val.isMarkable())
             dataRelocations_.writeUnsigned(masm.currentOffset());
     }
     void writeDataRelocation(const ImmGCPtr &ptr) {
         if (ptr.value)
             dataRelocations_.writeUnsigned(masm.currentOffset());
     }
+    void writePrebarrierOffset(CodeOffsetLabel label) {
+        preBarriers_.writeUnsigned(label.offset());
+    }
 
   protected:
     JSC::X86Assembler masm;
 
     typedef JSC::X86Assembler::JmpSrc JmpSrc;
     typedef JSC::X86Assembler::JmpDst JmpDst;
 
   public:
@@ -150,28 +154,30 @@ class AssemblerX86Shared
 
     // MacroAssemblers hold onto gcthings, so they are traced by the GC.
     void trace(JSTracer *trc);
 
     bool oom() const {
         return masm.oom() ||
                !enoughMemory_ ||
                jumpRelocations_.oom() ||
-               dataRelocations_.oom();
+               dataRelocations_.oom() ||
+               preBarriers_.oom();
     }
 
     void setPrinter(Sprinter *sp) {
         masm.setPrinter(sp);
     }
 
     void executableCopy(void *buffer);
     void processDeferredData(IonCode *code, uint8_t *data);
     void processCodeLabels(IonCode *code);
-    void copyJumpRelocationTable(uint8_t *buffer);
-    void copyDataRelocationTable(uint8_t *buffer);
+    void copyJumpRelocationTable(uint8_t *dest);
+    void copyDataRelocationTable(uint8_t *dest);
+    void copyPreBarrierTable(uint8_t *dest);
 
     bool addDeferredData(DeferredData *data, size_t bytes) {
         data->setOffset(dataBytesNeeded_);
         dataBytesNeeded_ += bytes;
         if (dataBytesNeeded_ >= MAX_BUFFER_SIZE)
             return false;
         return data_.append(data);
     }
@@ -186,25 +192,29 @@ class AssemblerX86Shared
     }
     // Size of the jump relocation table, in bytes.
     size_t jumpRelocationTableBytes() const {
         return jumpRelocations_.length();
     }
     size_t dataRelocationTableBytes() const {
         return dataRelocations_.length();
     }
+    size_t preBarrierTableBytes() const {
+        return preBarriers_.length();
+    }
     // Size of the data table, in bytes.
     size_t dataSize() const {
         return dataBytesNeeded_;
     }
     size_t bytesNeeded() const {
         return size() +
                dataSize() +
                jumpRelocationTableBytes() +
-               dataRelocationTableBytes();
+               dataRelocationTableBytes() +
+               preBarrierTableBytes();
     }
 
   public:
     void align(int alignment) {
         masm.align(alignment);
     }
     void movl(const Imm32 &imm32, const Register &dest) {
         masm.movl_i32r(imm32.value, dest.code());
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -460,33 +460,29 @@ CodeGeneratorShared::visitOutOfLineTrunc
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 void
 CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type)
 {
-    CodeOffsetLabel offset;
-
     if (index->isConstant()) {
         Address address(base, ToInt32(index) * sizeof(Value));
-        offset = masm.patchableCallPreBarrier(address, type);
+        masm.patchableCallPreBarrier(address, type);
     } else {
         BaseIndex address(base, ToRegister(index), TimesEight);
-        offset = masm.patchableCallPreBarrier(address, type);
+        masm.patchableCallPreBarrier(address, type);
     }
-
-    addPreBarrierOffset(offset);
 }
 
 void
 CodeGeneratorShared::emitPreBarrier(Address address, MIRType type)
 {
-    addPreBarrierOffset(masm.patchableCallPreBarrier(address, type));
+    masm.patchableCallPreBarrier(address, type);
 }
 
 void
 CodeGeneratorShared::dropArguments(unsigned argc)
 {
     for (unsigned i = 0; i < argc; i++)
         pushedArgumentSlots_.popBack();
 }
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -56,19 +56,16 @@ class CodeGeneratorShared : public LInst
     js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_;
 
     // Mapping from bailout table ID to an offset in the snapshot buffer.
     js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_;
 
     // Vector of information about generated polymorphic inline caches.
     js::Vector<IonCache, 0, SystemAllocPolicy> cacheList_;
 
-    // Vector of all patchable write pre-barrier offsets.
-    js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> barrierOffsets_;
-
     // List of stack slots that have been pushed as arguments to an MCall.
     js::Vector<uint32_t, 0, SystemAllocPolicy> pushedArgumentSlots_;
 
     // When profiling is enabled, this is the instrumentation manager which
     // maintains state of what script is currently being generated (for inline
     // scripts) and when instrumentation needs to be emitted or skipped.
     IonInstrumentation sps_;
 
@@ -157,20 +154,16 @@ class CodeGeneratorShared : public LInst
   protected:
 
     size_t allocateCache(const IonCache &cache) {
         size_t index = cacheList_.length();
         masm.reportMemory(cacheList_.append(cache));
         return index;
     }
 
-    void addPreBarrierOffset(CodeOffsetLabel offset) {
-        masm.reportMemory(barrierOffsets_.append(offset));
-    }
-
   protected:
     // Encodes an LSnapshot into the compressed snapshot buffer, returning
     // false on failure.
     bool encode(LSnapshot *snapshot);
     bool encodeSlots(LSnapshot *snapshot, MResumePoint *resumePoint, uint32_t *startIndex);
 
     // Attempts to assign a BailoutId to a snapshot, if one isn't already set.
     // If the bailout table is full, this returns false, which is not a fatal