diff --git a/js/src/methodjit/BaseCompiler.h b/js/src/methodjit/BaseCompiler.h index f8ee87130df0..cf9efa9349cd 100644 --- a/js/src/methodjit/BaseCompiler.h +++ b/js/src/methodjit/BaseCompiler.h @@ -100,6 +100,20 @@ class BaseCompiler : public MacroAssemblerTypedefs { } }; +#ifdef JS_CPU_X64 +inline bool +VerifyRange(void *start1, size_t size1, void *start2, size_t size2) +{ + uintptr_t end1 = uintptr_t(start1) + size1; + uintptr_t end2 = uintptr_t(start2) + size2; + + uintptr_t lowest = JS_MIN(uintptr_t(start1), uintptr_t(start2)); + uintptr_t highest = JS_MAX(end1, end2); + + return (highest - lowest < INT_MAX); +} +#endif + // This class wraps JSC::LinkBuffer for Mozilla-specific memory handling. // Every return |false| guarantees an OOM that has been correctly propagated, // and should continue to propagate. @@ -128,13 +142,7 @@ class LinkerHelper : public JSC::LinkBuffer verifiedRange = true; #endif #ifdef JS_CPU_X64 - uintptr_t lowest = JS_MIN(uintptr_t(m_code), uintptr_t(other.start())); - - uintptr_t myEnd = uintptr_t(m_code) + m_size; - uintptr_t otherEnd = uintptr_t(other.start()) + other.size(); - uintptr_t highest = JS_MAX(myEnd, otherEnd); - - return (highest - lowest < INT_MAX); + return VerifyRange(m_code, m_size, other.start(), other.size()); #else return true; #endif diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 139943112df8..7565e7ea0c62 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -666,19 +666,12 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct) Vector chunks(cx); Vector edges(cx); - /* - * Chunk compilation is not supported on x64, since there is no guarantee - * that cross chunk jumps will be patchable even to go to the default shim. - */ -#ifndef JS_CPU_X64 if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) { -#endif ChunkDescriptor desc; desc.begin = 0; desc.end = script->length; if (!chunks.append(desc)) return NULL; -#ifndef JS_CPU_X64 } else { if (!script->ensureRanInference(cx)) return NULL; @@ -871,7 +864,6 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct) return NULL; } } -#endif /* !JS_CPU_X64 */ size_t dataSize = sizeof(JITScript) + (chunks.length() * sizeof(ChunkDescriptor)) @@ -1259,6 +1251,15 @@ mjit::Compiler::generateEpilogue() CompileStatus mjit::Compiler::finishThisUp() { +#ifdef JS_CPU_X64 + /* Generate trampolines to ensure that cross chunk edges are patchable. */ + for (unsigned i = 0; i < chunkEdges.length(); i++) { + chunkEdges[i].sourceTrampoline = stubcc.masm.label(); + stubcc.masm.move(ImmPtr(NULL), Registers::ScratchReg); + stubcc.masm.jump(Registers::ScratchReg); + } +#endif + RETURN_IF_OOM(Compile_Error); /* @@ -1789,8 +1790,6 @@ mjit::Compiler::finishThisUp() outerChunk.chunk = chunk; - Repatcher repatch(chunk); - /* Patch all incoming and outgoing cross-chunk jumps. */ CrossChunkEdge *crossEdges = jit->edges(); for (unsigned i = 0; i < jit->nedges; i++) { @@ -1837,12 +1836,15 @@ mjit::Compiler::finishThisUp() * jump is never taken. */ edge.sourceJump1 = fullCode.locationOf(oedge.fastJump).executableAddress(); - repatch.relink(CodeLocationJump(edge.sourceJump1), targetLabel); if (oedge.slowJump.isSet()) { edge.sourceJump2 = stubCode.locationOf(oedge.slowJump.get()).executableAddress(); - repatch.relink(CodeLocationJump(edge.sourceJump2), targetLabel); } +#ifdef JS_CPU_X64 + edge.sourceTrampoline = + stubCode.locationOf(oedge.sourceTrampoline).executableAddress(); +#endif + jit->patchEdge(edge, label); break; } } diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 2708b1520475..951caf39328b 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -380,6 +380,10 @@ class Compiler : public BaseCompiler uint32_t source; uint32_t target; +#ifdef JS_CPU_X64 + Label sourceTrampoline; +#endif + Jump fastJump; MaybeJump slowJump; }; diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 1f856064f665..650a31d6daae 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -1230,13 +1230,34 @@ JITScript::patchEdge(const CrossChunkEdge &edge, void *label) { if (edge.sourceJump1 || edge.sourceJump2) { JITChunk *sourceChunk = chunk(script->code + edge.source); - JSC::CodeLocationLabel targetLabel(label); ic::Repatcher repatch(sourceChunk); +#ifdef JS_CPU_X64 + JS_ASSERT(edge.sourceTrampoline); + + static const uint32_t JUMP_LENGTH = 10; + + if (edge.sourceJump1) { + JSC::CodeLocationLabel targetLabel(VerifyRange(edge.sourceJump1, JUMP_LENGTH, label, 0) + ? label + : edge.sourceTrampoline); + repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel); + } + if (edge.sourceJump2) { + JSC::CodeLocationLabel targetLabel(VerifyRange(edge.sourceJump2, JUMP_LENGTH, label, 0) + ? label + : edge.sourceTrampoline); + repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel); + } + JSC::CodeLocationDataLabelPtr sourcePatch((char*)edge.sourceTrampoline + JUMP_LENGTH); + repatch.repatch(sourcePatch, label); +#else + JSC::CodeLocationLabel targetLabel(label); if (edge.sourceJump1) repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel); if (edge.sourceJump2) repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel); +#endif } if (edge.jumpTableEntries) { for (unsigned i = 0; i < edge.jumpTableEntries->length(); i++) @@ -1317,6 +1338,9 @@ JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses) CrossChunkEdge &edge = edges[i]; if (edge.source >= desc.begin && edge.source < desc.end) { edge.sourceJump1 = edge.sourceJump2 = NULL; +#ifdef JS_CPU_X64 + edge.sourceTrampoline = NULL; +#endif if (edge.jumpTableEntries) { cx->delete_(edge.jumpTableEntries); edge.jumpTableEntries = NULL; diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index bdb7c8904446..26d942e9e067 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -761,6 +761,14 @@ struct CrossChunkEdge void *sourceJump1; void *sourceJump2; +#ifdef JS_CPU_X64 + /* + * Location of a trampoline for the edge to perform an indirect jump if + * out of range, NULL if the source is not compiled. + */ + void *sourceTrampoline; +#endif + /* Any jump table entries along this edge. */ typedef Vector JumpTableEntryVector; JumpTableEntryVector *jumpTableEntries;