Bug 835003 - simplify generation of MTableSwitch jump tables (r=h4writer)
authorLuke Wagner <luke@mozilla.com>
Thu, 31 Jan 2013 11:30:35 -0800
changeset 120512 d693f77e31664963fac8a9828e0ff136c0053a1e
parent 120511 bf98cf3585ab0e1d5078a113bee98d8dc21336f3
child 120513 a3803aaadbdbff5af3728101d3b1a43b5948cadb
push id24256
push userryanvm@gmail.com
push dateFri, 01 Feb 2013 20:50:01 +0000
treeherdermozilla-central@4e7c92906a79 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs835003
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 835003 - simplify generation of MTableSwitch jump tables (r=h4writer)
js/src/assembler/assembler/X86Assembler.h
js/src/ion/Ion.cpp
js/src/ion/arm/Assembler-arm.cpp
js/src/ion/arm/Assembler-arm.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/shared/Assembler-shared.h
js/src/ion/shared/Assembler-x86-shared.cpp
js/src/ion/shared/Assembler-x86-shared.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -2437,24 +2437,29 @@ public:
     {
         FIXME_INSN_PRINTING;
         while (!m_formatter.isAligned(alignment))
             m_formatter.oneByteOp(OP_HLT);
 
         return label();
     }
 
+    void jumpTablePointer(uintptr_t ptr)
+    {
+        m_formatter.jumpTablePointer(ptr);
+    }
+
     // Linking & patching:
     //
     // 'link' and 'patch' methods are for use on unprotected code - such as the code
     // within the AssemblerBuffer, and code being patched by the patch buffer.  Once
     // code has been finalized it is (platform support permitting) within a non-
     // writable region of memory; to modify the code in an execute-only execuable
     // pool the 'repatch' and 'relink' methods should be used.
-    
+
     // Like Lua's emitter, we thread jump lists through the unpatched target
     // field, which will get fixed up when the label (which has a pointer to
     // the head of the jump list) is bound.
     bool nextJump(const JmpSrc& from, JmpSrc* next)
     {
         char* code = reinterpret_cast<char*>(m_formatter.data());
         int32_t offset = getInt32(code + from.m_offset);
         if (offset == -1)
@@ -2984,16 +2989,28 @@ private:
         }
 
         JmpSrc immediateRel32()
         {
             m_buffer.putIntUnchecked(0);
             return JmpSrc(m_buffer.size());
         }
 
+        // Data:
+
+        void jumpTablePointer(uintptr_t ptr)
+        {
+            m_buffer.ensureSpace(sizeof(uintptr_t));
+#if WTF_CPU_X86_64
+            m_buffer.putInt64Unchecked(ptr);
+#else
+            m_buffer.putIntUnchecked(ptr);
+#endif
+        }
+
         // Administrative methods:
 
         size_t size() const { return m_buffer.size(); }
         unsigned char *buffer() const { return m_buffer.buffer(); }
         bool oom() const { return m_buffer.oom(); }
         bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); }
         void* data() const { return m_buffer.data(); }
         void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) {
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -345,19 +345,16 @@ void
 IonCode::copyFrom(MacroAssembler &masm)
 {
     // Store the IonCode pointer right before the code buffer, so we can
     // recover the gcthing from relocation tables.
     *(IonCode **)(code_ - sizeof(IonCode *)) = this;
     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());
--- a/js/src/ion/arm/Assembler-arm.cpp
+++ b/js/src/ion/arm/Assembler-arm.cpp
@@ -740,45 +740,47 @@ Assembler::trace(JSTracer *trc)
         }
     }
 
     if (tmpDataRelocations_.length())
         ::TraceDataRelocations(trc, &m_buffer, &tmpDataRelocations_);
 }
 
 void
-Assembler::processDeferredData(IonCode *code, uint8_t *data)
-{
-    // Deferred Data is something like Pools for X86.
-    // Since ARM has competent pools, this isn't actually used.
-    // Except of course, for SwitchTables.  Those are really shoehorned
-    // in and don't take up any space in the instruction stream, so dataSize()
-    // is still 0.
-    // NOTE: this means arm will in fact break if the for loop is removed.
-    JS_ASSERT(dataSize() == 0);
-
-    for (size_t i = 0; i < data_.length(); i++) {
-        DeferredData *deferred = data_[i];
-        //Bind(code, deferred->label(), data + deferred->offset());
-        deferred->copy(code, data + deferred->offset());
-    }
-
-}
-
-// As far as I can tell, CodeLabels were supposed to be used in switch tables
-// and they aren't used there, nor anywhere else.
-void
 Assembler::processCodeLabels(IonCode *code)
 {
     for (size_t i = 0; i < codeLabels_.length(); i++) {
-        //Bind(code, label->dest(), code->raw() + label->src()->offset());
-        JS_NOT_REACHED("dead code?");
+        CodeLabel *label = codeLabels_[i];
+        Bind(code, label->dest(), code->raw() + actualOffset(label->src()->offset()));
     }
 }
 
+void
+Assembler::writeCodePointer(AbsoluteLabel *absoluteLabel) {
+    JS_ASSERT(!absoluteLabel->bound());
+    BufferOffset off = writeInst(-1);
+
+    // x86/x64 makes general use of AbsoluteLabel and weaves a linked list of
+    // uses of an AbsoluteLabel through the assembly. ARM only uses labels
+    // for the case statements of switch jump tables. Thus, for simplicity, we
+    // simply treat the AbsoluteLabel as a label and bind it to the offset of
+    // the jump table entry that needs to be patched.
+    LabelBase *label = absoluteLabel;
+    label->bind(off.getOffset());
+}
+
+void
+Assembler::Bind(IonCode *code, AbsoluteLabel *label, const void *address)
+{
+    // See writeCodePointer comment.
+    uint8_t *raw = code->raw();
+    uint32_t off = actualOffset(label->offset());
+    *reinterpret_cast<const void **>(raw + off) = address;
+}
+
 Assembler::Condition
 Assembler::InvertCondition(Condition cond)
 {
     const uint32_t ConditionInversionBit = 0x10000000;
     return Condition(ConditionInversionBit ^ cond);
 }
 
 Imm8::TwoImm8mData
@@ -1137,26 +1139,16 @@ Assembler::oom() const
     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)
-        return false;
-    return data_.append(data);
-}
-
-bool
 Assembler::addCodeLabel(CodeLabel *label)
 {
     return codeLabels_.append(label);
 }
 
 // Size of the instruction stream, in bytes.  Including pools. This function expects
 // all pools that need to be placed have been placed.  If they haven't then we
 // need to go an flush the pools :(
@@ -1180,25 +1172,19 @@ Assembler::dataRelocationTableBytes() co
 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() +
         preBarrierTableBytes();
 }
 
 // write a blob of binary into the instruction stream
 BufferOffset
 Assembler::writeInst(uint32_t x, uint32_t *dest)
@@ -2162,29 +2148,16 @@ Assembler::enterNoPool()
 }
 
 void
 Assembler::leaveNoPool()
 {
     m_buffer.leaveNoPool();
 }
 
-BufferOffset
-Assembler::as_jumpPool(uint32_t numCases)
-{
-    if (numCases == 0)
-        return BufferOffset();
-
-    BufferOffset ret = writeInst(-1);
-    for (uint32_t i = 1; i < numCases; i++)
-        writeInst(-1);
-
-    return ret;
-}
-
 ptrdiff_t
 Assembler::getBranchOffset(const Instruction *i_)
 {
     if (!i_->is<InstBranchImm>())
         return 0;
 
     InstBranchImm *i = i_->as<InstBranchImm>();
     BOffImm dest;
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -1167,17 +1167,16 @@ class Assembler
             target(target),
             kind(kind)
         { }
     };
 
     // TODO: this should actually be a pool-like object
     //       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
     {
@@ -1185,17 +1184,16 @@ class Assembler
         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.
     // During assembly, there is an active buffer that instructions are
@@ -1208,18 +1206,17 @@ class Assembler
     // to be null, but we shouldn't be looking at it in any case.
     static Assembler *dummy;
     Pool pools_[4];
     Pool *int32Pool;
     Pool *doublePool;
 
   public:
     Assembler()
-      : dataBytesNeeded_(0),
-        enoughMemory_(true),
+      : enoughMemory_(true),
         m_buffer(4, 4, 0, &pools_[0], 8),
         int32Pool(m_buffer.getPool(1)),
         doublePool(m_buffer.getPool(0)),
         isFinished(false),
         dtmActive(false),
         dtmCond(Always)
     {
         // Set up the backwards double region
@@ -1283,51 +1280,45 @@ class Assembler
     void setPrinter(Sprinter *sp) {
     }
 
   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 *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
     // instruction to be written.
     BufferOffset writeInst(uint32_t x, uint32_t *dest = NULL);
     // A static variant for the cases where we don't want to have an assembler
     // object at all. Normally, you would use the dummy (NULL) object.
     static void writeInstStatic(uint32_t x, uint32_t *dest);
 
   public:
-    // resreve enough space in the instruction stream for a jumpPool.
-    // return the reserved space.
-    BufferOffset as_jumpPool(uint32_t size);
+    void writeCodePointer(AbsoluteLabel *label);
 
     BufferOffset align(int alignment);
     BufferOffset as_nop();
     BufferOffset as_alu(Register dest, Register src1, Operand2 op2,
                 ALUOp op, SetCond_ sc = NoSetCond, Condition c = Always);
 
     BufferOffset as_mov(Register dest,
                 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
@@ -1535,16 +1526,17 @@ class Assembler
     void bind(Label *label, BufferOffset boff = BufferOffset());
     void bind(RepatchLabel *label);
     uint32_t currentOffset() {
         return nextOffset().getOffset();
     }
     void retarget(Label *label, Label *target);
     // I'm going to pretend this doesn't exist for now.
     void retarget(Label *label, void *target, Relocation::Kind reloc);
+    void Bind(IonCode *code, AbsoluteLabel *label, const void *address);
 
     void call(Label *label);
     void call(void *target);
 
     void as_bkpt();
 
   public:
     static void TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader);
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -21,42 +21,16 @@
 
 #include "jsscriptinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
-class DeferredJumpTable : public DeferredData
-{
-    MTableSwitch *mswitch;
-    BufferOffset off;
-    MacroAssembler *masm;
-  public:
-    DeferredJumpTable(MTableSwitch *mswitch, BufferOffset off_, MacroAssembler *masm_)
-      : mswitch(mswitch), off(off_), masm(masm_)
-    { }
-
-    void copy(IonCode *code, uint8_t *ignore__) const {
-        void **jumpData = (void **)(((char*)code->raw()) + masm->actualOffset(off).getOffset());
-        int numCases =  mswitch->numCases();
-        // For every case write the pointer to the start in the table
-        for (int j = 0; j < numCases; j++) {
-            LBlock *caseblock = mswitch->getCase(numCases - 1 - j)->lir();
-            Label *caseheader = caseblock->label();
-
-            uint32_t offset = caseheader->offset();
-            *jumpData = (void *)(code->raw() + masm->actualOffset(offset));
-            jumpData++;
-        }
-    }
-};
-
-
 // shared
 CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorShared(gen, graph),
     deoptLabel_(NULL)
 {
 }
 
 bool
@@ -893,16 +867,64 @@ CodeGeneratorARM::visitMoveGroup(LMoveGr
 
     MoveEmitter emitter(masm);
     emitter.emit(resolver);
     emitter.finish();
 
     return true;
 }
 
+class js::ion::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorARM>
+{
+    MTableSwitch *mir_;
+    Vector<CodeLabel*, 8, IonAllocPolicy> codeLabels_;
+
+    bool accept(CodeGeneratorARM *codegen) {
+        return codegen->visitOutOfLineTableSwitch(this);
+    }
+
+  public:
+    OutOfLineTableSwitch(MTableSwitch *mir)
+      : mir_(mir)
+    {}
+
+    MTableSwitch *mir() const {
+        return mir_;
+    }
+
+    bool addCodeLabel(CodeLabel *label) {
+        return codeLabels_.append(label);
+    }
+    CodeLabel *codeLabel(unsigned i) {
+        return codeLabels_[i];
+    }
+};
+
+bool
+CodeGeneratorARM::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool)
+{
+    MTableSwitch *mir = ool->mir();
+
+    int numCases = mir->numCases();
+    for (size_t i = 0; i < numCases; i++) {
+        LBlock *caseblock = mir->getCase(numCases - 1 - i)->lir();
+        Label *caseheader = caseblock->label();
+        uint32_t caseoffset = caseheader->offset();
+
+        // The entries of the jump table need to be absolute addresses and thus
+        // must be patched after codegen is finished.
+        CodeLabel *cl = ool->codeLabel(i);
+        cl->src()->bind(caseoffset);
+        if (!masm.addCodeLabel(cl))
+            return false;
+    }
+
+    return true;
+}
+
 bool
 CodeGeneratorARM::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index,
                                           const Register &base)
 {
     // the code generated by this is utter hax.
     // the end result looks something like:
     // SUBS index, input, #base
     // RSBSPL index, index, #max
@@ -932,21 +954,30 @@ CodeGeneratorARM::emitTableSwitchDispatc
 
     int32_t cases = mir->numCases();
     // Lower value with low value
     masm.ma_sub(index, Imm32(mir->low()), index, SetCond);
     masm.ma_rsb(index, Imm32(cases - 1), index, SetCond, Assembler::Unsigned);
     AutoForbidPools afp(&masm);
     masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::Unsigned);
     masm.ma_b(defaultcase);
-    DeferredJumpTable *d = new DeferredJumpTable(mir, masm.nextOffset(), &masm);
-    masm.as_jumpPool(cases);
 
-    if (!masm.addDeferredData(d, 0))
+    // To fill in the CodeLabels for the case entries, we need to first
+    // generate the case entries (we don't yet know their offsets in the
+    // instruction stream).
+    OutOfLineTableSwitch *ool = new OutOfLineTableSwitch(mir);
+    for (int32_t i = 0; i < cases; i++) {
+        CodeLabel *cl = new CodeLabel();
+        masm.writeCodePointer(cl->dest());
+        if (!ool->addCodeLabel(cl))
+            return false;
+    }
+    if (!addOutOfLineCode(ool))
         return false;
+
     return true;
 }
 
 bool
 CodeGeneratorARM::visitMathD(LMathD *math)
 {
     const LAllocation *src1 = math->getOperand(0);
     const LAllocation *src2 = math->getOperand(1);
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -10,16 +10,17 @@
 
 #include "Assembler-arm.h"
 #include "ion/shared/CodeGenerator-shared.h"
 
 namespace js {
 namespace ion {
 
 class OutOfLineBailout;
+class OutOfLineTableSwitch;
 
 class CodeGeneratorARM : public CodeGeneratorShared
 {
     friend class MoveResolverARM;
 
     CodeGeneratorARM *thisFromCtor() {return this;}
 
   protected:
@@ -102,16 +103,17 @@ class CodeGeneratorARM : public CodeGene
 
     virtual bool visitMathD(LMathD *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
+    bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool);
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
     ValueOperand ToTempValue(LInstruction *ins, size_t pos);
 
     // Functions for LTestVAndBranch.
     Register splitTagForTest(const ValueOperand &value);
--- a/js/src/ion/shared/Assembler-shared.h
+++ b/js/src/ion/shared/Assembler-shared.h
@@ -334,46 +334,16 @@ class CodeLabel : public TempObject
     AbsoluteLabel *dest() {
         return &dest_;
     }
     Label *src() {
         return &src_;
     }
 };
 
-// Deferred data is a chunk of data that cannot be computed until an assembly
-// buffer has been fully allocated, but should be attached to the final code
-// stream. At the time deferred data is emitted, the code buffer has been
-// completely allocated.
-class DeferredData : public TempObject
-{
-    // Label, which before linking is unbound.
-    AbsoluteLabel label_;
-
-    // Offset from the start of the data section.
-    int32_t offset_;
-
-  public:
-    DeferredData() : offset_(-1)
-    { }
-    int32_t offset() const {
-        JS_ASSERT(offset_ > -1);
-        return offset_;
-    }
-    void setOffset(int32_t offset) {
-        offset_ = offset;
-    }
-    AbsoluteLabel *label() {
-        return &label_;
-    }
-
-    // Must copy pending data into the buffer.
-    virtual void copy(IonCode *code, uint8_t *buffer) const = 0;
-};
-
 // Location of a jump or label in a generated IonCode block, relative to the
 // start of the block.
 
 class CodeOffsetJump
 {
     size_t offset_;
 
 #ifdef JS_SMALL_BRANCH
--- a/js/src/ion/shared/Assembler-x86-shared.cpp
+++ b/js/src/ion/shared/Assembler-x86-shared.cpp
@@ -86,26 +86,16 @@ AssemblerX86Shared::trace(JSTracer *trc)
 
 void
 AssemblerX86Shared::executableCopy(void *buffer)
 {
     masm.executableCopy(buffer);
 }
 
 void
-AssemblerX86Shared::processDeferredData(IonCode *code, uint8_t *data)
-{
-    for (size_t i = 0; i < data_.length(); i++) {
-        DeferredData *deferred = data_[i];
-        Bind(code, deferred->label(), data + deferred->offset());
-        deferred->copy(code, data + deferred->offset());
-    }
-}
-
-void
 AssemblerX86Shared::processCodeLabels(IonCode *code)
 {
     for (size_t i = 0; i < codeLabels_.length(); i++) {
         CodeLabel *label = codeLabels_[i];
         Bind(code, label->dest(), code->raw() + label->src()->offset());
     }
 }
 
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -23,23 +23,21 @@ class AssemblerX86Shared
 
         RelativePatch(int32_t offset, void *target, Relocation::Kind kind)
           : offset(offset),
             target(target),
             kind(kind)
         { }
     };
 
-    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)
@@ -134,18 +132,17 @@ class AssemblerX86Shared
 
     static void staticAsserts() {
         // DoubleConditionBits should not interfere with x86 condition codes.
         JS_STATIC_ASSERT(!((Equal | NotEqual | Above | AboveOrEqual | Below |
                             BelowOrEqual | Parity | NoParity) & DoubleConditionBits));
     }
 
     AssemblerX86Shared()
-      : dataBytesNeeded_(0),
-        enoughMemory_(true)
+      : enoughMemory_(true)
     {
     }
 
     static Condition InvertCondition(Condition cond);
 
     static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) {
         return static_cast<Condition>(cond & ~DoubleConditionBits);
     }
@@ -163,30 +160,21 @@ class AssemblerX86Shared
                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 *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);
-    }
-    
     bool addCodeLabel(CodeLabel *label) {
         return codeLabels_.append(label);
     }
 
     // Size of the instruction stream, in bytes.
     size_t size() const {
         return masm.size();
     }
@@ -196,31 +184,34 @@ class AssemblerX86Shared
     }
     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() +
                preBarrierTableBytes();
     }
 
   public:
     void align(int alignment) {
         masm.align(alignment);
     }
+    void writeCodePointer(AbsoluteLabel *label) {
+        JS_ASSERT(!label->bound());
+        // Thread the patch list through the unpatched address word in the
+        // instruction stream.
+        masm.jumpTablePointer(label->prev());
+        label->setPrev(masm.size());
+    }
     void movl(const Imm32 &imm32, const Register &dest) {
         masm.movl_i32r(imm32.value, dest.code());
     }
     void movl(const Register &src, const Register &dest) {
         masm.movl_rr(src.code(), dest.code());
     }
     void movl(const Operand &src, const Register &dest) {
         switch (src.kind()) {
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -17,40 +17,16 @@
 #include "ion/IonCompartment.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
-class DeferredJumpTable : public DeferredData
-{
-    MTableSwitch *mswitch;
-
-  public:
-    DeferredJumpTable(MTableSwitch *mswitch)
-      : mswitch(mswitch)
-    { }
-
-    void copy(IonCode *code, uint8_t *buffer) const {
-        void **jumpData = (void **)buffer;
-
-        // For every case write the pointer to the start in the table
-        for (size_t j = 0; j < mswitch->numCases(); j++) {
-            LBlock *caseblock = mswitch->getCase(j)->lir();
-            Label *caseheader = caseblock->label();
-
-            uint32_t offset = caseheader->offset();
-            *jumpData = (void *)(code->raw() + offset);
-            jumpData++;
-        }
-    }
-};
-
 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorShared(gen, graph),
     deoptLabel_(NULL)
 {
 }
 
 double
 test(double x, double y)
@@ -1021,38 +997,90 @@ CodeGeneratorX86Shared::visitMoveGroup(L
 
     MoveEmitter emitter(masm);
     emitter.emit(resolver);
     emitter.finish();
 
     return true;
 }
 
+class OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorX86Shared>
+{
+    MTableSwitch *mir_;
+    CodeLabel *jumpLabel_;
+
+    bool accept(CodeGeneratorX86Shared *codegen) {
+        return codegen->visitOutOfLineTableSwitch(this);
+    }
+
+  public:
+    OutOfLineTableSwitch(MTableSwitch *mir)
+      : mir_(mir), jumpLabel_(new CodeLabel)
+    {}
+
+    MTableSwitch *mir() const {
+        return mir_;
+    }
+
+    CodeLabel *jumpLabel() const {
+        return jumpLabel_;
+    }
+};
+
+bool
+CodeGeneratorX86Shared::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool)
+{
+    MTableSwitch *mir = ool->mir();
+
+    masm.align(sizeof(void*));
+    masm.bind(ool->jumpLabel()->src());
+    if (!masm.addCodeLabel(ool->jumpLabel()))
+        return false;
+
+    for (size_t i = 0; i < mir->numCases(); i++) {
+        LBlock *caseblock = mir->getCase(i)->lir();
+        Label *caseheader = caseblock->label();
+        uint32_t caseoffset = caseheader->offset();
+
+        // The entries of the jump table need to be absolute addresses and thus
+        // must be patched after codegen is finished.
+        CodeLabel *cl = new CodeLabel();
+        masm.writeCodePointer(cl->dest());
+        cl->src()->bind(caseoffset);
+        if (!masm.addCodeLabel(cl))
+            return false;
+    }
+
+    return true;
+}
+
 bool
 CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index,
                                                 const Register &base)
 {
     Label *defaultcase = mir->getDefault()->lir()->label();
 
     // Lower value with low value
     if (mir->low() != 0)
         masm.subl(Imm32(mir->low()), index);
 
     // Jump to default case if input is out of range
     int32_t cases = mir->numCases();
     masm.cmpl(index, Imm32(cases));
     masm.j(AssemblerX86Shared::AboveOrEqual, defaultcase);
 
-    // Create a JumpTable that during linking will get written.
-    DeferredJumpTable *d = new DeferredJumpTable(mir);
-    if (!masm.addDeferredData(d, (1 << ScalePointer) * cases))
+    // To fill in the CodeLabels for the case entries, we need to first
+    // generate the case entries (we don't yet know their offsets in the
+    // instruction stream).
+    OutOfLineTableSwitch *ool = new OutOfLineTableSwitch(mir);
+    if (!addOutOfLineCode(ool))
         return false;
 
     // Compute the position where a pointer to the right case stands.
-    masm.mov(d->label(), base);
+    masm.mov(ool->jumpLabel()->dest(), base);
     Operand pointer = Operand(base, index, ScalePointer);
 
     // Jump to the right case
     masm.jmp(pointer);
 
     return true;
 }
 
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -12,16 +12,17 @@
 
 namespace js {
 namespace ion {
 
 class OutOfLineBailout;
 class OutOfLineUndoALUOperation;
 class MulNegativeZeroCheck;
 class OutOfLineTruncate;
+class OutOfLineTableSwitch;
 
 class CodeGeneratorX86Shared : public CodeGeneratorShared
 {
     friend class MoveResolverX86;
 
     CodeGeneratorX86Shared *thisFromCtor() {
         return this;
     }
@@ -113,16 +114,17 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitGuardClass(LGuardClass *guard);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
     bool visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation *ool);
     bool visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool);
     bool visitOutOfLineTruncate(OutOfLineTruncate *ool);
+    bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool);
     bool generateInvalidateEpilogue();
 };
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorX86Shared>
 {
     LSnapshot *snapshot_;