Bug 1050219 - IonMonkey MIPS: Fix patching backedges while patched code is running. r=nbp
authorBranislav Rankov <branislav.rankov@imgtec.com>
Mon, 18 Aug 2014 17:00:27 +0200
changeset 200257 32628ddca30c9e8a39be492a787bfa5a6e50825d
parent 200256 2a8bff1b8b1d3b3fd89d8cbbb6a1279451c4b3a5
child 200258 5482a918ee738fb105676ddfb9e4ed4185649782
push id47858
push userbranislav.rankov@rt-rk.com
push dateTue, 19 Aug 2014 12:17:34 +0000
treeherdermozilla-inbound@32628ddca30c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1050219
milestone34.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 1050219 - IonMonkey MIPS: Fix patching backedges while patched code is running. r=nbp
js/src/jit/Ion.cpp
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/mips/Assembler-mips.cpp
js/src/jit/mips/Assembler-mips.h
js/src/jit/mips/CodeGenerator-mips.cpp
js/src/jit/mips/CodeGenerator-mips.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/x64/Assembler-x64.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -426,19 +426,20 @@ JitRuntime::patchIonBackedges(JSRuntime 
 
     // Patch all loop backedges in Ion code so that they either jump to the
     // normal loop header or to an interrupt handler each time they run.
     for (InlineListIterator<PatchableBackedge> iter(backedgeList_.begin());
          iter != backedgeList_.end();
          iter++)
     {
         PatchableBackedge *patchableBackedge = *iter;
-        PatchJump(patchableBackedge->backedge, target == BackedgeLoopHeader
-                                               ? patchableBackedge->loopHeader
-                                               : patchableBackedge->interruptCheck);
+        if (target == BackedgeLoopHeader)
+            PatchBackedge(patchableBackedge->backedge, patchableBackedge->loopHeader, target);
+        else
+            PatchBackedge(patchableBackedge->backedge, patchableBackedge->interruptCheck, target);
     }
 }
 
 void
 jit::RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode)
 {
     JitRuntime *jitRuntime = rt->jitRuntime();
     if (!jitRuntime)
@@ -1055,17 +1056,20 @@ IonScript::copyPatchableBackedges(JSCont
         CodeLocationLabel loopHeader(code, CodeOffsetLabel(loopHeaderOffset));
         CodeLocationLabel interruptCheck(code, CodeOffsetLabel(interruptCheckOffset));
         new(patchableBackedge) PatchableBackedge(backedge, loopHeader, interruptCheck);
 
         // Point the backedge to either of its possible targets, according to
         // whether an interrupt is currently desired, matching the targets
         // established by ensureIonCodeAccessible() above. We don't handle the
         // interrupt immediately as the interrupt lock is held here.
-        PatchJump(backedge, cx->runtime()->interrupt ? interruptCheck : loopHeader);
+        if (cx->runtime()->interrupt)
+            PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
+        else
+            PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);
 
         cx->runtime()->jitRuntime()->addPatchableBackedge(patchableBackedge);
     }
 }
 
 void
 IonScript::copySafepointIndices(const SafepointIndex *si, MacroAssembler &masm)
 {
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/arm/Architecture-arm.h"
 #include "jit/CompactBuffer.h"
 #include "jit/IonCode.h"
+#include "jit/JitCompartment.h"
 #include "jit/shared/Assembler-shared.h"
 #include "jit/shared/IonAssemblerBufferWithConstantPools.h"
 
 namespace js {
 namespace jit {
 
 // NOTE: there are duplicates in this list! Sometimes we want to specifically
 // refer to the link register as a link register (bl lr is much clearer than bl
@@ -1030,16 +1031,22 @@ class Operand
     }
     VFPAddr toVFPAddr() const {
         return VFPAddr(baseReg(), VFPOffImm(offset));
     }
 };
 
 void
 PatchJump(CodeLocationJump &jump_, CodeLocationLabel label);
+static inline void
+PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
+{
+    PatchJump(jump_, label);
+}
+
 class InstructionIterator;
 class Assembler;
 typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, Assembler> ARMBuffer;
 
 class Assembler : public AssemblerShared
 {
   public:
     // ARM conditional constants:
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1014,16 +1014,19 @@ class MacroAssemblerARMCompat : public M
     }
     void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label *label) {
         subPtr(imm, lhs);
         branch32(cond, lhs, Imm32(0), label);
     }
     void moveValue(const Value &val, Register type, Register data);
 
     CodeOffsetJump jumpWithPatch(RepatchLabel *label, Condition cond = Always);
+    CodeOffsetJump backedgeJump(RepatchLabel *label) {
+        return jumpWithPatch(label);
+    }
     template <typename T>
     CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel *label) {
         ma_cmp(reg, ptr);
         return jumpWithPatch(label, cond);
     }
     template <typename T>
     CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel *label) {
         ma_ldr(addr, secondScratchReg_);
--- a/js/src/jit/mips/Assembler-mips.cpp
+++ b/js/src/jit/mips/Assembler-mips.cpp
@@ -158,16 +158,44 @@ jit::PatchJump(CodeLocationJump &jump_, 
     Instruction *inst1 = (Instruction *)jump_.raw();
     Instruction *inst2 = inst1->next();
 
     Assembler::UpdateLuiOriValue(inst1, inst2, (uint32_t)label.raw());
 
     AutoFlushICache::flush(uintptr_t(inst1), 8);
 }
 
+// For more infromation about backedges look at comment in
+// MacroAssemblerMIPSCompat::backedgeJump()
+void
+jit::PatchBackedge(CodeLocationJump &jump, CodeLocationLabel label,
+                   JitRuntime::BackedgeTarget target)
+{
+    uint32_t sourceAddr = (uint32_t)jump.raw();
+    uint32_t targetAddr = (uint32_t)label.raw();
+    InstImm *branch = (InstImm *)jump.raw();
+
+    MOZ_ASSERT(branch->extractOpcode() == (uint32_t(op_beq) >> OpcodeShift));
+
+    if (BOffImm16::IsInRange(targetAddr - sourceAddr)) {
+        branch->setBOffImm16(BOffImm16(targetAddr - sourceAddr));
+    } else {
+        if (target == JitRuntime::BackedgeLoopHeader) {
+            Instruction *lui = &branch[1];
+            Assembler::UpdateLuiOriValue(lui, lui->next(), targetAddr);
+            // Jump to ori. The lui will be executed in delay slot.
+            branch->setBOffImm16(BOffImm16(2 * sizeof(uint32_t)));
+        } else {
+            Instruction *lui = &branch[4];
+            Assembler::UpdateLuiOriValue(lui, lui->next(), targetAddr);
+            branch->setBOffImm16(BOffImm16(4 * sizeof(uint32_t)));
+        }
+    }
+}
+
 void
 Assembler::finish()
 {
     MOZ_ASSERT(!isFinished);
     isFinished = true;
 }
 
 void
@@ -583,16 +611,23 @@ Assembler::as_xori(Register rd, Register
 // Branch and jump instructions
 BufferOffset
 Assembler::as_bal(BOffImm16 off)
 {
     BufferOffset bo = writeInst(InstImm(op_regimm, zero, rt_bgezal, off).encode());
     return bo;
 }
 
+BufferOffset
+Assembler::as_b(BOffImm16 off)
+{
+    BufferOffset bo = writeInst(InstImm(op_beq, zero, zero, off).encode());
+    return bo;
+}
+
 InstImm
 Assembler::getBranchCode(JumpOrCall jumpOrCall)
 {
     if (jumpOrCall == BranchIsCall)
         return InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0));
 
     return InstImm(op_beq, zero, zero, BOffImm16(0));
 }
@@ -1309,20 +1344,29 @@ Assembler::bind(InstImm *inst, uint32_t 
 void
 Assembler::bind(RepatchLabel *label)
 {
     BufferOffset dest = nextOffset();
     if (label->used()) {
         // If the label has a use, then change this use to refer to
         // the bound label;
         BufferOffset b(label->offset());
-        Instruction *inst1 = editSrc(b);
-        Instruction *inst2 = inst1->next();
+        InstImm *inst1 = (InstImm *)editSrc(b);
 
-        UpdateLuiOriValue(inst1, inst2, dest.getOffset());
+        // If first instruction is branch, then this is a loop backedge.
+        if (inst1->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)) {
+            // Backedges are short jumps when bound, but can become long
+            // when patched.
+            uint32_t offset = dest.getOffset() - label->offset();
+            MOZ_ASSERT(BOffImm16::IsInRange(offset));
+            inst1->setBOffImm16(BOffImm16(offset));
+        } else {
+            UpdateLuiOriValue(inst1, inst1->next(), dest.getOffset());
+        }
+
     }
     label->bind(dest.getOffset());
 }
 
 void
 Assembler::retarget(Label *label, Label *target)
 {
     if (label->used()) {
--- a/js/src/jit/mips/Assembler-mips.h
+++ b/js/src/jit/mips/Assembler-mips.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/CompactBuffer.h"
 #include "jit/IonCode.h"
 #include "jit/IonSpewer.h"
+#include "jit/JitCompartment.h"
 #include "jit/mips/Architecture-mips.h"
 #include "jit/shared/Assembler-shared.h"
 #include "jit/shared/IonAssemblerBuffer.h"
 
 namespace js {
 namespace jit {
 
 static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero };
@@ -614,16 +615,20 @@ class Operand
     Register baseReg() const {
         MOZ_ASSERT(tag == MEM);
         return Register::FromCode(reg);
     }
 };
 
 void
 PatchJump(CodeLocationJump &jump_, CodeLocationLabel label);
+
+void
+PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target);
+
 class Assembler;
 typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer;
 
 class Assembler : public AssemblerShared
 {
   public:
 
     enum Condition {
@@ -806,16 +811,17 @@ class Assembler : public AssemblerShared
     static void WriteInstStatic(uint32_t x, uint32_t *dest);
 
   public:
     BufferOffset align(int alignment);
     BufferOffset as_nop();
 
     // Branch and jump instructions
     BufferOffset as_bal(BOffImm16 off);
+    BufferOffset as_b(BOffImm16 off);
 
     InstImm getBranchCode(JumpOrCall jumpOrCall);
     InstImm getBranchCode(Register s, Register t, Condition c);
     InstImm getBranchCode(Register s, Condition c);
     InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc);
 
     BufferOffset as_j(JOffImm26 off);
     BufferOffset as_jal(JOffImm26 off);
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -83,17 +83,17 @@ CodeGeneratorMIPS::branchToBlock(Assembl
 
         CodeOffsetJump backedge;
         Label skip;
         if (fmt == Assembler::DoubleFloat)
             masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
         else
             masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
 
-        backedge = masm.jumpWithPatch(&rejoin);
+        backedge = masm.backedgeJump(&rejoin);
         masm.bind(&rejoin);
         masm.bind(&skip);
 
         if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry)))
             MOZ_CRASH();
     } else {
         if (fmt == Assembler::DoubleFloat)
             masm.branchDouble(cond, lhs, rhs, mir->lir()->label());
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -116,17 +116,17 @@ class CodeGeneratorMIPS : public CodeGen
         if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
             // Note: the backedge is initially a jump to the next instruction.
             // It will be patched to the target block's label during link().
             RepatchLabel rejoin;
             CodeOffsetJump backedge;
             Label skip;
 
             masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
-            backedge = masm.jumpWithPatch(&rejoin);
+            backedge = masm.backedgeJump(&rejoin);
             masm.bind(&rejoin);
             masm.bind(&skip);
 
             if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry)))
                 MOZ_CRASH();
         } else {
             masm.ma_b(lhs, rhs, label, cond);
         }
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -2805,33 +2805,89 @@ MacroAssemblerMIPSCompat::moveValue(cons
     moveData(val, data);
 }
 void
 MacroAssemblerMIPSCompat::moveValue(const Value &val, const ValueOperand &dest)
 {
     moveValue(val, dest.typeReg(), dest.payloadReg());
 }
 
+/* There are 3 paths trough backedge jump. They are listed here in the order
+ * in which instructions are executed.
+ *  - The short jump is simple:
+ *     b offset            # Jumps directly to target.
+ *     lui at, addr1_hi    # In delay slot. Don't care about 'at' here.
+ *
+ *  - The long jump to loop header:
+ *      b label1
+ *      lui at, addr1_hi   # In delay slot. We use the value in 'at' later.
+ *    label1:
+ *      ori at, addr1_lo
+ *      jr at
+ *      lui at, addr2_hi   # In delay slot. Don't care about 'at' here.
+ *
+ *  - The long jump to interrupt loop:
+ *      b label2
+ *      lui at, addr1_hi   # In delay slot. Don't care about 'at' here.
+ *    label2:
+ *      lui at, addr2_hi
+ *      ori at, addr2_lo
+ *      jr at
+ *      nop                # In delay slot.
+ *
+ * The backedge is done this way to avoid patching lui+ori pair while it is
+ * being executed. Look also at jit::PatchBackedge().
+ */
+CodeOffsetJump
+MacroAssemblerMIPSCompat::backedgeJump(RepatchLabel *label)
+{
+    // Only one branch per label.
+    MOZ_ASSERT(!label->used());
+    uint32_t dest = label->bound() ? label->offset() : LabelBase::INVALID_OFFSET;
+    BufferOffset bo = nextOffset();
+    label->use(bo.getOffset());
+
+    // Backedges are short jumps when bound, but can become long when patched.
+    m_buffer.ensureSpace(8 * sizeof(uint32_t));
+    if (label->bound()) {
+        int32_t offset = label->offset() - bo.getOffset();
+        MOZ_ASSERT(BOffImm16::IsInRange(offset));
+        as_b(BOffImm16(offset));
+    } else {
+        // Jump to "label1" by default to jump to the loop header.
+        as_b(BOffImm16(2 * sizeof(uint32_t)));
+    }
+    // No need for nop here. We can safely put next instruction in delay slot.
+    ma_liPatchable(ScratchRegister, Imm32(dest));
+    MOZ_ASSERT(nextOffset().getOffset() - bo.getOffset() == 3 * sizeof(uint32_t));
+    as_jr(ScratchRegister);
+    // No need for nop here. We can safely put next instruction in delay slot.
+    ma_liPatchable(ScratchRegister, Imm32(dest));
+    as_jr(ScratchRegister);
+    as_nop();
+    MOZ_ASSERT(nextOffset().getOffset() - bo.getOffset() == 8 * sizeof(uint32_t));
+    return CodeOffsetJump(bo.getOffset());
+}
+
 CodeOffsetJump
 MacroAssemblerMIPSCompat::jumpWithPatch(RepatchLabel *label)
 {
     // Only one branch per label.
     MOZ_ASSERT(!label->used());
     uint32_t dest = label->bound() ? label->offset() : LabelBase::INVALID_OFFSET;
 
     BufferOffset bo = nextOffset();
     label->use(bo.getOffset());
     addLongJump(bo);
     ma_liPatchable(ScratchRegister, Imm32(dest));
     as_jr(ScratchRegister);
     as_nop();
     return CodeOffsetJump(bo.getOffset());
 }
 
-
 /////////////////////////////////////////////////////////////////
 // X86/X64-common/ARM/MIPS interface.
 /////////////////////////////////////////////////////////////////
 void
 MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst)
 {
     storeValue(val, Address(Register::FromCode(dst.base()), dst.disp()));
 }
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -752,16 +752,17 @@ class MacroAssemblerMIPSCompat : public 
     }
 
 protected:
     uint32_t getType(const Value &val);
     void moveData(const Value &val, Register data);
 public:
     void moveValue(const Value &val, Register type, Register data);
 
+    CodeOffsetJump backedgeJump(RepatchLabel *label);
     CodeOffsetJump jumpWithPatch(RepatchLabel *label);
 
     template <typename T>
     CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel *label) {
         movePtr(ptr, ScratchRegister);
         Label skipJump;
         ma_b(reg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
         CodeOffsetJump off = jumpWithPatch(label);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1247,17 +1247,17 @@ CodeGeneratorShared::jumpToBlock(MBasicB
     // No jump necessary if we can fall through to the next block.
     if (isNextBlock(mir->lir()))
         return;
 
     if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
         // Note: the backedge is initially a jump to the next instruction.
         // It will be patched to the target block's label during link().
         RepatchLabel rejoin;
-        CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin);
+        CodeOffsetJump backedge = masm.backedgeJump(&rejoin);
         masm.bind(&rejoin);
 
         masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
     } else {
         masm.jump(mir->lir()->label());
     }
 }
 
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_x64_Assembler_x64_h
 #define jit_x64_Assembler_x64_h
 
 #include "mozilla/ArrayUtils.h"
 
 #include "jit/IonCode.h"
+#include "jit/JitCompartment.h"
 #include "jit/shared/Assembler-shared.h"
 
 namespace js {
 namespace jit {
 
 static MOZ_CONSTEXPR_VAR Register rax = { JSC::X86Registers::eax };
 static MOZ_CONSTEXPR_VAR Register rbx = { JSC::X86Registers::ebx };
 static MOZ_CONSTEXPR_VAR Register rcx = { JSC::X86Registers::ecx };
@@ -749,16 +750,21 @@ PatchJump(CodeLocationJump jump, CodeLoc
 {
     if (JSC::X86Assembler::canRelinkJump(jump.raw(), label.raw())) {
         JSC::X86Assembler::setRel32(jump.raw(), label.raw());
     } else {
         JSC::X86Assembler::setRel32(jump.raw(), jump.jumpTableEntry());
         Assembler::PatchJumpEntry(jump.jumpTableEntry(), label.raw());
     }
 }
+static inline void
+PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
+{
+    PatchJump(jump_, label);
+}
 
 static inline bool
 GetIntArgReg(uint32_t intArg, uint32_t floatArg, Register *out)
 {
 #if defined(_WIN64)
     uint32_t arg = intArg + floatArg;
 #else
     uint32_t arg = intArg;
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -662,16 +662,20 @@ class MacroAssemblerX64 : public MacroAs
         return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED));
     }
 
     CodeOffsetJump jumpWithPatch(RepatchLabel *label, Condition cond) {
         JmpSrc src = jSrc(cond, label);
         return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED));
     }
 
+    CodeOffsetJump backedgeJump(RepatchLabel *label) {
+        return jumpWithPatch(label);
+    }
+
     template <typename S, typename T>
     CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel *label) {
         cmpPtr(lhs, ptr);
         return jumpWithPatch(label, cond);
     }
     void branchPtr(Condition cond, Register lhs, Register rhs, Label *label) {
         cmpPtr(lhs, rhs);
         j(cond, label);
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -7,16 +7,17 @@
 #ifndef jit_x86_Assembler_x86_h
 #define jit_x86_Assembler_x86_h
 
 #include "mozilla/ArrayUtils.h"
 
 #include "assembler/assembler/X86Assembler.h"
 #include "jit/CompactBuffer.h"
 #include "jit/IonCode.h"
+#include "jit/JitCompartment.h"
 #include "jit/shared/Assembler-shared.h"
 
 namespace js {
 namespace jit {
 
 static MOZ_CONSTEXPR_VAR Register eax = { JSC::X86Registers::eax };
 static MOZ_CONSTEXPR_VAR Register ecx = { JSC::X86Registers::ecx };
 static MOZ_CONSTEXPR_VAR Register edx = { JSC::X86Registers::edx };
@@ -156,16 +157,21 @@ PatchJump(CodeLocationJump jump, CodeLoc
     //   0F 80+cc <imm32>, or
     //   E9 <imm32>
     unsigned char *x = (unsigned char *)jump.raw() - 5;
     JS_ASSERT(((*x >= 0x80 && *x <= 0x8F) && *(x - 1) == 0x0F) ||
               (*x == 0xE9));
 #endif
     JSC::X86Assembler::setRel32(jump.raw(), label.raw());
 }
+static inline void
+PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
+{
+    PatchJump(jump_, label);
+}
 
 // Return operand from a JS -> JS call.
 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
 
 class Assembler : public AssemblerX86Shared
 {
     void writeRelocation(JmpSrc src) {
         jumpRelocations_.writeUnsigned(src.offset());
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -653,16 +653,20 @@ class MacroAssemblerX86 : public MacroAs
         return CodeOffsetJump(size());
     }
 
     CodeOffsetJump jumpWithPatch(RepatchLabel *label, Assembler::Condition cond) {
         j(cond, label);
         return CodeOffsetJump(size());
     }
 
+    CodeOffsetJump backedgeJump(RepatchLabel *label) {
+        return jumpWithPatch(label);
+    }
+
     template <typename S, typename T>
     CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel *label) {
         branchPtr(cond, lhs, ptr, label);
         return CodeOffsetJump(size());
     }
     void branchPtr(Condition cond, Register lhs, Register rhs, RepatchLabel *label) {
         cmpl(lhs, rhs);
         j(cond, label);