Bug 1271010 - Baldr: refactor nop-jump patching code (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Sat, 28 May 2016 16:51:07 -0500
changeset 338589 d9a0b729a7be40ae0f8d1f8ce4bd15415fba0914
parent 338588 520c9c228706376f3285bff5e693cad017db3435
child 338590 d21a912dfd85657ed906dcc7ec46b4d0a1eacca9
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1271010
milestone49.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 1271010 - Baldr: refactor nop-jump patching code (r=bbouvier) MozReview-Commit-ID: 92hS8F1U9vO
js/src/asmjs/WasmFrameIterator.cpp
js/src/jit/MacroAssembler.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm64/MacroAssembler-arm64.cpp
js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/jit/x86-shared/BaseAssembler-x86-shared.h
js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
--- a/js/src/asmjs/WasmFrameIterator.cpp
+++ b/js/src/asmjs/WasmFrameIterator.cpp
@@ -329,28 +329,17 @@ wasm::GenerateFunctionEpilogue(MacroAsse
     // Generate a nop that is overwritten by a jump to the profiling epilogue
     // when profiling is enabled.
     {
 #if defined(JS_CODEGEN_ARM)
         // Forbid pools from being inserted between the profilingJump label and
         // the nop since we need the location of the actual nop to patch it.
         AutoForbidPools afp(&masm, 1);
 #endif
-
-        // The exact form of this instruction must be kept consistent with the
-        // patching in Module::setProfilingEnabled.
-        offsets->profilingJump = masm.currentOffset();
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        masm.twoByteNop();
-#elif defined(JS_CODEGEN_ARM)
-        masm.nop();
-#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-        masm.nop();
-        masm.nop();
-#endif
+        offsets->profilingJump = masm.nopPatchableToNearJump().offset();
     }
 
     // Normal epilogue:
     masm.addToStackPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress));
     masm.ret();
     masm.setFramePushed(0);
 
     // Profiling epilogue:
@@ -764,52 +753,16 @@ wasm::EnableProfilingThunk(const Module&
 // Replace all the nops in all the epilogues of asm.js functions with jumps
 // to the profiling epilogues.
 void
 wasm::EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled)
 {
     if (!codeRange.isFunction())
         return;
 
-    uint8_t* jump = module.code() + codeRange.funcProfilingJump();
+    uint8_t* profilingJump = module.code() + codeRange.funcProfilingJump();
     uint8_t* profilingEpilogue = module.code() + codeRange.funcProfilingEpilogue();
 
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-    // An unconditional jump with a 1 byte offset immediate has the opcode
-    // 0x90. The offset is relative to the address of the instruction after
-    // the jump. 0x66 0x90 is the canonical two-byte nop.
-    ptrdiff_t jumpImmediate = profilingEpilogue - jump - 2;
-    MOZ_ASSERT(jumpImmediate > 0 && jumpImmediate <= 127);
-    if (enabled) {
-        MOZ_ASSERT(jump[0] == 0x66);
-        MOZ_ASSERT(jump[1] == 0x90);
-        jump[0] = 0xeb;
-        jump[1] = jumpImmediate;
-    } else {
-        MOZ_ASSERT(jump[0] == 0xeb);
-        MOZ_ASSERT(jump[1] == jumpImmediate);
-        jump[0] = 0x66;
-        jump[1] = 0x90;
-    }
-#elif defined(JS_CODEGEN_ARM)
-    if (enabled) {
-        MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
-        new (jump) InstBImm(BOffImm(profilingEpilogue - jump), Assembler::Always);
-    } else {
-        MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
-        new (jump) InstNOP();
-    }
-#elif defined(JS_CODEGEN_ARM64)
-    (void)jump;
-    (void)profilingEpilogue;
-    MOZ_CRASH();
-#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    InstImm* instr = (InstImm*)jump;
     if (enabled)
-        instr->setBOffImm16(BOffImm16(profilingEpilogue - jump));
+        MacroAssembler::patchNopToNearJump(profilingJump, profilingEpilogue);
     else
-        instr->makeNop();
-#elif defined(JS_CODEGEN_NONE)
-    MOZ_CRASH();
-#else
-# error "Missing architecture"
-#endif
+        MacroAssembler::patchNearJumpToNop(profilingJump);
 }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -488,16 +488,22 @@ class MacroAssembler : public MacroAssem
 
     // Thunks provide the ability to jump to any uint32_t offset from any other
     // uint32_t offset without using a constant pool (thus returning a simple
     // CodeOffset instead of a CodeOffsetJump).
     CodeOffset thunkWithPatch() PER_SHARED_ARCH;
     void patchThunk(uint32_t thunkOffset, uint32_t targetOffset) PER_SHARED_ARCH;
     static void repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset) PER_SHARED_ARCH;
 
+    // Emit a nop that can be patched to and from a nop and a jump with an int8
+    // relative displacement.
+    CodeOffset nopPatchableToNearJump() PER_SHARED_ARCH;
+    static void patchNopToNearJump(uint8_t* jump, uint8_t* target) PER_SHARED_ARCH;
+    static void patchNearJumpToNop(uint8_t* jump) PER_SHARED_ARCH;
+
     // Push the return address and make a call. On platforms where this function
     // is not defined, push the link register (pushReturnAddress) at the entry
     // point of the callee.
     void callAndPushReturnAddress(Register reg) DEFINED_ON(x86_shared);
     void callAndPushReturnAddress(Label* label) DEFINED_ON(x86_shared);
 
     void pushReturnAddress() DEFINED_ON(mips_shared, arm, arm64);
     void popReturnAddress() DEFINED_ON(mips_shared, arm, arm64);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4829,16 +4829,38 @@ MacroAssembler::repatchThunk(uint8_t* co
     uint32_t* u32 = reinterpret_cast<uint32_t*>(code + u32Offset);
 
     uint32_t addOffset = u32Offset - 4;
     MOZ_ASSERT(reinterpret_cast<Instruction*>(code + addOffset)->is<InstALU>());
 
     *u32 = (targetOffset - addOffset) - 8;
 }
 
+CodeOffset
+MacroAssembler::nopPatchableToNearJump()
+{
+    CodeOffset offset(currentOffset());
+    ma_nop();
+    return offset;
+}
+
+void
+MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target)
+{
+    MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
+    new (jump) InstBImm(BOffImm(target - jump), Assembler::Always);
+}
+
+void
+MacroAssembler::patchNearJumpToNop(uint8_t* jump)
+{
+    MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
+    new (jump) InstNOP();
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 void
 MacroAssembler::popReturnAddress()
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -552,16 +552,34 @@ MacroAssembler::patchThunk(uint32_t thun
 }
 
 void
 MacroAssembler::repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset)
 {
     MOZ_CRASH("NYI");
 }
 
+CodeOffset
+MacroAssembler::nopPatchableToNearJump()
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::patchNearJumpToNop(uint8_t* jump)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 void
 MacroAssembler::popReturnAddress()
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1230,16 +1230,37 @@ MacroAssembler::patchThunk(uint32_t u32O
 
 void
 MacroAssembler::repatchThunk(uint8_t* code, uint32_t u32Offset, uint32_t targetOffset)
 {
     uint32_t* u32 = reinterpret_cast<uint32_t*>(code + u32Offset);
     *u32 = targetOffset - u32Offset;
 }
 
+CodeOffset
+MacroAssembler::nopPatchableToNearJump()
+{
+    CodeOffset offset(currentOffset());
+    masm.nop();
+    masm.nop();
+    return offset;
+}
+
+void
+MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target)
+{
+    ((InstImm*)jump)->setBOffImm16(BOffImm16(target - jump));
+}
+
+void
+MacroAssembler::patchNearJumpToNop(uint8_t* jump)
+{
+    ((InstImm*)jump)->makeNop();
+}
+
 void
 MacroAssembler::call(wasm::SymbolicAddress target)
 {
     movePtr(target, CallReg);
     call(CallReg);
 }
 
 void
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -906,17 +906,16 @@ class AssemblerX86Shared : public Assemb
             // Thread the jump list through the unpatched jump targets.
             label->use(j.offset());
         }
         return j;
     }
 
   public:
     void nop() { masm.nop(); }
-    void twoByteNop() { masm.twoByteNop(); }
     void j(Condition cond, Label* label) { jSrc(cond, label); }
     void jmp(Label* label) { jmpSrc(label); }
     void j(Condition cond, RepatchLabel* label) { jSrc(cond, label); }
     void jmp(RepatchLabel* label) { jmpSrc(label); }
 
     void j(Condition cond, wasm::JumpTarget target) {
         Label l;
         j(cond, &l);
@@ -1065,16 +1064,26 @@ class AssemblerX86Shared : public Assemb
         unsigned char* code = masm.data();
         X86Encoding::AutoUnprotectAssemblerBufferRegion unprotect(masm, thunkOffset - 4, 4);
         X86Encoding::SetRel32(code + thunkOffset, code + targetOffset);
     }
     static void repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset) {
         X86Encoding::SetRel32(code + thunkOffset, code + targetOffset);
     }
 
+    CodeOffset twoByteNop() {
+        return CodeOffset(masm.twoByteNop().offset());
+    }
+    static void patchTwoByteNopToJump(uint8_t* jump, uint8_t* target) {
+        X86Encoding::BaseAssembler::patchTwoByteNopToJump(jump, target);
+    }
+    static void patchJumpToTwoByteNop(uint8_t* jump) {
+        X86Encoding::BaseAssembler::patchJumpToTwoByteNop(jump);
+    }
+
     void breakpoint() {
         masm.int3();
     }
 
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool HasPOPCNT() { return CPUInfo::IsPOPCNTPresent(); }
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -70,21 +70,44 @@ public:
     bool oom() const { return m_formatter.oom(); }
 
     void nop()
     {
         spew("nop");
         m_formatter.oneByteOp(OP_NOP);
     }
 
-    void twoByteNop()
+    MOZ_MUST_USE JmpSrc
+    twoByteNop()
     {
         spew("nop (2 byte)");
+        JmpSrc r(m_formatter.size());
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_NOP);
+        return r;
+    }
+
+    static void patchTwoByteNopToJump(uint8_t* jump, uint8_t* target)
+    {
+        // Note: the offset is relative to the address of the instruction after
+        // the jump which is two bytes.
+        ptrdiff_t rel8 = target - jump - 2;
+        MOZ_RELEASE_ASSERT(rel8 >= INT8_MIN && rel8 <= INT8_MAX);
+        MOZ_RELEASE_ASSERT(jump[0] == PRE_OPERAND_SIZE);
+        MOZ_RELEASE_ASSERT(jump[1] == OP_NOP);
+        jump[0] = OP_JMP_rel8;
+        jump[1] = rel8;
+    }
+
+    static void patchJumpToTwoByteNop(uint8_t* jump)
+    {
+        // See twoByteNop.
+        MOZ_RELEASE_ASSERT(jump[0] == OP_JMP_rel8);
+        jump[0] = PRE_OPERAND_SIZE;
+        jump[1] = OP_NOP;
     }
 
     /*
      * The nop multibytes sequences are directly taken from the Intel's
      * architecture software developer manual.
      * They are defined for sequences of sizes from 1 to 9 included.
      */
     void nop_one()
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -565,16 +565,34 @@ MacroAssembler::patchThunk(uint32_t thun
 }
 
 void
 MacroAssembler::repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset)
 {
     Assembler::repatchThunk(code, thunkOffset, targetOffset);
 }
 
+CodeOffset
+MacroAssembler::nopPatchableToNearJump()
+{
+    return Assembler::twoByteNop();
+}
+
+void
+MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target)
+{
+    Assembler::patchTwoByteNopToJump(jump, target);
+}
+
+void
+MacroAssembler::patchNearJumpToNop(uint8_t* jump)
+{
+    Assembler::patchJumpToTwoByteNop(jump);
+}
+
 void
 MacroAssembler::callAndPushReturnAddress(Register reg)
 {
     call(reg);
 }
 
 void
 MacroAssembler::callAndPushReturnAddress(Label* label)