Bug 973725 - OdinMonkey: support linking a single asm.js module more than once (r=benj)
authorLuke Wagner <luke@mozilla.com>
Tue, 18 Feb 2014 11:06:38 -0600
changeset 169681 2a34429afff1b8192378a5427e2d118d44d00622
parent 169680 22693bfef4d86c7989baaa90950c8d7ca49bbdd5
child 169682 e3a09f5cec6b11fdaa4632ce05313d7746ed5896
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbenj
bugs973725
milestone30.0a1
Bug 973725 - OdinMonkey: support linking a single asm.js module more than once (r=benj)
js/src/jit-test/lib/bullet.js
js/src/jit-test/tests/asm.js/testCloning.js
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSLink.cpp
js/src/jit/AsmJSModule.cpp
js/src/jit/AsmJSModule.h
--- a/js/src/jit-test/lib/bullet.js
+++ b/js/src/jit-test/lib/bullet.js
@@ -54438,17 +54438,22 @@ function b35() {
   ,b39,__ZNK10__cxxabiv120__si_class_type_info27has_unambiguous_public_baseEPNS_19__dynamic_cast_infoEPvi,b39,__ZNK10__cxxabiv121__vmi_class_type_info27has_unambiguous_public_baseEPNS_19__dynamic_cast_infoEPvi,b39,__ZN28btTriangleConvexcastCallback15processTriangleEP9btVector3ii,b39,__ZNK23btPolyhedralConvexShape49batchedUnitVectorGetSupportingVertexWithoutMarginEPK9btVector3PS0_i,b39,__ZN18btConstraintSolver9allSolvedERK19btContactSolverInfoP12btIDebugDrawP12btStackAlloc
   ,b39,__ZN24btConvexTriangleCallback15processTriangleEP9btVector3ii,b39,__ZNK13btSphereShape49batchedUnitVectorGetSupportingVertexWithoutMarginEPK9btVector3PS0_i,b39,__ZZN33btConvexConcaveCollisionAlgorithm21calculateTimeOfImpactEP17btCollisionObjectS1_RK16btDispatcherInfoP16btManifoldResultEN31LocalTriangleSphereCastCallback15processTriangleEP9btVector3ii,b39,__ZNK21btConvexInternalShape11getAabbSlowERK11btTransformR9btVector3S4_,b39,__ZN25btTriangleRaycastCallback15processTriangleEP9btVector3ii
   ,b39,__ZNK10__cxxabiv117__class_type_info27has_unambiguous_public_baseEPNS_19__dynamic_cast_infoEPvi,b39,__ZN21btCollisionDispatcher25dispatchAllCollisionPairsEP22btOverlappingPairCacheRK16btDispatcherInfoP12btDispatcher,b39,__ZNK10btBoxShape49batchedUnitVectorGetSupportingVertexWithoutMarginEPK9btVector3PS0_i,b39,__ZNK10btBoxShape7getAabbERK11btTransformR9btVector3S4_,b39,__ZNK15btTriangleShape49batchedUnitVectorGetSupportingVertexWithoutMarginEPK9btVector3PS0_i
   ,b39,__ZN15btGjkConvexCastC2EPK13btConvexShapeS2_P22btVoronoiSimplexSolver,b39,__ZNK16btDbvtBroadphase7getAabbEP17btBroadphaseProxyR9btVector3S3_,b39,__ZN23btDiscreteDynamicsWorld12addRigidBodyEP11btRigidBodyss,b39,__ZNK21btConvexInternalShape7getAabbERK11btTransformR9btVector3S4_,b39,__ZNK16btCollisionWorld7rayTestERK9btVector3S2_RNS_17RayResultCallbackE
   ,b39,__ZNK10btBoxShape8getPlaneER9btVector3S1_i,b39,__ZNK15btTriangleShape16getPlaneEquationEiR9btVector3S1_,b39,__ZNK15btTriangleShape7getAabbERK11btTransformR9btVector3S4_,b39,__ZN17DebugDrawcallback15processTriangleEP9btVector3ii,b39,__ZN23btDiscreteDynamicsWorld18addCollisionObjectEP17btCollisionObjectss,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39,b39];
   return { _strlen: _strlen, _free: _free, _main: _main, __GLOBAL__I_a: __GLOBAL__I_a, _memset: _memset, _malloc: _malloc, _memcpy: _memcpy, _llvm_uadd_with_overflow_i64: _llvm_uadd_with_overflow_i64, runPostSets: runPostSets, stackAlloc: stackAlloc, stackSave: stackSave, stackRestore: stackRestore, setThrew: setThrew, setTempRet0: setTempRet0, setTempRet1: setTempRet1, setTempRet2: setTempRet2, setTempRet3: setTempRet3, setTempRet4: setTempRet4, setTempRet5: setTempRet5, setTempRet6: setTempRet6, setTempRet7: setTempRet7, setTempRet8: setTempRet8, setTempRet9: setTempRet9, dynCall_iiiiiiii: dynCall_iiiiiiii, dynCall_vif: dynCall_vif, dynCall_viifii: dynCall_viifii, dynCall_viiiii: dynCall_viiiii, dynCall_vi: dynCall_vi, dynCall_vii: dynCall_vii, dynCall_viiifii: dynCall_viiifii, dynCall_vifiii: dynCall_vifiii, dynCall_ii: dynCall_ii, dynCall_viiiiffffiif: dynCall_viiiiffffiif, dynCall_fiii: dynCall_fiii, dynCall_viiif: dynCall_viiif, dynCall_fiiiiiiiiiii: dynCall_fiiiiiiiiiii, dynCall_fiifii: dynCall_fiifii, dynCall_iii: dynCall_iii, dynCall_iiii: dynCall_iiii, dynCall_fif: dynCall_fif, dynCall_viiiiiiii: dynCall_viiiiiiii, dynCall_vifi: dynCall_vifi, dynCall_viiiiii: dynCall_viiiiii, dynCall_iiiiiiiiii: dynCall_iiiiiiiiii, dynCall_viffiii: dynCall_viffiii, dynCall_iiiiiii: dynCall_iiiiiii, dynCall_fiiiiiiiiii: dynCall_fiiiiiiiiii, dynCall_fiiiii: dynCall_fiiiii, dynCall_iiiiiiiiiiii: dynCall_iiiiiiiiiiii, dynCall_vifii: dynCall_vifii, dynCall_fi: dynCall_fi, dynCall_viiiiiiiiii: dynCall_viiiiiiiiii, dynCall_viiiifffffif: dynCall_viiiifffffif, dynCall_viiiiiffii: dynCall_viiiiiffii, dynCall_iifif: dynCall_iifif, dynCall_iiiii: dynCall_iiiii, dynCall_viii: dynCall_viii, dynCall_viifi: dynCall_viifi, dynCall_v: dynCall_v, dynCall_viif: dynCall_viif, dynCall_iiif: dynCall_iiif, dynCall_fiiifii: dynCall_fiiifii, dynCall_viiii: dynCall_viiii };
 };
 
-var asm = asmModule({ "Math": Math, "Int8Array": Int8Array, "Int16Array": Int16Array, "Int32Array": Int32Array, "Uint8Array": Uint8Array, "Uint16Array": Uint16Array, "Uint32Array": Uint32Array, "Float32Array": Float32Array, "Float64Array": Float64Array }, { "abort": abort, "assert": assert, "asmPrintInt": asmPrintInt, "asmPrintFloat": asmPrintFloat, "min": Math_min, "invoke_iiiiiiii": invoke_iiiiiiii, "invoke_vif": invoke_vif, "invoke_viifii": invoke_viifii, "invoke_viiiii": invoke_viiiii, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viiifii": invoke_viiifii, "invoke_vifiii": invoke_vifiii, "invoke_ii": invoke_ii, "invoke_viiiiffffiif": invoke_viiiiffffiif, "invoke_fiii": invoke_fiii, "invoke_viiif": invoke_viiif, "invoke_fiiiiiiiiiii": invoke_fiiiiiiiiiii, "invoke_fiifii": invoke_fiifii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_fif": invoke_fif, "invoke_viiiiiiii": invoke_viiiiiiii, "invoke_vifi": invoke_vifi, "invoke_viiiiii": invoke_viiiiii, "invoke_iiiiiiiiii": invoke_iiiiiiiiii, "invoke_viffiii": invoke_viffiii, "invoke_iiiiiii": invoke_iiiiiii, "invoke_fiiiiiiiiii": invoke_fiiiiiiiiii, "invoke_fiiiii": invoke_fiiiii, "invoke_iiiiiiiiiiii": invoke_iiiiiiiiiiii, "invoke_vifii": invoke_vifii, "invoke_fi": invoke_fi, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viiiifffffif": invoke_viiiifffffif, "invoke_viiiiiffii": invoke_viiiiiffii, "invoke_iifif": invoke_iifif, "invoke_iiiii": invoke_iiiii, "invoke_viii": invoke_viii, "invoke_viifi": invoke_viifi, "invoke_v": invoke_v, "invoke_viif": invoke_viif, "invoke_iiif": invoke_iiif, "invoke_fiiifii": invoke_fiiifii, "invoke_viiii": invoke_viiii, "_llvm_lifetime_end": _llvm_lifetime_end, "_cosf": _cosf, "_fabsf": _fabsf, "_sysconf": _sysconf, "___cxa_throw": ___cxa_throw, "_atexit": _atexit, "_abort": _abort, "_fprintf": _fprintf, "_llvm_eh_exception": _llvm_eh_exception, "_printf": _printf, "_acosf": _acosf, "_fflush": _fflush, "__reallyNegative": __reallyNegative, "_sqrtf": _sqrtf, "_llvm_pow_f32": _llvm_pow_f32, "___setErrNo": ___setErrNo, "_fwrite": _fwrite, "_send": _send, "_write": _write, "_exit": _exit, "_atan2f": _atan2f, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_is_number_type": ___cxa_is_number_type, "_time": _time, "__formatString": __formatString, "___cxa_does_inherit": ___cxa_does_inherit, "___cxa_guard_acquire": ___cxa_guard_acquire, "__ZSt9terminatev": __ZSt9terminatev, "_gettimeofday": _gettimeofday, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "_sinf": _sinf, "___assert_func": ___assert_func, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "_pwrite": _pwrite, "___cxa_call_unexpected": ___cxa_call_unexpected, "_sbrk": _sbrk, "___cxa_guard_abort": ___cxa_guard_abort, "___cxa_allocate_exception": ___cxa_allocate_exception, "___errno_location": ___errno_location, "___gxx_personality_v0": ___gxx_personality_v0, "_llvm_lifetime_start": _llvm_lifetime_start, "_fmod": _fmod, "___cxa_guard_release": ___cxa_guard_release, "__exit": __exit, "___resumeException": ___resumeException, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "cttz_i8": cttz_i8, "ctlz_i8": ctlz_i8, "NaN": NaN, "Infinity": Infinity, "__ZTVN10__cxxabiv117__class_type_infoE": __ZTVN10__cxxabiv117__class_type_infoE, "__ZTVN10__cxxabiv120__si_class_type_infoE": __ZTVN10__cxxabiv120__si_class_type_infoE, "___dso_handle": ___dso_handle }, buffer);
+var ffis = { "abort": abort, "assert": assert, "asmPrintInt": asmPrintInt, "asmPrintFloat": asmPrintFloat, "min": Math_min, "invoke_iiiiiiii": invoke_iiiiiiii, "invoke_vif": invoke_vif, "invoke_viifii": invoke_viifii, "invoke_viiiii": invoke_viiiii, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viiifii": invoke_viiifii, "invoke_vifiii": invoke_vifiii, "invoke_ii": invoke_ii, "invoke_viiiiffffiif": invoke_viiiiffffiif, "invoke_fiii": invoke_fiii, "invoke_viiif": invoke_viiif, "invoke_fiiiiiiiiiii": invoke_fiiiiiiiiiii, "invoke_fiifii": invoke_fiifii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_fif": invoke_fif, "invoke_viiiiiiii": invoke_viiiiiiii, "invoke_vifi": invoke_vifi, "invoke_viiiiii": invoke_viiiiii, "invoke_iiiiiiiiii": invoke_iiiiiiiiii, "invoke_viffiii": invoke_viffiii, "invoke_iiiiiii": invoke_iiiiiii, "invoke_fiiiiiiiiii": invoke_fiiiiiiiiii, "invoke_fiiiii": invoke_fiiiii, "invoke_iiiiiiiiiiii": invoke_iiiiiiiiiiii, "invoke_vifii": invoke_vifii, "invoke_fi": invoke_fi, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viiiifffffif": invoke_viiiifffffif, "invoke_viiiiiffii": invoke_viiiiiffii, "invoke_iifif": invoke_iifif, "invoke_iiiii": invoke_iiiii, "invoke_viii": invoke_viii, "invoke_viifi": invoke_viifi, "invoke_v": invoke_v, "invoke_viif": invoke_viif, "invoke_iiif": invoke_iiif, "invoke_fiiifii": invoke_fiiifii, "invoke_viiii": invoke_viiii, "_llvm_lifetime_end": _llvm_lifetime_end, "_cosf": _cosf, "_fabsf": _fabsf, "_sysconf": _sysconf, "___cxa_throw": ___cxa_throw, "_atexit": _atexit, "_abort": _abort, "_fprintf": _fprintf, "_llvm_eh_exception": _llvm_eh_exception, "_printf": _printf, "_acosf": _acosf, "_fflush": _fflush, "__reallyNegative": __reallyNegative, "_sqrtf": _sqrtf, "_llvm_pow_f32": _llvm_pow_f32, "___setErrNo": ___setErrNo, "_fwrite": _fwrite, "_send": _send, "_write": _write, "_exit": _exit, "_atan2f": _atan2f, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_is_number_type": ___cxa_is_number_type, "_time": _time, "__formatString": __formatString, "___cxa_does_inherit": ___cxa_does_inherit, "___cxa_guard_acquire": ___cxa_guard_acquire, "__ZSt9terminatev": __ZSt9terminatev, "_gettimeofday": _gettimeofday, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "_sinf": _sinf, "___assert_func": ___assert_func, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "_pwrite": _pwrite, "___cxa_call_unexpected": ___cxa_call_unexpected, "_sbrk": _sbrk, "___cxa_guard_abort": ___cxa_guard_abort, "___cxa_allocate_exception": ___cxa_allocate_exception, "___errno_location": ___errno_location, "___gxx_personality_v0": ___gxx_personality_v0, "_llvm_lifetime_start": _llvm_lifetime_start, "_fmod": _fmod, "___cxa_guard_release": ___cxa_guard_release, "__exit": __exit, "___resumeException": ___resumeException, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "cttz_i8": cttz_i8, "ctlz_i8": ctlz_i8, "NaN": NaN, "Infinity": Infinity, "__ZTVN10__cxxabiv117__class_type_infoE": __ZTVN10__cxxabiv117__class_type_infoE, "__ZTVN10__cxxabiv120__si_class_type_infoE": __ZTVN10__cxxabiv120__si_class_type_infoE, "___dso_handle": ___dso_handle };
+
+// Stress-test re-linking by linking once before the "real" one.
+var throwAway = asmModule(this, ffis, new ArrayBuffer(4*4096));
+
+var asm = asmModule(this, ffis, buffer);
 var _strlen = Module["_strlen"] = asm["_strlen"];
 var _free = Module["_free"] = asm["_free"];
 var _main = Module["_main"] = asm["_main"];
 var __GLOBAL__I_a = Module["__GLOBAL__I_a"] = asm["__GLOBAL__I_a"];
 var _memset = Module["_memset"] = asm["_memset"];
 var _malloc = Module["_malloc"] = asm["_malloc"];
 var _memcpy = Module["_memcpy"] = asm["_memcpy"];
 var _llvm_uadd_with_overflow_i64 = Module["_llvm_uadd_with_overflow_i64"] = asm["_llvm_uadd_with_overflow_i64"];
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testCloning.js
@@ -0,0 +1,37 @@
+load(libdir + "asm.js");
+
+var code = asmCompile(USE_ASM + "function g() { return 42 } return g");
+assertEq(asmLink(code)(), 42);
+assertEq(asmLink(code)(), 42);
+
+var code = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var i32=new glob.Int32Array(buf); function g() { return i32[0]|0 } return g');
+var i32_1 = new Int32Array(4096);
+i32_1[0] = 42;
+var i32_2 = new Int32Array(4096);
+i32_2[0] = 13;
+assertEq(asmLink(code, this, null, i32_1.buffer)(), 42);
+assertEq(asmLink(code, this, null, i32_2.buffer)(), 13);
+var i32_3 = new Int32Array(4097);
+assertAsmLinkFail(code, this, null, i32_3.buffer);
+
+var code = asmCompile('glob', 'ffis', USE_ASM + 'var ffi=ffis.ffi; function g(n) { n=n|0; var i=0; for(; (i|0)<(n|0); i=(i+1)|0) ffi() } return g');
+var calls1 = 0, calls2 = 0;
+function ffi1() { calls1++ }
+function ffi2() { calls2++ }
+asmLink(code, null, {ffi:ffi1})(100000);
+assertEq(calls1, 100000);
+assertEq(calls2, 0);
+calls1 = 0;
+asmLink(code, null, {ffi:ffi2})(50000);
+assertEq(calls1, 0);
+assertEq(calls2, 50000);
+
+var code = asmCompile(USE_ASM + 'var g = 0; function h() { g=(g+1)|0; return g|0 } return h');
+var h1 = code();
+assertEq(h1(), 1);
+assertEq(h1(), 2);
+var h2 = code();
+assertEq(h2(), 1);
+assertEq(h1(), 3);
+assertEq(h2(), 2);
+assertEq(h1(), 4);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1576,17 +1576,17 @@ class MOZ_STACK_CLASS ModuleCompiler
         }
         out->reset(JS_smprintf("total compilation time %dms; %s%s",
                                msTotal,
                                storedInCache ? "stored in cache" : "not stored in cache",
                                slowFuns ? slowFuns.get() : ""));
 #endif
     }
 
-    bool extractModule(ScopedJSDeletePtr<AsmJSModule> *module, AsmJSStaticLinkData *linkData)
+    bool finish(ScopedJSDeletePtr<AsmJSModule> *module)
     {
         module_->initCharsEnd(parser_.tokenStream.currentToken().pos.end);
 
         masm_.finish();
         if (masm_.oom())
             return false;
 
 #if defined(JS_CODEGEN_ARM)
@@ -1625,66 +1625,60 @@ class MOZ_STACK_CLASS ModuleCompiler
             for (uint32_t i = 0; i < basicBlocks.length(); i++) {
                 Record &r = basicBlocks[i];
                 r.startOffset = masm_.actualOffset(r.startOffset);
                 r.endOffset = masm_.actualOffset(r.endOffset);
             }
         }
 #endif
 
-        // Some link information does not need to be permanently stored in the
-        // AsmJSModule since it is not needed after staticallyLink (which
-        // occurs during compilation and on cache deserialization). This link
-        // information is collected into AsmJSStaticLinkData which can then be
-        // serialized/deserialized alongside the AsmJSModule.
-
-        linkData->operationCallbackExitOffset = masm_.actualOffset(operationCallbackLabel_.offset());
+        module_->setOperationCallbackOffset(masm_.actualOffset(operationCallbackLabel_.offset()));
 
         // CodeLabels produced during codegen
         for (size_t i = 0; i < masm_.numCodeLabels(); i++) {
             CodeLabel src = masm_.codeLabel(i);
             int32_t labelOffset = src.dest()->offset();
             int32_t targetOffset = masm_.actualOffset(src.src()->offset());
             // The patched uses of a label embed a linked list where the
             // to-be-patched immediate is the offset of the next to-be-patched
             // instruction.
             while (labelOffset != LabelBase::INVALID_OFFSET) {
                 size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset);
-                AsmJSStaticLinkData::RelativeLink link;
+                AsmJSModule::RelativeLink link;
                 link.patchAtOffset = patchAtOffset;
                 link.targetOffset = targetOffset;
-                if (!linkData->relativeLinks.append(link))
+                if (!module_->addRelativeLink(link))
                     return false;
                 labelOffset = *(uintptr_t *)(module_->codeBase() + patchAtOffset);
             }
         }
 
         // Function-pointer-table entries
         for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) {
             FuncPtrTable &table = funcPtrTables_[tableIndex];
             unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
             for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
-                AsmJSStaticLinkData::RelativeLink link;
+                AsmJSModule::RelativeLink link;
                 link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
                 link.targetOffset = masm_.actualOffset(table.elem(elemIndex).code()->offset());
-                if (!linkData->relativeLinks.append(link))
+                if (!module_->addRelativeLink(link))
                     return false;
             }
         }
 
 #if defined(JS_CODEGEN_X86)
         // Global data accesses in x86 need to be patched with the absolute
         // address of the global. Globals are allocated sequentially after the
         // code section so we can just use an RelativeLink.
         for (unsigned i = 0; i < globalAccesses_.length(); i++) {
             AsmJSGlobalAccess a = globalAccesses_[i];
-            AsmJSStaticLinkData::RelativeLink link;
+            AsmJSModule::RelativeLink link;
             link.patchAtOffset = masm_.labelOffsetToPatchOffset(a.patchAt.offset());
             link.targetOffset = module_->offsetOfGlobalData() + a.globalDataOffset;
-            if (!linkData->relativeLinks.append(link))
+            if (!module_->addRelativeLink(link))
                 return false;
         }
 #endif
 
 #if defined(JS_CODEGEN_X64)
         // Global data accesses on x64 use rip-relative addressing and thus do
         // not need patching after deserialization.
         uint8_t *code = module_->codeBase();
@@ -1692,20 +1686,20 @@ class MOZ_STACK_CLASS ModuleCompiler
             AsmJSGlobalAccess a = globalAccesses_[i];
             masm_.patchAsmJSGlobalAccess(a.patchAt, code, module_->globalData(), a.globalDataOffset);
         }
 #endif
 
         // Absolute links
         for (size_t i = 0; i < masm_.numAsmJSAbsoluteLinks(); i++) {
             AsmJSAbsoluteLink src = masm_.asmJSAbsoluteLink(i);
-            AsmJSStaticLinkData::AbsoluteLink link;
+            AsmJSModule::AbsoluteLink link;
             link.patchAt = masm_.actualOffset(src.patchAt.offset());
             link.target = src.target;
-            if (!linkData->absoluteLinks.append(link))
+            if (!module_->addAbsoluteLink(link))
                 return false;
         }
 
         *module = module_.forget();
         return true;
     }
 };
 
@@ -6785,29 +6779,28 @@ GenerateStubs(ModuleCompiler &m)
     if (!GenerateThrowExit(m, &throwLabel))
         return false;
 
     return true;
 }
 
 static bool
 FinishModule(ModuleCompiler &m,
-             ScopedJSDeletePtr<AsmJSModule> *module,
-             AsmJSStaticLinkData *linkData)
+             ScopedJSDeletePtr<AsmJSModule> *module)
 {
     LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     TempAllocator alloc(&lifo);
     IonContext ionContext(m.cx(), &alloc);
 
     m.masm().resetForNewCodeGenerator(alloc);
 
     if (!GenerateStubs(m))
         return false;
 
-    return m.extractModule(module, linkData);
+    return m.finish(module);
 }
 
 static bool
 CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
             ScopedJSDeletePtr<AsmJSModule> *moduleOut,
             ScopedJSFreePtr<char> *compilationTimeReport)
 {
     if (!LookupAsmJSModuleInCache(cx, parser, moduleOut, compilationTimeReport))
@@ -6853,22 +6846,21 @@ CheckModule(ExclusiveContext *cx, AsmJSP
     if (!CheckModuleReturn(m))
         return false;
 
     TokenKind tk = PeekToken(m.parser());
     if (tk != TOK_EOF && tk != TOK_RC)
         return m.fail(nullptr, "top-level export (return) must be the last statement");
 
     ScopedJSDeletePtr<AsmJSModule> module;
-    AsmJSStaticLinkData linkData(cx);
-    if (!FinishModule(m, &module, &linkData))
-        return false;
-
-    bool storedInCache = StoreAsmJSModuleInCache(parser, *module, linkData, cx);
-    module->staticallyLink(linkData, cx);
+    if (!FinishModule(m, &module))
+        return false;
+
+    bool storedInCache = StoreAsmJSModuleInCache(parser, *module, cx);
+    module->staticallyLink(cx);
 
     m.buildCompilationTimeReport(storedInCache, compilationTimeReport);
     *moduleOut = module.forget();
     return true;
 }
 
 static bool
 Warn(AsmJSParser &parser, int errorNumber, const char *str)
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -25,17 +25,32 @@
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsNaN;
 
-static const unsigned MODULE_FUN_SLOT = 0;
+static bool
+CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
+{
+    ScopedJSDeletePtr<AsmJSModule> module;
+    if (!moduleObj->module().clone(cx, &module))
+        return false;
+
+    module->staticallyLink(cx);
+
+    AsmJSModuleObject *newModuleObj = AsmJSModuleObject::create(cx, &module);
+    if (!newModuleObj)
+        return false;
+
+    moduleObj.set(newModuleObj);
+    return true;
+}
 
 static bool
 LinkFail(JSContext *cx, const char *str)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
                                  nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
     return false;
 }
@@ -206,22 +221,17 @@ ValidateConstant(JSContext *cx, AsmJSMod
     }
 
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
 {
-    if (module.isLinked())
-        return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than "
-                            "once. This limitation should be removed in a future release. To "
-                            "work around this, compile a second module (e.g., using the "
-                            "Function constructor).");
-    module.setIsLinked();
+    module.setIsDynamicallyLinked();
 
     RootedValue globalVal(cx);
     if (args.length() > 0)
         globalVal = args[0];
 
     RootedValue importVal(cx);
     if (args.length() > 1)
         importVal = args[1];
@@ -620,19 +630,19 @@ SendModuleToAttachedProfiler(JSContext *
         return false;
 #endif
 
     return true;
 }
 
 
 static JSObject *
-CreateExportObject(JSContext *cx, HandleObject moduleObj)
+CreateExportObject(JSContext *cx, Handle<AsmJSModuleObject*> moduleObj)
 {
-    AsmJSModule &module = moduleObj->as<AsmJSModuleObject>().module();
+    AsmJSModule &module = moduleObj->module();
 
     if (module.numExportedFunctions() == 1) {
         const AsmJSModule::ExportedFunction &func = module.exportedFunction(0);
         if (!func.maybeFieldName())
             return NewExportedFunction(cx, func, moduleObj, 0);
     }
 
     gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions());
@@ -652,27 +662,38 @@ CreateExportObject(JSContext *cx, Handle
         RootedValue val(cx, ObjectValue(*fun));
         if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0))
             return nullptr;
     }
 
     return obj;
 }
 
+static const unsigned MODULE_FUN_SLOT = 0;
+
 // Implements the semantics of an asm.js module function that has been successfully validated.
 static bool
 LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
     // function and stores its module in an extended slot.
     RootedFunction fun(cx, &args.callee().as<JSFunction>());
-    RootedObject moduleObj(cx,  &fun->getExtendedSlot(MODULE_FUN_SLOT).toObject());
-    AsmJSModule &module = moduleObj->as<AsmJSModuleObject>().module();
+    Rooted<AsmJSModuleObject*> moduleObj(cx,
+        &fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>());
+
+    // When a module is linked, it is dynamically specialized to the given
+    // arguments (buffer, ffis). Thus, if the module is linked again (it is just
+    // a function so it can be called multiple times), we need to clone a new
+    // module.
+    if (moduleObj->module().isDynamicallyLinked() && !CloneModule(cx, &moduleObj))
+        return false;
+
+    AsmJSModule &module = moduleObj->module();
 
     // Link the module by performing the link-time validation checks in the
     // asm.js spec and then patching the generated module to associate it with
     // the given heap (ArrayBuffer) and a new global data segment (the closure
     // state shared by the inner asm.js functions).
     if (!DynamicallyLinkModule(cx, args, module)) {
         // Linking failed, so reparse the entire asm.js module from scratch to
         // get normal interpreted bytecode which we can simply Invoke. Very slow.
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -27,28 +27,30 @@
 
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 
 using namespace js;
 using namespace jit;
 using namespace frontend;
+using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::Compression::LZ4;
 
 void
 AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
 {
-    JS_ASSERT(linked_);
+    JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
+    JS_ASSERT(dynamicallyLinked_);
     JS_ASSERT(!maybeHeap_);
+
     maybeHeap_ = heap;
     heapDatum() = heap->dataPointer();
 
-    JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
 #if defined(JS_CODEGEN_X86)
     uint8_t *heapOffset = heap->dataPointer();
     void *heapLength = (void*)heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const jit::AsmJSHeapAccess &access = heapAccesses_[i];
         if (access.hasLengthCheck())
             JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
         void *addr = access.patchOffsetAt(code_);
@@ -292,29 +294,58 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
         break;
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind");
     return nullptr;
 }
 
 void
-AsmJSModule::staticallyLink(const AsmJSStaticLinkData &linkData, ExclusiveContext *cx)
+AsmJSModule::restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx)
 {
-    // Process AsmJSStaticLinkData:
+#ifdef DEBUG
+    // Put the absolute links back to -1 so patchDataWithValueCheck assertions
+    // in staticallyLink are valid.
+    for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) {
+        AbsoluteLink link = staticLinkData_.absoluteLinks[i];
+        Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(),
+                                           PatchedImmPtr((void*)-1),
+                                           PatchedImmPtr(AddressOf(link.target, cx)));
+    }
+#endif
 
-    operationCallbackExit_ = code_ + linkData.operationCallbackExitOffset;
+    if (maybePrevBuffer) {
+#if defined(JS_CODEGEN_X86)
+        // Subtract out the base-pointer added by AsmJSModule::initHeap.
+        uint8_t *ptrBase = maybePrevBuffer->dataPointer();
+        for (unsigned i = 0; i < heapAccesses_.length(); i++) {
+            const jit::AsmJSHeapAccess &access = heapAccesses_[i];
+            void *addr = access.patchOffsetAt(code_);
+            uint8_t *ptr = reinterpret_cast<uint8_t*>(JSC::X86Assembler::getPointer(addr));
+            JS_ASSERT(ptr >= ptrBase);
+            JSC::X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
+        }
+#endif
+    }
+}
 
-    for (size_t i = 0; i < linkData.relativeLinks.length(); i++) {
-        AsmJSStaticLinkData::RelativeLink link = linkData.relativeLinks[i];
+void
+AsmJSModule::staticallyLink(ExclusiveContext *cx)
+{
+    // Process staticLinkData_
+
+    operationCallbackExit_ = code_ + staticLinkData_.operationCallbackExitOffset;
+
+    for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) {
+        RelativeLink link = staticLinkData_.relativeLinks[i];
         *(void **)(code_ + link.patchAtOffset) = code_ + link.targetOffset;
     }
 
-    for (size_t i = 0; i < linkData.absoluteLinks.length(); i++) {
-        AsmJSStaticLinkData::AbsoluteLink link = linkData.absoluteLinks[i];
+    for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) {
+        AbsoluteLink link = staticLinkData_.absoluteLinks[i];
         Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(),
                                            PatchedImmPtr(AddressOf(link.target, cx)),
                                            PatchedImmPtr((void*)-1));
     }
 
     // Initialize global data segment
 
     for (size_t i = 0; i < exits_.length(); i++) {
@@ -324,17 +355,17 @@ AsmJSModule::staticallyLink(const AsmJSS
 }
 
 AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t charsBegin)
   : globalArgumentName_(nullptr),
     importArgumentName_(nullptr),
     bufferArgumentName_(nullptr),
     code_(nullptr),
     operationCallbackExit_(nullptr),
-    linked_(false),
+    dynamicallyLinked_(false),
     loadedFromCache_(false),
     charsBegin_(charsBegin),
     scriptSource_(scriptSource)
 {
     mozilla::PodZero(&pod);
     scriptSource_->incref();
     pod.minHeapLength_ = AsmJSAllocationGranularity;
 }
@@ -379,17 +410,18 @@ AsmJSModule::addSizeOfMisc(mozilla::Mall
                         heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
 #if defined(MOZ_VTUNE)
                         profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
 #if defined(JS_ION_PERF)
                         profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
                         perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
-                        functionCounts_.sizeOfExcludingThis(mallocSizeOf);
+                        functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
+                        staticLinkData_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 static void
 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
 {
     fop->delete_(&obj->as<AsmJSModuleObject>().module());
 }
 
@@ -546,16 +578,30 @@ DeserializeVector(ExclusiveContext *cx, 
         return nullptr;
     for (size_t i = 0; i < vec->length(); i++) {
         if (!(cursor = (*vec)[i].deserialize(cx, cursor)))
             return nullptr;
     }
     return cursor;
 }
 
+template <class T>
+bool
+CloneVector(ExclusiveContext *cx, const Vector<T, 0, SystemAllocPolicy> &in,
+            Vector<T, 0, SystemAllocPolicy> *out)
+{
+    if (!out->resize(in.length()))
+        return false;
+    for (size_t i = 0; i < in.length(); i++) {
+        if (!in[i].clone(cx, &(*out)[i]))
+            return false;
+    }
+    return true;
+}
+
 template <class T, class AllocPolicy, class ThisVector>
 size_t
 SerializedPodVectorSize(const mozilla::VectorBase<T, 0, AllocPolicy, ThisVector> &vec)
 {
     return sizeof(uint32_t) +
            vec.length() * sizeof(T);
 }
 
@@ -576,16 +622,27 @@ DeserializePodVector(ExclusiveContext *c
     uint32_t length;
     cursor = ReadScalar<uint32_t>(cursor, &length);
     if (!vec->resize(length))
         return nullptr;
     cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
     return cursor;
 }
 
+template <class T>
+bool
+ClonePodVector(ExclusiveContext *cx, const Vector<T, 0, SystemAllocPolicy> &in,
+               Vector<T, 0, SystemAllocPolicy> *out)
+{
+    if (!out->resize(in.length()))
+        return false;
+    PodCopy(out->begin(), in.begin(), in.length());
+    return true;
+}
+
 uint8_t *
 AsmJSModule::Global::serialize(uint8_t *cursor) const
 {
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     cursor = SerializeName(cursor, name_);
     return cursor;
 }
 
@@ -599,16 +656,23 @@ AsmJSModule::Global::serializedSize() co
 const uint8_t *
 AsmJSModule::Global::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
 {
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
     (cursor = DeserializeName(cx, cursor, &name_));
     return cursor;
 }
 
+bool
+AsmJSModule::Global::clone(ExclusiveContext *cx, Global *out) const
+{
+    *out = *this;
+    return true;
+}
+
 uint8_t *
 AsmJSModule::Exit::serialize(uint8_t *cursor) const
 {
     cursor = WriteBytes(cursor, this, sizeof(*this));
     return cursor;
 }
 
 size_t
@@ -619,16 +683,23 @@ AsmJSModule::Exit::serializedSize() cons
 
 const uint8_t *
 AsmJSModule::Exit::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
 {
     cursor = ReadBytes(cursor, this, sizeof(*this));
     return cursor;
 }
 
+bool
+AsmJSModule::Exit::clone(ExclusiveContext *cx, Exit *out) const
+{
+    *out = *this;
+    return true;
+}
+
 uint8_t *
 AsmJSModule::ExportedFunction::serialize(uint8_t *cursor) const
 {
     cursor = SerializeName(cursor, name_);
     cursor = SerializeName(cursor, maybeFieldName_);
     cursor = SerializePodVector(cursor, argCoercions_);
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
@@ -649,42 +720,98 @@ AsmJSModule::ExportedFunction::deseriali
 {
     (cursor = DeserializeName(cx, cursor, &name_)) &&
     (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
     (cursor = DeserializePodVector(cx, cursor, &argCoercions_)) &&
     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
+bool
+AsmJSModule::ExportedFunction::clone(ExclusiveContext *cx, ExportedFunction *out) const
+{
+    out->name_ = name_;
+    out->maybeFieldName_ = maybeFieldName_;
+
+    if (!ClonePodVector(cx, argCoercions_, &out->argCoercions_))
+        return false;
+
+    out->pod = pod;
+    return true;
+}
+
+size_t
+AsmJSModule::StaticLinkData::serializedSize() const
+{
+    return sizeof(uint32_t) +
+           SerializedPodVectorSize(relativeLinks) +
+           SerializedPodVectorSize(absoluteLinks);
+}
+
+uint8_t *
+AsmJSModule::StaticLinkData::serialize(uint8_t *cursor) const
+{
+    cursor = WriteScalar<uint32_t>(cursor, operationCallbackExitOffset);
+    cursor = SerializePodVector(cursor, relativeLinks);
+    cursor = SerializePodVector(cursor, absoluteLinks);
+    return cursor;
+}
+
+const uint8_t *
+AsmJSModule::StaticLinkData::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
+{
+    (cursor = ReadScalar<uint32_t>(cursor, &operationCallbackExitOffset)) &&
+    (cursor = DeserializePodVector(cx, cursor, &relativeLinks)) &&
+    (cursor = DeserializePodVector(cx, cursor, &absoluteLinks));
+    return cursor;
+}
+
+bool
+AsmJSModule::StaticLinkData::clone(ExclusiveContext *cx, StaticLinkData *out) const
+{
+    out->operationCallbackExitOffset = operationCallbackExitOffset;
+    return ClonePodVector(cx, relativeLinks, &out->relativeLinks) &&
+           ClonePodVector(cx, absoluteLinks, &out->absoluteLinks);
+}
+
+size_t
+AsmJSModule::StaticLinkData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return relativeLinks.sizeOfExcludingThis(mallocSizeOf) +
+           absoluteLinks.sizeOfExcludingThis(mallocSizeOf);
+}
+
 size_t
 AsmJSModule::serializedSize() const
 {
     return sizeof(pod) +
            pod.codeBytes_ +
            SerializedNameSize(globalArgumentName_) +
            SerializedNameSize(importArgumentName_) +
            SerializedNameSize(bufferArgumentName_) +
            SerializedVectorSize(globals_) +
            SerializedVectorSize(exits_) +
            SerializedVectorSize(exports_) +
-           SerializedPodVectorSize(heapAccesses_);
+           SerializedPodVectorSize(heapAccesses_) +
+           staticLinkData_.serializedSize();
 }
 
 uint8_t *
 AsmJSModule::serialize(uint8_t *cursor) const
 {
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     cursor = WriteBytes(cursor, code_, pod.codeBytes_);
     cursor = SerializeName(cursor, globalArgumentName_);
     cursor = SerializeName(cursor, importArgumentName_);
     cursor = SerializeName(cursor, bufferArgumentName_);
     cursor = SerializeVector(cursor, globals_);
     cursor = SerializeVector(cursor, exits_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, heapAccesses_);
+    cursor = staticLinkData_.serialize(cursor);
     return cursor;
 }
 
 const uint8_t *
 AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
 {
     // To avoid GC-during-deserialization corner cases, prevent atoms from
     // being collected.
@@ -694,46 +821,59 @@ AsmJSModule::deserialize(ExclusiveContex
     (code_ = AllocateExecutableMemory(cx, pod.totalBytes_)) &&
     (cursor = ReadBytes(cursor, code_, pod.codeBytes_)) &&
     (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
     (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
     (cursor = DeserializeName(cx, cursor, &bufferArgumentName_)) &&
     (cursor = DeserializeVector(cx, cursor, &globals_)) &&
     (cursor = DeserializeVector(cx, cursor, &exits_)) &&
     (cursor = DeserializeVector(cx, cursor, &exports_)) &&
-    (cursor = DeserializePodVector(cx, cursor, &heapAccesses_));
+    (cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) &&
+    (cursor = staticLinkData_.deserialize(cx, cursor));
 
     loadedFromCache_ = true;
     return cursor;
 }
 
-size_t
-AsmJSStaticLinkData::serializedSize() const
-{
-    return sizeof(uint32_t) +
-           SerializedPodVectorSize(relativeLinks) +
-           SerializedPodVectorSize(absoluteLinks);
-}
-
-uint8_t *
-AsmJSStaticLinkData::serialize(uint8_t *cursor) const
+bool
+AsmJSModule::clone(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
-    cursor = WriteScalar<uint32_t>(cursor, operationCallbackExitOffset);
-    cursor = SerializePodVector(cursor, relativeLinks);
-    cursor = SerializePodVector(cursor, absoluteLinks);
-    return cursor;
-}
+    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, charsBegin_);
+    if (!*moduleOut)
+        return false;
+
+    AsmJSModule &out = **moduleOut;
+
+    // Mirror the order of serialize/deserialize in cloning:
+
+    out.pod = pod;
+
+    out.code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
+    if (!out.code_)
+        return false;
+
+    memcpy(out.code_, code_, pod.codeBytes_);
 
-const uint8_t *
-AsmJSStaticLinkData::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
-{
-    (cursor = ReadScalar<uint32_t>(cursor, &operationCallbackExitOffset)) &&
-    (cursor = DeserializePodVector(cx, cursor, &relativeLinks)) &&
-    (cursor = DeserializePodVector(cx, cursor, &absoluteLinks));
-    return cursor;
+    out.globalArgumentName_ = globalArgumentName_;
+    out.importArgumentName_ = importArgumentName_;
+    out.bufferArgumentName_ = bufferArgumentName_;
+
+    if (!CloneVector(cx, globals_, &out.globals_) ||
+        !CloneVector(cx, exits_, &out.exits_) ||
+        !CloneVector(cx, exports_, &out.exports_) ||
+        !ClonePodVector(cx, heapAccesses_, &out.heapAccesses_) ||
+        !staticLinkData_.clone(cx, &out.staticLinkData_))
+    {
+        return false;
+    }
+
+    out.loadedFromCache_ = loadedFromCache_;
+
+    out.restoreToInitialState(maybeHeap_, cx);
+    return true;
 }
 
 static bool
 GetCPUID(uint32_t *cpuId)
 {
     enum Arch {
         X86 = 0x1,
         X64 = 0x2,
@@ -985,48 +1125,45 @@ struct ScopedCacheEntryOpenedForWrite
         if (memory)
             cx->asmJSCacheOps().closeEntryForWrite(cx->global(), serializedSize, memory, handle);
     }
 };
 
 bool
 js::StoreAsmJSModuleInCache(AsmJSParser &parser,
                             const AsmJSModule &module,
-                            const AsmJSStaticLinkData &linkData,
                             ExclusiveContext *cx)
 {
     MachineId machineId;
     if (!machineId.extractCurrentState(cx))
         return false;
 
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser, module))
         return false;
 
     size_t serializedSize = machineId.serializedSize() +
                             moduleChars.serializedSize() +
-                            module.serializedSize() +
-                            linkData.serializedSize();
+                            module.serializedSize();
 
     JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
     if (!open)
         return false;
 
     const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser);
     const jschar *end = parser.tokenStream.rawBase() + ModuleChars::endOffset(parser);
 
     ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
     if (!open(cx->global(), begin, end, entry.serializedSize, &entry.memory, &entry.handle))
         return false;
 
     uint8_t *cursor = entry.memory;
     cursor = machineId.serialize(cursor);
     cursor = moduleChars.serialize(cursor);
     cursor = module.serialize(cursor);
-    cursor = linkData.serialize(cursor);
 
     JS_ASSERT(cursor == entry.memory + serializedSize);
     return true;
 }
 
 struct ScopedCacheEntryOpenedForRead
 {
     ExclusiveContext *cx;
@@ -1084,27 +1221,22 @@ js::LookupAsmJSModuleInCache(ExclusiveCo
     ScopedJSDeletePtr<AsmJSModule> module(
         cx->new_<AsmJSModule>(parser.ss, parser.offsetOfCurrentAsmJSModule()));
     if (!module)
         return false;
     cursor = module->deserialize(cx, cursor);
     if (!cursor)
         return false;
 
-    AsmJSStaticLinkData linkData(cx);
-    cursor = linkData.deserialize(cx, cursor);
-    if (!cursor)
-        return false;
-
     bool atEnd = cursor == entry.memory + entry.serializedSize;
     MOZ_ASSERT(atEnd, "Corrupt cache file");
     if (!atEnd)
         return true;
 
-    module->staticallyLink(linkData, cx);
+    module->staticallyLink(cx);
 
     parser.tokenStream.advance(module->charsEnd());
 
     int64_t usecAfter = PRMJ_Now();
     int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
     *compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms);
     *moduleOut = module.forget();
     return true;
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -39,53 +39,16 @@ enum AsmJSMathBuiltinFunction
     AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
     AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
     AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
     AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
     AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
     AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max
 };
 
-// Static-link data is used to patch a module either after it has been
-// compiled or deserialized with various absolute addresses (of code or
-// data in the process) or relative addresses (of code or data in the same
-// AsmJSModule). Since AsmJSStaticLinkData can be serialized alongside the
-// AsmJSModule and isn't needed after compilation/deserialization, it
-// doesn't need to be stored in the AsmJSModule.
-struct AsmJSStaticLinkData
-{
-    struct RelativeLink
-    {
-        uint32_t patchAtOffset;
-        uint32_t targetOffset;
-    };
-
-    typedef Vector<RelativeLink> RelativeLinkVector;
-
-    struct AbsoluteLink
-    {
-        jit::CodeOffsetLabel patchAt;
-        jit::AsmJSImmKind target;
-    };
-
-    typedef Vector<AbsoluteLink> AbsoluteLinkVector;
-
-    uint32_t operationCallbackExitOffset;
-    RelativeLinkVector relativeLinks;
-    AbsoluteLinkVector absoluteLinks;
-
-    AsmJSStaticLinkData(ExclusiveContext *cx)
-      : relativeLinks(cx), absoluteLinks(cx)
-    {}
-
-    size_t serializedSize() const;
-    uint8_t *serialize(uint8_t *cursor) const;
-    const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
-};
-
 // An asm.js module represents the collection of functions nested inside a
 // single outer "use asm" function. For example, this asm.js module:
 //   function() { "use asm"; function f() {} function g() {} return f }
 // contains the functions 'f' and 'g'.
 //
 // An asm.js module contains both the jit-code produced by compiling all the
 // functions in the module as well all the data required to perform the
 // link-time validation step in the asm.js spec.
@@ -201,16 +164,17 @@ class AsmJSModule
         double constantValue() const {
             JS_ASSERT(pod.which_ == Constant);
             return pod.u.constant.value_;
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
+        bool clone(ExclusiveContext *cx, Global *out) const;
     };
 
     class Exit
     {
         unsigned ffiIndex_;
         unsigned globalDataOffset_;
         unsigned interpCodeOffset_;
         unsigned ionCodeOffset_;
@@ -236,16 +200,17 @@ class AsmJSModule
         void initIonOffset(unsigned off) {
             JS_ASSERT(!ionCodeOffset_);
             ionCodeOffset_ = off;
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
+        bool clone(ExclusiveContext *cx, Exit *out) const;
     };
     typedef int32_t (*CodePtr)(uint64_t *args, uint8_t *global);
 
     typedef Vector<AsmJSCoercion, 0, SystemAllocPolicy> ArgCoercionVector;
 
     enum ReturnType { Return_Int32, Return_Double, Return_Void };
 
     class ExportedFunction
@@ -307,16 +272,17 @@ class AsmJSModule
         }
         ReturnType returnType() const {
             return pod.returnType_;
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
+        bool clone(ExclusiveContext *cx, ExportedFunction *out) const;
     };
 
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     // Function information to add to the VTune JIT profiler following linking.
     struct ProfiledFunction
     {
         JSAtom *name;
         unsigned startCodeOffset;
@@ -357,16 +323,50 @@ class AsmJSModule
 
         ProfiledBlocksFunction(ProfiledBlocksFunction &&copy)
           : ProfiledFunction(copy.name, copy.startCodeOffset, copy.endCodeOffset),
             endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::Move(copy.blocks))
         { }
     };
 #endif
 
+    struct RelativeLink
+    {
+        uint32_t patchAtOffset;
+        uint32_t targetOffset;
+    };
+
+    typedef Vector<RelativeLink, 0, SystemAllocPolicy> RelativeLinkVector;
+
+    struct AbsoluteLink
+    {
+        jit::CodeOffsetLabel patchAt;
+        jit::AsmJSImmKind target;
+    };
+
+    typedef Vector<AbsoluteLink, 0, SystemAllocPolicy> AbsoluteLinkVector;
+
+    // Static-link data is used to patch a module either after it has been
+    // compiled or deserialized with various absolute addresses (of code or
+    // data in the process) or relative addresses (of code or data in the same
+    // AsmJSModule).
+    struct StaticLinkData
+    {
+        uint32_t operationCallbackExitOffset;
+        RelativeLinkVector relativeLinks;
+        AbsoluteLinkVector absoluteLinks;
+
+        size_t serializedSize() const;
+        uint8_t *serialize(uint8_t *cursor) const;
+        const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
+        bool clone(ExclusiveContext *cx, StaticLinkData *out) const;
+
+        size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+    };
+
   private:
     typedef Vector<ExportedFunction, 0, SystemAllocPolicy> ExportedFunctionVector;
     typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
     typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector;
     typedef Vector<jit::AsmJSHeapAccess, 0, SystemAllocPolicy> HeapAccessVector;
     typedef Vector<jit::IonScriptCounts *, 0, SystemAllocPolicy> FunctionCountsVector;
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     typedef Vector<ProfiledFunction, 0, SystemAllocPolicy> ProfiledFunctionVector;
@@ -401,17 +401,18 @@ class AsmJSModule
         size_t                            codeBytes_;     // function bodies and stubs
         size_t                            totalBytes_;    // function bodies, stubs, and global data
         uint32_t                          minHeapLength_;
     } pod;
 
     uint8_t *                             code_;
     uint8_t *                             operationCallbackExit_;
 
-    bool                                  linked_;
+    StaticLinkData                        staticLinkData_;
+    bool                                  dynamicallyLinked_;
     bool                                  loadedFromCache_;
     HeapPtr<ArrayBufferObject>            maybeHeap_;
 
     uint32_t                              charsBegin_;
     ScriptSource *                        scriptSource_;
 
     FunctionCountsVector                  functionCounts_;
 
@@ -719,45 +720,59 @@ class AsmJSModule
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
     }
     uint32_t minHeapLength() const {
         return pod.minHeapLength_;
     }
 
     bool allocateAndCopyCode(ExclusiveContext *cx, jit::MacroAssembler &masm);
-    void staticallyLink(const AsmJSStaticLinkData &linkData, ExclusiveContext *cx);
+
+    // StaticLinkData setters (called after finishing compilation, before
+    // staticLink).
+    bool addRelativeLink(RelativeLink link) {
+        return staticLinkData_.relativeLinks.append(link);
+    }
+    bool addAbsoluteLink(AbsoluteLink link) {
+        return staticLinkData_.absoluteLinks.append(link);
+    }
+    void setOperationCallbackOffset(uint32_t offset) {
+        staticLinkData_.operationCallbackExitOffset = offset;
+    }
+
+    void restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx);
+    void staticallyLink(ExclusiveContext *cx);
 
     uint8_t *codeBase() const {
         JS_ASSERT(code_);
         JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
         return code_;
     }
 
     uint8_t *operationCallbackExit() const {
         return operationCallbackExit_;
     }
 
-    void setIsLinked() {
-        JS_ASSERT(!linked_);
-        linked_ = true;
+    void setIsDynamicallyLinked() {
+        JS_ASSERT(!dynamicallyLinked_);
+        dynamicallyLinked_ = true;
     }
-    bool isLinked() const {
-        return linked_;
+    bool isDynamicallyLinked() const {
+        return dynamicallyLinked_;
     }
     uint8_t *maybeHeap() const {
-        JS_ASSERT(linked_);
+        JS_ASSERT(dynamicallyLinked_);
         return heapDatum();
     }
     ArrayBufferObject *maybeHeapBufferObject() const {
-        JS_ASSERT(linked_);
+        JS_ASSERT(dynamicallyLinked_);
         return maybeHeap_;
     }
     size_t heapLength() const {
-        JS_ASSERT(linked_);
+        JS_ASSERT(dynamicallyLinked_);
         return maybeHeap_ ? maybeHeap_->byteLength() : 0;
     }
 
     void initGlobalArgumentName(PropertyName *n) {
         JS_ASSERT_IF(n, n->isTenured());
         globalArgumentName_ = n;
     }
     void initImportArgumentName(PropertyName *n) {
@@ -785,23 +800,24 @@ class AsmJSModule
 
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode,
                        size_t *asmJSModuleData);
 
     size_t serializedSize() const;
     uint8_t *serialize(uint8_t *cursor) const;
     const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
     bool loadedFromCache() const { return loadedFromCache_; }
+
+    bool clone(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const;
 };
 
 // Store the just-parsed module in the cache using AsmJSCacheOps.
 extern bool
 StoreAsmJSModuleInCache(AsmJSParser &parser,
                         const AsmJSModule &module,
-                        const AsmJSStaticLinkData &linkData,
                         ExclusiveContext *cx);
 
 // Attempt to load the asm.js module that is about to be parsed from the cache
 // using AsmJSCacheOps. On cache hit, *module will be non-null. Note: the
 // return value indicates whether or not an error was encountered, not whether
 // there was a cache hit.
 extern bool
 LookupAsmJSModuleInCache(ExclusiveContext *cx,