Enable chunked compilation on x64, bug 728372. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 18 Feb 2012 08:52:04 -0800
changeset 87165 3b8ad7252ccbf8b718b6127cff9df0aba18c1c8f
parent 87164 161d2a62a56345c96aea714e27791197d86d8b6f
child 87166 44bdafff8b4c091016b9ebccf7a9524d0deaf9a0
push id22091
push userbmo@edmorley.co.uk
push dateMon, 20 Feb 2012 12:09:20 +0000
treeherdermozilla-central@b8e7474374d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs728372
milestone13.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
Enable chunked compilation on x64, bug 728372. r=dvander
js/src/methodjit/BaseCompiler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -95,16 +95,30 @@ class BaseCompiler : public MacroAssembl
   public:
     BaseCompiler() : cx(NULL)
     { }
 
     BaseCompiler(JSContext *cx) : cx(cx)
     { }
 };
 
+#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.
 class LinkerHelper : public JSC::LinkBuffer
 {
   protected:
     Assembler &masm;
 #ifdef DEBUG
@@ -123,23 +137,17 @@ class LinkerHelper : public JSC::LinkBuf
         JS_ASSERT(verifiedRange);
     }
 
     bool verifyRange(const JSC::JITCode &other) {
 #ifdef DEBUG
         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
     }
 
     bool verifyRange(JITChunk *chunk) {
         return verifyRange(JSC::JITCode(chunk->code.m_code.executableAddress(),
                                         chunk->code.m_size));
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -661,29 +661,22 @@ MakeJITScript(JSContext *cx, JSScript *s
 
     ScriptAnalysis *analysis = script->analysis();
 
     JITScript *&location = construct ? script->jitCtor : script->jitNormal;
 
     Vector<ChunkDescriptor> chunks(cx);
     Vector<CrossChunkEdge> 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;
 
         /* Outgoing edges within the current chunk. */
         Vector<CrossChunkEdge> currentEdges(cx);
         uint32_t chunkStart = 0;
 
@@ -866,17 +859,16 @@ MakeJITScript(JSContext *cx, JSScript *s
         if (chunkStart != script->length) {
             ChunkDescriptor desc;
             desc.begin = chunkStart;
             desc.end = script->length;
             if (!chunks.append(desc))
                 return NULL;
         }
     }
-#endif /* !JS_CPU_X64 */
 
     size_t dataSize = sizeof(JITScript)
         + (chunks.length() * sizeof(ChunkDescriptor))
         + (edges.length() * sizeof(CrossChunkEdge));
     uint8_t *cursor = (uint8_t *) OffTheBooks::calloc_(dataSize);
     if (!cursor)
         return NULL;
 
@@ -1254,16 +1246,25 @@ CompileStatus
 mjit::Compiler::generateEpilogue()
 {
     return Compile_Okay;
 }
 
 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);
 
     /*
      * Watch for reallocation of the global slots while we were in the middle
      * of compiling due to, e.g. standard class initialization.
      */
     if (globalSlots && globalObj->getRawSlots() != globalSlots)
         return Compile_Retry;
@@ -1784,18 +1785,16 @@ mjit::Compiler::finishThisUp()
     Probes::registerMJITCode(cx, jit,
                              a,
                              (JSActiveFrame**) inlineFrames.begin(),
                              result, masm.size(),
                              result + masm.size(), stubcc.size());
 
     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++) {
         CrossChunkEdge &edge = crossEdges[i];
         if (bytecodeInChunk(outerScript->code + edge.source)) {
             JS_ASSERT(!edge.sourceJump1 && !edge.sourceJump2);
             void *label = edge.targetLabel ? edge.targetLabel : edge.shimLabel;
             CodeLocationLabel targetLabel(label);
@@ -1832,22 +1831,25 @@ mjit::Compiler::finishThisUp()
                     /*
                      * Only a single edge needs to be patched; we ensured while
                      * generating chunks that no two cross chunk edges can have
                      * the same source and target. Note that there may not be
                      * an edge to patch, if constant folding determined the
                      * 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;
                 }
             }
         } else if (bytecodeInChunk(outerScript->code + edge.target)) {
             JS_ASSERT(!edge.targetLabel);
             JS_ASSERT(jumpMap[edge.target].isSet());
             edge.targetLabel = fullCode.locationOf(jumpMap[edge.target]).executableAddress();
             jit->patchEdge(edge, edge.targetLabel);
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -375,16 +375,20 @@ class Compiler : public BaseCompiler
             return type;
         }
     };
 
     struct OutgoingChunkEdge {
         uint32_t source;
         uint32_t target;
 
+#ifdef JS_CPU_X64
+        Label sourceTrampoline;
+#endif
+
         Jump fastJump;
         MaybeJump slowJump;
     };
 
     struct SlotType
     {
         uint32_t slot;
         VarType vt;
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1225,23 +1225,44 @@ JITChunk::polyICSectionsLimit() const
 }
 #endif  // JS_POLYIC
 
 void
 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++)
             *(*edge.jumpTableEntries)[i] = label;
     }
 }
 
 template <typename T>
@@ -1312,16 +1333,19 @@ JITScript::destroyChunk(JSContext *cx, u
         cx->delete_(desc.chunk);
         desc.chunk = NULL;
 
         CrossChunkEdge *edges = this->edges();
         for (unsigned i = 0; i < nedges; i++) {
             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;
                 }
             } else if (edge.target >= desc.begin && edge.target < desc.end) {
                 edge.targetLabel = NULL;
                 patchEdge(edge, edge.shimLabel);
             }
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -756,16 +756,24 @@ struct CrossChunkEdge
     /* Bytecode offsets of the source and target of the edge. */
     uint32_t source;
     uint32_t target;
 
     /* Locations of the jump(s) for the source, NULL if not compiled. */
     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<void**,4,SystemAllocPolicy> JumpTableEntryVector;
     JumpTableEntryVector *jumpTableEntries;
 
     /* Location of the label for the target, NULL if not compiled. */
     void *targetLabel;
 
     /*