Bug 1229642 - Factor AsmJSLink.cpp into wasm/asm.js and consolidate AsmJS* into AsmJS.cpp (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Mon, 28 Dec 2015 17:39:55 -0600
changeset 277798 2fe666bbf4ec1e0a31c98f0690addf51404bdf8f
parent 277797 5e0769303a5efc3c745cc9ffd0492fa3a75dcf74
child 277799 164ec87ab3cb22c241ebe8d3501d5a2f05e5f441
push id29835
push usercbook@mozilla.com
push dateWed, 30 Dec 2015 11:00:19 +0000
treeherderautoland@c690c50b2b54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1229642
milestone46.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 1229642 - Factor AsmJSLink.cpp into wasm/asm.js and consolidate AsmJS* into AsmJS.cpp (r=bbouvier)
js/src/asmjs/AsmJS.cpp
js/src/asmjs/AsmJS.h
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSLink.h
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/asmjs/AsmJSValidate.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/asmjs/WasmSignalHandlers.cpp
js/src/asmjs/WasmStubs.cpp
js/src/asmjs/WasmTypes.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/arm/Architecture-arm.h
js/src/jit/mips-shared/Architecture-mips-shared.h
js/src/jit/x86-shared/Architecture-x86-shared.h
js/src/jsapi.cpp
js/src/jsfun.h
js/src/jsobj.cpp
js/src/jsscriptinlines.h
js/src/moz.build
js/src/vm/ArrayBufferObject.cpp
js/src/vm/SharedArrayObject.cpp
rename from js/src/asmjs/AsmJSValidate.cpp
rename to js/src/asmjs/AsmJS.cpp
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -11,53 +11,756 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include "asmjs/AsmJSValidate.h"
-
-#include "mozilla/Move.h"
+#include "asmjs/AsmJS.h"
+
+#include "mozilla/Compression.h"
+#include "mozilla/MathAlgorithms.h"
 
 #include "jsmath.h"
 #include "jsprf.h"
 #include "jsutil.h"
 
-#include "asmjs/AsmJSLink.h"
-#include "asmjs/AsmJSModule.h"
+#include "jswrapper.h"
+
 #include "asmjs/WasmGenerator.h"
+#include "asmjs/WasmSerialize.h"
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "jit/AtomicOperations.h"
 #include "jit/MIR.h"
+#include "js/Class.h"
+#include "js/MemoryMetrics.h"
+#include "vm/StringBuffer.h"
 #include "vm/Time.h"
+#include "vm/TypedArrayObject.h"
 
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
 using namespace js::wasm;
 
+using mozilla::Compression::LZ4;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
+using mozilla::MallocSizeOf;
 using mozilla::Move;
 using mozilla::PodCopy;
+using mozilla::PodEqual;
+using mozilla::PodZero;
 using mozilla::PositiveInfinity;
 using JS::AsmJSOption;
 using JS::GenericNaN;
 
 /*****************************************************************************/
+// asm.js module object
+
+// The asm.js spec recognizes this set of builtin Math functions.
+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,
+    AsmJSMathBuiltin_clz32
+};
+
+// The asm.js spec will recognize this set of builtin Atomics functions.
+enum AsmJSAtomicsBuiltinFunction
+{
+    AsmJSAtomicsBuiltin_compareExchange,
+    AsmJSAtomicsBuiltin_exchange,
+    AsmJSAtomicsBuiltin_load,
+    AsmJSAtomicsBuiltin_store,
+    AsmJSAtomicsBuiltin_fence,
+    AsmJSAtomicsBuiltin_add,
+    AsmJSAtomicsBuiltin_sub,
+    AsmJSAtomicsBuiltin_and,
+    AsmJSAtomicsBuiltin_or,
+    AsmJSAtomicsBuiltin_xor,
+    AsmJSAtomicsBuiltin_isLockFree
+};
+
+// Set of known global object SIMD's attributes, i.e. types
+enum AsmJSSimdType
+{
+    AsmJSSimdType_int32x4,
+    AsmJSSimdType_float32x4,
+    AsmJSSimdType_bool32x4
+};
+
+static inline bool
+IsSignedIntSimdType(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4:
+        return true;
+      case AsmJSSimdType_float32x4:
+      case AsmJSSimdType_bool32x4:
+        return false;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type");
+}
+
+// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
+enum AsmJSSimdOperation
+{
+#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
+    FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION)
+#undef ASMJSSIMDOPERATION
+};
+
+// An AsmJSModule extends (via containment) a wasm::Module with the extra
+// persistent state necessary to represent a compiled asm.js module.
+class js::AsmJSModule
+{
+  public:
+    class Global
+    {
+      public:
+        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
+                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
+        enum VarInitKind { InitConstant, InitImport };
+        enum ConstantKind { GlobalConstant, MathConstant };
+
+      private:
+        struct CacheablePod {
+            Which which_;
+            union {
+                struct {
+                    uint32_t globalDataOffset_;
+                    VarInitKind initKind_;
+                    union {
+                        wasm::ValType importType_;
+                        wasm::Val val_;
+                    } u;
+                } var;
+                uint32_t ffiIndex_;
+                Scalar::Type viewType_;
+                AsmJSMathBuiltinFunction mathBuiltinFunc_;
+                AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
+                AsmJSSimdType simdCtorType_;
+                struct {
+                    AsmJSSimdType type_;
+                    AsmJSSimdOperation which_;
+                } simdOp;
+                struct {
+                    ConstantKind kind_;
+                    double value_;
+                } constant;
+            } u;
+        } pod;
+        PropertyName* name_;
+
+        friend class AsmJSModule;
+
+        Global(Which which, PropertyName* name) {
+            mozilla::PodZero(&pod);  // zero padding for Valgrind
+            pod.which_ = which;
+            name_ = name;
+            MOZ_ASSERT_IF(name_, name_->isTenured());
+        }
+
+        void trace(JSTracer* trc) {
+            if (name_)
+                TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
+        }
+
+      public:
+        Global() {}
+        Which which() const {
+            return pod.which_;
+        }
+        uint32_t varGlobalDataOffset() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            return pod.u.var.globalDataOffset_;
+        }
+        VarInitKind varInitKind() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            return pod.u.var.initKind_;
+        }
+        wasm::Val varInitVal() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
+            return pod.u.var.u.val_;
+        }
+        wasm::ValType varInitImportType() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+            return pod.u.var.u.importType_;
+        }
+        PropertyName* varImportField() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+            return name_;
+        }
+        PropertyName* ffiField() const {
+            MOZ_ASSERT(pod.which_ == FFI);
+            return name_;
+        }
+        uint32_t ffiIndex() const {
+            MOZ_ASSERT(pod.which_ == FFI);
+            return pod.u.ffiIndex_;
+        }
+        // When a view is created from an imported constructor:
+        //   var I32 = stdlib.Int32Array;
+        //   var i32 = new I32(buffer);
+        // the second import has nothing to validate and thus has a null field.
+        PropertyName* maybeViewName() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+            return name_;
+        }
+        Scalar::Type viewType() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+            return pod.u.viewType_;
+        }
+        PropertyName* mathName() const {
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+            return name_;
+        }
+        PropertyName* atomicsName() const {
+            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+            return name_;
+        }
+        AsmJSMathBuiltinFunction mathBuiltinFunction() const {
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+            return pod.u.mathBuiltinFunc_;
+        }
+        AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
+            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+            return pod.u.atomicsBuiltinFunc_;
+        }
+        AsmJSSimdType simdCtorType() const {
+            MOZ_ASSERT(pod.which_ == SimdCtor);
+            return pod.u.simdCtorType_;
+        }
+        PropertyName* simdCtorName() const {
+            MOZ_ASSERT(pod.which_ == SimdCtor);
+            return name_;
+        }
+        PropertyName* simdOperationName() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return name_;
+        }
+        AsmJSSimdOperation simdOperation() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.which_;
+        }
+        AsmJSSimdType simdOperationType() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.type_;
+        }
+        PropertyName* constantName() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return name_;
+        }
+        ConstantKind constantKind() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return pod.u.constant.kind_;
+        }
+        double constantValue() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return pod.u.constant.value_;
+        }
+
+        WASM_DECLARE_SERIALIZABLE(Global);
+    };
+
+    typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
+
+    // An import is slightly different than an asm.js FFI function: a single
+    // asm.js FFI function can be called with many different signatures. When
+    // compiled to wasm, each unique FFI function paired with signature
+    // generates a wasm import.
+    class Import
+    {
+        uint32_t ffiIndex_;
+      public:
+        Import() = default;
+        explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
+        uint32_t ffiIndex() const { return ffiIndex_; }
+    };
+
+    typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
+    class Export
+    {
+        PropertyName* name_;
+        PropertyName* maybeFieldName_;
+        struct CacheablePod {
+            uint32_t wasmIndex_;
+            uint32_t startOffsetInModule_;  // Store module-start-relative offsets
+            uint32_t endOffsetInModule_;    // so preserved by serialization.
+        } pod;
+
+      public:
+        Export() {}
+        Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+               uint32_t startOffsetInModule, uint32_t endOffsetInModule)
+          : name_(name),
+            maybeFieldName_(maybeFieldName)
+        {
+            MOZ_ASSERT(name_->isTenured());
+            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
+            pod.wasmIndex_ = wasmIndex;
+            pod.startOffsetInModule_ = startOffsetInModule;
+            pod.endOffsetInModule_ = endOffsetInModule;
+        }
+
+        void trace(JSTracer* trc) {
+            TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
+            if (maybeFieldName_)
+                TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
+        }
+
+        PropertyName* name() const {
+            return name_;
+        }
+        PropertyName* maybeFieldName() const {
+            return maybeFieldName_;
+        }
+        uint32_t startOffsetInModule() const {
+            return pod.startOffsetInModule_;
+        }
+        uint32_t endOffsetInModule() const {
+            return pod.endOffsetInModule_;
+        }
+        static const uint32_t ChangeHeap = UINT32_MAX;
+        bool isChangeHeap() const {
+            return pod.wasmIndex_ == ChangeHeap;
+        }
+        uint32_t wasmIndex() const {
+            MOZ_ASSERT(!isChangeHeap());
+            return pod.wasmIndex_;
+        }
+
+        WASM_DECLARE_SERIALIZABLE(Export)
+    };
+
+    typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+
+    typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
+
+  private:
+    UniqueWasmModule            wasmModule_;
+    wasm::UniqueStaticLinkData  linkData_;
+    struct CacheablePod {
+        uint32_t                minHeapLength_;
+        uint32_t                maxHeapLength_;
+        uint32_t                heapLengthMask_;
+        uint32_t                numFFIs_;
+        uint32_t                srcLength_;
+        uint32_t                srcLengthWithRightBrace_;
+        bool                    strict_;
+        bool                    hasArrayView_;
+        bool                    isSharedView_;
+        bool                    hasFixedMinHeapLength_;
+    } pod;
+    const ScriptSourceHolder    scriptSource_;
+    const uint32_t              srcStart_;
+    const uint32_t              srcBodyStart_;
+    GlobalVector                globals_;
+    ImportVector                imports_;
+    ExportVector                exports_;
+    PropertyName*               globalArgumentName_;
+    PropertyName*               importArgumentName_;
+    PropertyName*               bufferArgumentName_;
+
+  public:
+    explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
+                         bool strict)
+      : scriptSource_(scriptSource),
+        srcStart_(srcStart),
+        srcBodyStart_(srcBodyStart),
+        globalArgumentName_(nullptr),
+        importArgumentName_(nullptr),
+        bufferArgumentName_(nullptr)
+    {
+        mozilla::PodZero(&pod);
+        pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
+        pod.maxHeapLength_ = 0x80000000;
+        pod.strict_ = strict;
+
+        MOZ_ASSERT(srcStart_ <= srcBodyStart_);
+
+        // AsmJSCheckedImmediateRange should be defined to be at most the minimum
+        // heap length so that offsets can be folded into bounds checks.
+        MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_);
+    }
+
+    void trace(JSTracer* trc) {
+        if (wasmModule_)
+            wasmModule_->trace(trc);
+        for (Global& global : globals_)
+            global.trace(trc);
+        for (Export& exp : exports_)
+            exp.trace(trc);
+        if (globalArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
+        if (importArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
+        if (bufferArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
+    }
+
+    /*************************************************************************/
+    // These functions may be used as soon as the module is constructed:
+
+    ScriptSource* scriptSource() const {
+        return scriptSource_.get();
+    }
+    bool strict() const {
+        return pod.strict_;
+    }
+
+    // srcStart() refers to the offset in the ScriptSource to the beginning of
+    // the asm.js module function. If the function has been created with the
+    // Function constructor, this will be the first character in the function
+    // source. Otherwise, it will be the opening parenthesis of the arguments
+    // list.
+    uint32_t srcStart() const {
+        return srcStart_;
+    }
+
+    // srcBodyStart() refers to the offset in the ScriptSource to the end
+    // of the 'use asm' string-literal token.
+    uint32_t srcBodyStart() const {
+        return srcBodyStart_;
+    }
+
+    // While these functions may be accessed at any time, their values will
+    // change as the module is compiled.
+    uint32_t minHeapLength() const {
+        return pod.minHeapLength_;
+    }
+    uint32_t maxHeapLength() const {
+        return pod.maxHeapLength_;
+    }
+    uint32_t heapLengthMask() const {
+        MOZ_ASSERT(pod.hasFixedMinHeapLength_);
+        return pod.heapLengthMask_;
+    }
+
+    void initGlobalArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        globalArgumentName_ = n;
+    }
+    void initImportArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        importArgumentName_ = n;
+    }
+    void initBufferArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        bufferArgumentName_ = n;
+    }
+    PropertyName* globalArgumentName() const {
+        return globalArgumentName_;
+    }
+    PropertyName* importArgumentName() const {
+        return importArgumentName_;
+    }
+    PropertyName* bufferArgumentName() const {
+        return bufferArgumentName_;
+    }
+
+    bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Variable, nullptr);
+        g.pod.u.var.initKind_ = Global::InitConstant;
+        g.pod.u.var.u.val_ = v;
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return globals_.append(g);
+    }
+    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Variable, name);
+        g.pod.u.var.initKind_ = Global::InitImport;
+        g.pod.u.var.u.importType_ = importType;
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return globals_.append(g);
+    }
+    // See Import comment above for FFI vs. Import.
+    bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
+        MOZ_ASSERT(!isFinished());
+        if (pod.numFFIs_ == UINT32_MAX)
+            return false;
+        Global g(Global::FFI, field);
+        g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
+        return globals_.append(g);
+    }
+    bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
+        MOZ_ASSERT(!isFinished());
+        pod.hasArrayView_ = true;
+        pod.isSharedView_ = false;
+        Global g(Global::ArrayView, maybeField);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(field);
+        pod.isSharedView_ = false;
+        Global g(Global::ArrayViewCtor, field);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addByteLength() {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::ByteLength, nullptr);
+        return globals_.append(g);
+    }
+    bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::MathBuiltinFunction, field);
+        g.pod.u.mathBuiltinFunc_ = func;
+        return globals_.append(g);
+    }
+    bool addMathBuiltinConstant(double value, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Constant, field);
+        g.pod.u.constant.value_ = value;
+        g.pod.u.constant.kind_ = Global::MathConstant;
+        return globals_.append(g);
+    }
+    bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::AtomicsBuiltinFunction, field);
+        g.pod.u.atomicsBuiltinFunc_ = func;
+        return globals_.append(g);
+    }
+    bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::SimdCtor, field);
+        g.pod.u.simdCtorType_ = type;
+        return globals_.append(g);
+    }
+    bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::SimdOperation, field);
+        g.pod.u.simdOp.type_ = type;
+        g.pod.u.simdOp.which_ = op;
+        return globals_.append(g);
+    }
+    bool addGlobalConstant(double value, PropertyName* name) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Constant, name);
+        g.pod.u.constant.value_ = value;
+        g.pod.u.constant.kind_ = Global::GlobalConstant;
+        return globals_.append(g);
+    }
+    // See Import comment above for FFI vs. Import.
+    bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
+        MOZ_ASSERT(imports_.length() == importIndex);
+        return imports_.emplaceBack(ffiIndex);
+    }
+    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+                   uint32_t funcSrcBegin, uint32_t funcSrcEnd)
+    {
+        // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
+        // (the entire file) and ExportedFunctions store offsets relative to
+        // the beginning of the module (so that they are caching-invariant).
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(srcStart_ < funcSrcBegin);
+        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
+        return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
+                                    funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
+    }
+    bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
+        MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
+        MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
+        MOZ_ASSERT(max <= pod.maxHeapLength_);
+        MOZ_ASSERT(min <= max);
+        pod.heapLengthMask_ = mask;
+        pod.minHeapLength_ = min;
+        pod.maxHeapLength_ = max;
+        pod.hasFixedMinHeapLength_ = true;
+        return true;
+    }
+
+    const GlobalVector& globals() const {
+        return globals_;
+    }
+    const ImportVector& imports() const {
+        return imports_;
+    }
+    const ExportVector& exports() const {
+        return exports_;
+    }
+
+    void setViewsAreShared() {
+        if (pod.hasArrayView_)
+            pod.isSharedView_ = true;
+    }
+    bool hasArrayView() const {
+        return pod.hasArrayView_;
+    }
+    bool isSharedView() const {
+        return pod.isSharedView_;
+    }
+    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
+        MOZ_ASSERT(!isFinished());
+        if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
+            return false;
+        if (len > pod.maxHeapLength_)
+            return false;
+        len = RoundUpToNextValidAsmJSHeapLength(len);
+        if (len > pod.minHeapLength_)
+            pod.minHeapLength_ = len;
+        return true;
+    }
+
+    /*************************************************************************/
+    // A module isFinished() when compilation completes. After being finished,
+    // a module must be statically and dynamically linked before execution.
+
+    bool isFinished() const {
+        return !!wasmModule_;
+    }
+    void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData,
+                uint32_t endBeforeCurly, uint32_t endAfterCurly)
+    {
+        MOZ_ASSERT(!isFinished());
+
+        wasmModule_.reset(wasmModule);
+        linkData_ = Move(linkData);
+
+        MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
+        MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
+        pod.srcLength_ = endBeforeCurly - srcStart_;
+        pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
+
+        MOZ_ASSERT(isFinished());
+    }
+
+    /*************************************************************************/
+    // These accessor functions can only be used after finish():
+
+    wasm::Module& wasmModule() const {
+        MOZ_ASSERT(isFinished());
+        return *wasmModule_;
+    }
+    uint32_t numFFIs() const {
+        MOZ_ASSERT(isFinished());
+        return pod.numFFIs_;
+    }
+    uint32_t srcEndBeforeCurly() const {
+        MOZ_ASSERT(isFinished());
+        return srcStart_ + pod.srcLength_;
+    }
+    uint32_t srcEndAfterCurly() const {
+        MOZ_ASSERT(isFinished());
+        return srcStart_ + pod.srcLengthWithRightBrace_;
+    }
+    bool staticallyLink(ExclusiveContext* cx) {
+        return wasmModule_->staticallyLink(cx, *linkData_);
+    }
+
+    // See WASM_DECLARE_SERIALIZABLE.
+    size_t serializedSize() const;
+    uint8_t* serialize(uint8_t* cursor) const;
+    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
+    bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
+    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+};
+
+static void
+AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
+{
+    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+    if (moduleObj.hasModule())
+        fop->delete_(&moduleObj.module());
+}
+
+static void
+AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
+{
+    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+    if (moduleObj.hasModule())
+        moduleObj.module().trace(trc);
+}
+
+const Class AsmJSModuleObject::class_ = {
+    "AsmJSModuleObject",
+    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
+    JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    AsmJSModuleObject_finalize,
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    AsmJSModuleObject_trace
+};
+
+static AsmJSModuleObject*
+NewAsmJSModuleObject(ExclusiveContext* cx)
+{
+    AutoSetNewObjectMetadata metadata(cx);
+    JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
+    if (!obj)
+        return nullptr;
+
+    return &obj->as<AsmJSModuleObject>();
+}
+
+bool
+AsmJSModuleObject::hasModule() const
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    return !getReservedSlot(MODULE_SLOT).isUndefined();
+}
+
+void
+AsmJSModuleObject::setModule(AsmJSModule* newModule)
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    if (hasModule())
+        js_delete(&module());
+    setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
+}
+
+AsmJSModule&
+AsmJSModuleObject::module() const
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
+}
+
+void
+AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+    module().addSizeOfMisc(mallocSizeOf, code, data);
+}
+
+/*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode*
 NextNode(ParseNode* pn)
 {
     return pn->pn_next;
 }
 
@@ -534,28 +1237,28 @@ class NumLit
         Bool32x4,
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
     union {
         Value scalar_;
-        jit::SimdConstant simd_;
+        SimdConstant simd_;
     } u;
 
   public:
     NumLit() = default;
 
     NumLit(Which w, Value v) : which_(w) {
         u.scalar_ = v;
         MOZ_ASSERT(!isSimd());
     }
 
-    NumLit(Which w, jit::SimdConstant c) : which_(w) {
+    NumLit(Which w, SimdConstant c) : which_(w) {
         u.simd_ = c;
         MOZ_ASSERT(isSimd());
     }
 
     Which which() const {
         return which_;
     }
 
@@ -837,40 +1540,40 @@ class Type
             return ValType::F32;
         else if (isDouble())
             return ValType::F64;
         else if (isInt32x4())
             return ValType::I32x4;
         return ValType::F32x4;
     }
 
-    jit::MIRType toMIRType() const {
+    MIRType toMIRType() const {
         switch (which_) {
           case Double:
           case DoubleLit:
           case MaybeDouble:
-            return jit::MIRType_Double;
+            return MIRType_Double;
           case Float:
           case Floatish:
           case MaybeFloat:
-            return jit::MIRType_Float32;
+            return MIRType_Float32;
           case Fixnum:
           case Int:
           case Signed:
           case Unsigned:
           case Intish:
-            return jit::MIRType_Int32;
+            return MIRType_Int32;
           case Int32x4:
-            return jit::MIRType_Int32x4;
+            return MIRType_Int32x4;
           case Float32x4:
-            return jit::MIRType_Float32x4;
+            return MIRType_Float32x4;
           case Bool32x4:
-            return jit::MIRType_Bool32x4;
+            return MIRType_Bool32x4;
           case Void:
-            return jit::MIRType_None;
+            return MIRType_None;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
     }
 
     AsmJSSimdType simdType() const {
         MOZ_ASSERT(isSimd());
         switch (which_) {
           case Int32x4:
@@ -1514,17 +2217,18 @@ class MOZ_STACK_CLASS ModuleValidator
         global->u.simdOp.type_ = type;
         global->u.simdOp.which_ = op;
         return globals_.putNew(var, global) &&
                module().addSimdOperation(type, op, opName);
     }
     bool addByteLength(PropertyName* name) {
         canValidateChangeHeap_ = true;
         Global* global = validationLifo_.new_<Global>(Global::ByteLength);
-        return global && globals_.putNew(name, global) &&
+        return global &&
+               globals_.putNew(name, global) &&
                module().addByteLength();
     }
     bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) {
         hasChangeHeap_ = true;
         Global* global = validationLifo_.new_<Global>(Global::ChangeHeap);
         if (!global)
             return false;
         global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
@@ -1545,24 +2249,26 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!global)
             return false;
         uint32_t index;
         if (!module().addFFI(field, &index))
             return false;
         global->u.ffiIndex_ = index;
         return globals_.putNew(var, global);
     }
-    bool addExport(const Func& func, PropertyName* maybeFieldName) {
+    bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) {
         MallocSig::ArgVector args;
         if (!args.appendAll(func.sig().args()))
             return false;
         MallocSig sig(Move(args), func.sig().ret());
         uint32_t wasmIndex;
         if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex))
             return false;
+        if (wasmIndex == AsmJSModule::Export::ChangeHeap)
+            return fail(pn, "too many exports");
         return module().addExport(func.name(), maybeFieldName, wasmIndex,
                                   func.srcBegin(), func.srcEnd());
     }
     bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
         return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap,
                                   g.changeHeapSrcBegin(), g.changeHeapSrcEnd());
     }
   private:
@@ -1580,19 +2286,17 @@ class MOZ_STACK_CLASS ModuleValidator
             return false;
         global->u.funcIndex_ = funcIndex;
         if (!globals_.putNew(name, global))
             return false;
         const LifoSig* lifoSig = getLifoSig(sig);
         if (!lifoSig)
             return false;
         *func = validationLifo_.new_<Func>(name, firstUse, *lifoSig, funcIndex);
-        if (!*func)
-            return false;
-        return functions_.append(*func);
+        return *func && functions_.append(*func);
     }
     template <class SigT>
     bool declareFuncPtrTable(PropertyName* name, uint32_t firstUse, SigT& sig, uint32_t mask,
                              uint32_t* index)
     {
         if (!mg_.declareFuncPtrTable(/* numElems = */ mask + 1, index))
             return false;
         MOZ_ASSERT(*index == numFuncPtrTables());
@@ -1624,19 +2328,18 @@ class MOZ_STACK_CLASS ModuleValidator
         if (p) {
             *lifoSig = &p->key().sig();
             *importIndex = p->value();
             return true;
         }
         *lifoSig = getLifoSig(sig);
         if (!*lifoSig)
             return false;
-        if (!mg_.declareImport(Move(sig), importIndex))
-            return false;
-        return imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
+        return mg_.declareImport(Move(sig), importIndex) &&
+               imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
                module().addImport(ffiIndex, *importIndex);
     }
 
     bool tryOnceToValidateChangeHeap() {
         bool ret = canValidateChangeHeap_;
         canValidateChangeHeap_ = false;
         return ret;
     }
@@ -3704,17 +4407,17 @@ CheckAtomicsStore(FunctionValidator& f, 
     f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
     f.patchU8(viewTypeAt, uint8_t(viewType));
 
     *type = rhsType;
     return true;
 }
 
 static bool
-CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op)
+CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op)
 {
     if (CallArgListLength(call) != 3)
         return f.fail(call, "Atomics binary operator must be passed 3 arguments");
 
     ParseNode* arrayArg = CallArgList(call);
     ParseNode* indexArg = NextNode(arrayArg);
     ParseNode* valueArg = NextNode(indexArg);
 
@@ -6786,17 +7489,17 @@ CheckModuleExportFunction(ModuleValidato
         return m.fail(pn, "expected name of exported function");
 
     PropertyName* funcName = pn->name();
     const ModuleValidator::Global* global = m.lookupGlobal(funcName);
     if (!global)
         return m.failName(pn, "exported function name '%s' not found", funcName);
 
     if (global->which() == ModuleValidator::Global::Function)
-        return m.addExport(m.function(global->funcIndex()), maybeFieldName);
+        return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
 
     if (global->which() == ModuleValidator::Global::ChangeHeap)
         return m.addChangeHeapExport(funcName, *global, maybeFieldName);
 
     return m.failName(pn, "'%s' is not a function", funcName);
 }
 
 static bool
@@ -6927,16 +7630,1329 @@ CheckModule(ExclusiveContext* cx, AsmJSP
 
     if (!m.finish(slowFuncs))
         return false;
 
     *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
     return true;
 }
 
+/*****************************************************************************/
+// Runtime calls to asm.js module exports
+
+static AsmJSModuleObject&
+FunctionToModuleObject(JSFunction* fun)
+{
+    MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
+    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT);
+    return v.toObject().as<AsmJSModuleObject>();
+}
+
+static unsigned
+FunctionToExportIndex(JSFunction* fun)
+{
+    MOZ_ASSERT(IsAsmJSFunction(fun));
+    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT);
+    return v.toInt32();
+}
+
+static bool
+ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
+{
+    HandleValue bufferArg = args.get(0);
+    if (!IsArrayBuffer(bufferArg)) {
+        ReportIncompatible(cx, args);
+        return false;
+    }
+
+    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
+    uint32_t heapLength = newBuffer->byteLength();
+    if (heapLength & module.heapLengthMask() ||
+        heapLength < module.minHeapLength() ||
+        heapLength > module.maxHeapLength())
+    {
+        args.rval().set(BooleanValue(false));
+        return true;
+    }
+
+    if (!module.hasArrayView()) {
+        args.rval().set(BooleanValue(true));
+        return true;
+    }
+
+    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
+
+    bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
+    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
+        return false;
+
+    args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx)));
+    return true;
+}
+
+static bool
+CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedFunction callee(cx, &args.callee().as<JSFunction>());
+
+    AsmJSModule& module = FunctionToModuleObject(callee).module();
+    const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)];
+
+    // The heap-changing function is a special-case and is implemented by C++.
+    if (exp.isChangeHeap())
+        return ChangeHeap(cx, module, args);
+
+    return module.wasmModule().callExport(cx, exp.wasmIndex(), args);
+}
+
+/*****************************************************************************/
+// Link-time validation
+
+static bool
+LinkFail(JSContext* cx, const char* str)
+{
+    JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
+                                 nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
+    return false;
+}
+
+static bool
+GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
+{
+    if (!objVal.isObject())
+        return LinkFail(cx, "accessing property of non-object");
+
+    RootedObject obj(cx, &objVal.toObject());
+    if (IsScriptedProxy(obj))
+        return LinkFail(cx, "accessing property of a Proxy");
+
+    Rooted<PropertyDescriptor> desc(cx);
+    RootedId id(cx, NameToId(field));
+    if (!GetPropertyDescriptor(cx, obj, id, &desc))
+        return false;
+
+    if (!desc.object())
+        return LinkFail(cx, "property not present on object");
+
+    if (!desc.isDataDescriptor())
+        return LinkFail(cx, "property is not a data property");
+
+    v.set(desc.value());
+    return true;
+}
+
+static bool
+HasPureCoercion(JSContext* cx, HandleValue v)
+{
+    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v))
+        return true;
+
+    // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
+    // bug that generates code that passes functions for some imports. To avoid
+    // breaking all the code that contains this bug, we make an exception for
+    // functions that don't have user-defined valueOf or toString, for their
+    // coercions are not observable and coercion via ToNumber/ToInt32
+    // definitely produces NaN/0. We should remove this special case later once
+    // most apps have been built with newer Emscripten.
+    jsid toString = NameToId(cx->names().toString);
+    if (v.toObject().is<JSFunction>() &&
+        HasObjectValueOf(&v.toObject(), cx) &&
+        ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
+                       HandleValue importVal)
+{
+    void* datum = globalData + global.varGlobalDataOffset();
+
+    switch (global.varInitKind()) {
+      case AsmJSModule::Global::InitConstant: {
+        Val v = global.varInitVal();
+        switch (v.type()) {
+          case ValType::I32:
+            *(int32_t*)datum = v.i32();
+            break;
+          case ValType::I64:
+            MOZ_CRASH("int64");
+          case ValType::F32:
+            *(float*)datum = v.f32();
+            break;
+          case ValType::F64:
+            *(double*)datum = v.f64();
+            break;
+          case ValType::I32x4:
+          case ValType::B32x4:
+            // Bool32x4 uses the same data layout as Int32x4.
+            memcpy(datum, v.i32x4(), Simd128DataSize);
+            break;
+          case ValType::F32x4:
+            memcpy(datum, v.f32x4(), Simd128DataSize);
+            break;
+        }
+        break;
+      }
+
+      case AsmJSModule::Global::InitImport: {
+        RootedPropertyName field(cx, global.varImportField());
+        RootedValue v(cx);
+        if (!GetDataProperty(cx, importVal, field, &v))
+            return false;
+
+        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
+            return LinkFail(cx, "Imported values must be primitives");
+
+        switch (global.varInitImportType()) {
+          case ValType::I32:
+            if (!ToInt32(cx, v, (int32_t*)datum))
+                return false;
+            break;
+          case ValType::I64:
+            MOZ_CRASH("int64");
+          case ValType::F32:
+            if (!RoundFloat32(cx, v, (float*)datum))
+                return false;
+            break;
+          case ValType::F64:
+            if (!ToNumber(cx, v, (double*)datum))
+                return false;
+            break;
+          case ValType::I32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::F32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::B32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
+                return false;
+            // Bool32x4 uses the same data layout as Int32x4.
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          }
+        }
+        break;
+      }
+    }
+
+    return true;
+}
+
+static bool
+ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
+            AutoVectorRooter<JSFunction*>* ffis)
+{
+    RootedPropertyName field(cx, global.ffiField());
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, importVal, field, &v))
+        return false;
+
+    if (!v.isObject() || !v.toObject().is<JSFunction>())
+        return LinkFail(cx, "FFI imports must be functions");
+
+    (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
+    return true;
+}
+
+static bool
+ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedPropertyName field(cx, global.maybeViewName());
+    if (!field)
+        return true;
+
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, field, &v))
+        return false;
+
+    bool tac = IsTypedArrayConstructor(v, global.viewType());
+    if (!tac)
+        return LinkFail(cx, "bad typed array constructor");
+
+    return true;
+}
+
+static bool
+ValidateByteLength(JSContext* cx, HandleValue globalVal)
+{
+    RootedPropertyName field(cx, cx->names().byteLength);
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, field, &v))
+        return false;
+
+    if (!v.isObject() || !v.toObject().isBoundFunction())
+        return LinkFail(cx, "byteLength must be a bound function object");
+
+    RootedFunction fun(cx, &v.toObject().as<JSFunction>());
+
+    RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
+    if (!IsNativeFunction(boundTarget, fun_call))
+        return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
+
+    RootedValue boundThis(cx, fun->getBoundFunctionThis());
+    if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
+        return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
+
+    return true;
+}
+
+static bool
+ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
+        return false;
+
+    RootedPropertyName field(cx, global.mathName());
+    if (!GetDataProperty(cx, v, field, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.mathBuiltinFunction()) {
+      case AsmJSMathBuiltin_sin: native = math_sin; break;
+      case AsmJSMathBuiltin_cos: native = math_cos; break;
+      case AsmJSMathBuiltin_tan: native = math_tan; break;
+      case AsmJSMathBuiltin_asin: native = math_asin; break;
+      case AsmJSMathBuiltin_acos: native = math_acos; break;
+      case AsmJSMathBuiltin_atan: native = math_atan; break;
+      case AsmJSMathBuiltin_ceil: native = math_ceil; break;
+      case AsmJSMathBuiltin_floor: native = math_floor; break;
+      case AsmJSMathBuiltin_exp: native = math_exp; break;
+      case AsmJSMathBuiltin_log: native = math_log; break;
+      case AsmJSMathBuiltin_pow: native = math_pow; break;
+      case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
+      case AsmJSMathBuiltin_min: native = math_min; break;
+      case AsmJSMathBuiltin_max: native = math_max; break;
+      case AsmJSMathBuiltin_abs: native = math_abs; break;
+      case AsmJSMathBuiltin_atan2: native = math_atan2; break;
+      case AsmJSMathBuiltin_imul: native = math_imul; break;
+      case AsmJSMathBuiltin_clz32: native = math_clz32; break;
+      case AsmJSMathBuiltin_fround: native = math_fround; break;
+    }
+
+    if (!IsNativeFunction(v, native))
+        return LinkFail(cx, "bad Math.* builtin function");
+
+    return true;
+}
+
+static PropertyName*
+SimdTypeToName(JSContext* cx, AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4:   return cx->names().int32x4;
+      case AsmJSSimdType_float32x4: return cx->names().float32x4;
+      case AsmJSSimdType_bool32x4:  return cx->names().bool32x4;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
+}
+
+static SimdTypeDescr::Type
+AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4: return Int32x4::type;
+      case AsmJSSimdType_float32x4: return Float32x4::type;
+      case AsmJSSimdType_bool32x4: return Bool32x4::type;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
+                 MutableHandleValue out)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
+        return false;
+
+    AsmJSSimdType type;
+    if (global.which() == AsmJSModule::Global::SimdCtor)
+        type = global.simdCtorType();
+    else
+        type = global.simdOperationType();
+
+    RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
+    if (!GetDataProperty(cx, v, simdTypeName, &v))
+        return false;
+
+    if (!v.isObject())
+        return LinkFail(cx, "bad SIMD type");
+
+    RootedObject simdDesc(cx, &v.toObject());
+    if (!simdDesc->is<SimdTypeDescr>())
+        return LinkFail(cx, "bad SIMD type");
+
+    if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
+        return LinkFail(cx, "bad SIMD type");
+
+    out.set(v);
+    return true;
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedValue _(cx);
+    return ValidateSimdType(cx, global, globalVal, &_);
+}
+
+static bool
+ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    // SIMD operations are loaded from the SIMD type, so the type must have been
+    // validated before the operation.
+    RootedValue v(cx);
+    JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
+
+    RootedPropertyName opName(cx, global.simdOperationName());
+    if (!GetDataProperty(cx, v, opName, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.simdOperationType()) {
+#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
+#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
+#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break;
+#define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
+      case AsmJSSimdType_int32x4:
+        switch (global.simdOperation()) {
+          FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
+          default:
+            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                    "place");
+        }
+        break;
+      case AsmJSSimdType_float32x4:
+        switch (global.simdOperation()) {
+          FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)
+          default:
+             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                     "place");
+        }
+        break;
+      case AsmJSSimdType_bool32x4:
+        switch (global.simdOperation()) {
+          FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4)
+          default:
+             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                     "place");
+        }
+        break;
+#undef FALLTHROUGH
+#undef SET_NATIVE_FLOAT32X4
+#undef SET_NATIVE_INT32X4
+#undef SET_NATIVE_BOOL32X4
+#undef SET_NATIVE
+    }
+    if (!native || !IsNativeFunction(v, native))
+        return LinkFail(cx, "bad SIMD.type.* operation");
+    return true;
+}
+
+static bool
+ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
+        return false;
+    RootedPropertyName field(cx, global.atomicsName());
+    if (!GetDataProperty(cx, v, field, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.atomicsBuiltinFunction()) {
+      case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
+      case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
+      case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
+      case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
+      case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break;
+      case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
+      case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
+      case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
+      case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
+      case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
+      case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
+    }
+
+    if (!IsNativeFunction(v, native))
+        return LinkFail(cx, "bad Atomics.* builtin function");
+
+    return true;
+}
+
+static bool
+ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedPropertyName field(cx, global.constantName());
+    RootedValue v(cx, globalVal);
+
+    if (global.constantKind() == AsmJSModule::Global::MathConstant) {
+        if (!GetDataProperty(cx, v, cx->names().Math, &v))
+            return false;
+    }
+
+    if (!GetDataProperty(cx, v, field, &v))
+        return false;
+
+    if (!v.isNumber())
+        return LinkFail(cx, "math / global constant value needs to be a number");
+
+    // NaN != NaN
+    if (IsNaN(global.constantValue())) {
+        if (!IsNaN(v.toNumber()))
+            return LinkFail(cx, "global constant value needs to be NaN");
+    } else {
+        if (v.toNumber() != global.constantValue())
+            return LinkFail(cx, "global constant value mismatch");
+    }
+
+    return true;
+}
+
+static bool
+CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
+            MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
+{
+    if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
+        return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
+
+    if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
+        return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
+
+    buffer.set(&AsAnyArrayBuffer(bufferVal));
+    uint32_t heapLength = buffer->byteLength();
+
+    if (!IsValidAsmJSHeapLength(heapLength)) {
+        UniqueChars msg(
+            JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
+                        "valid length is 0x%x",
+                        heapLength,
+                        RoundUpToNextValidAsmJSHeapLength(heapLength)));
+        return LinkFail(cx, msg.get());
+    }
+
+    // This check is sufficient without considering the size of the loaded datum because heap
+    // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
+    MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
+    if (heapLength < module.minHeapLength()) {
+        UniqueChars msg(
+            JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
+                        "by const heap accesses and/or change-heap minimum-length requirements).",
+                        heapLength,
+                        module.minHeapLength()));
+        return LinkFail(cx, msg.get());
+    }
+
+    if (heapLength > module.maxHeapLength()) {
+        UniqueChars msg(
+            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
+                        heapLength,
+                        module.maxHeapLength()));
+        return LinkFail(cx, msg.get());
+    }
+
+    // Shell builtins may have disabled signal handlers since the module we're
+    // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
+    // as well for the caching case.
+    if (module.wasmModule().compileArgs() != CompileArgs(cx))
+        return LinkFail(cx, "Signals have been toggled since compilation");
+
+    if (buffer->is<ArrayBufferObject>()) {
+        Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
+        bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
+        if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
+            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
+    }
+
+    return true;
+}
+
+static bool
+DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
+{
+    HandleValue globalVal = args.get(0);
+    HandleValue importVal = args.get(1);
+    HandleValue bufferVal = args.get(2);
+
+    Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
+    if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer))
+        return false;
+
+    AutoVectorRooter<JSFunction*> ffis(cx);
+    if (!ffis.resize(module.numFFIs()))
+        return false;
+
+    for (const AsmJSModule::Global& global : module.globals()) {
+        switch (global.which()) {
+          case AsmJSModule::Global::Variable:
+            if (!ValidateGlobalVariable(cx, global, module.wasmModule().globalData(), importVal))
+                return false;
+            break;
+          case AsmJSModule::Global::FFI:
+            if (!ValidateFFI(cx, global, importVal, &ffis))
+                return false;
+            break;
+          case AsmJSModule::Global::ArrayView:
+          case AsmJSModule::Global::ArrayViewCtor:
+            if (!ValidateArrayView(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::ByteLength:
+            if (!ValidateByteLength(cx, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::MathBuiltinFunction:
+            if (!ValidateMathBuiltinFunction(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::AtomicsBuiltinFunction:
+            if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::Constant:
+            if (!ValidateConstant(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::SimdCtor:
+            if (!ValidateSimdType(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::SimdOperation:
+            if (!ValidateSimdOperation(cx, global, globalVal))
+                return false;
+            break;
+        }
+    }
+
+    AutoVectorRooter<JSFunction*> imports(cx);
+    for (const AsmJSModule::Import& import : module.imports()) {
+        if (!imports.append(ffis[import.ffiIndex()]))
+            return false;
+    }
+
+    return module.wasmModule().dynamicallyLink(cx, buffer, imports);
+}
+
+static JSFunction*
+NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
+                    HandleObject moduleObj, unsigned exportIndex)
+{
+    unsigned numArgs = func.isChangeHeap()
+                       ? 1
+                       : module.wasmModule().exports()[func.wasmIndex()].sig().args().length();
+
+    RootedPropertyName name(cx, func.name());
+    JSFunction* fun =
+        NewNativeConstructor(cx, CallAsmJS, numArgs, name,
+                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
+                             JSFunction::ASMJS_CTOR);
+    if (!fun)
+        return nullptr;
+
+    fun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    fun->setExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
+    return fun;
+}
+
+static bool
+HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
+                         HandlePropertyName name)
+{
+    if (cx->isExceptionPending())
+        return false;
+
+    ScriptSource* source = module.scriptSource();
+
+    // Source discarding is allowed to affect JS semantics because it is never
+    // enabled for normal JS content.
+    bool haveSource = source->hasSourceData();
+    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+        return false;
+    if (!haveSource) {
+        JS_ReportError(cx, "asm.js link failure with source discarding enabled");
+        return false;
+    }
+
+    uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
+    uint32_t end = module.srcEndBeforeCurly();
+    Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
+    if (!src)
+        return false;
+
+    RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
+                                               name, gc::AllocKind::FUNCTION,
+                                               TenuredObject));
+    if (!fun)
+        return false;
+
+    Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
+    if (!formals.reserve(3))
+        return false;
+
+    if (module.globalArgumentName())
+        formals.infallibleAppend(module.globalArgumentName());
+    if (module.importArgumentName())
+        formals.infallibleAppend(module.importArgumentName());
+    if (module.bufferArgumentName())
+        formals.infallibleAppend(module.bufferArgumentName());
+
+    CompileOptions options(cx);
+    options.setMutedErrors(source->mutedErrors())
+           .setFile(source->filename())
+           .setNoScriptRval(false);
+
+    // The exported function inherits an implicit strict context if the module
+    // also inherited it somehow.
+    if (module.strict())
+        options.strictOption = true;
+
+    AutoStableStringChars stableChars(cx);
+    if (!stableChars.initTwoByte(cx, src))
+        return false;
+
+    const char16_t* chars = stableChars.twoByteRange().start().get();
+    SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
+                                              ? SourceBufferHolder::GiveOwnership
+                                              : SourceBufferHolder::NoOwnership;
+    SourceBufferHolder srcBuf(chars, end - begin, ownership);
+    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
+        return false;
+
+    // Call the function we just recompiled.
+    args.setCallee(ObjectValue(*fun));
+    return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
+}
+
+static JSObject*
+CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj)
+{
+    AsmJSModule& module = moduleObj->module();
+    const AsmJSModule::ExportVector& exports = module.exports();
+
+    if (exports.length() == 1) {
+        const AsmJSModule::Export& func = exports[0];
+        if (!func.maybeFieldName())
+            return NewExportedFunction(cx, module, func, moduleObj, 0);
+    }
+
+    gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length());
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
+    if (!obj)
+        return nullptr;
+
+    for (unsigned i = 0; i < exports.length(); i++) {
+        const AsmJSModule::Export& func = exports[i];
+
+        RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i));
+        if (!fun)
+            return nullptr;
+
+        MOZ_ASSERT(func.maybeFieldName() != nullptr);
+        RootedId id(cx, NameToId(func.maybeFieldName()));
+        RootedValue val(cx, ObjectValue(*fun));
+        if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
+            return nullptr;
+    }
+
+    return obj;
+}
+
+// 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>());
+    Rooted<AsmJSModuleObject*> moduleObj(cx, &FunctionToModuleObject(fun));
+
+    // 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().wasmModule().dynamicallyLinked()) {
+        Rooted<AsmJSModuleObject*> clone(cx, NewAsmJSModuleObject(cx));
+        if (!clone)
+            return false;
+
+        if (!moduleObj->module().clone(cx, clone))
+            return false;
+
+        moduleObj = clone;
+    }
+
+    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.
+        RootedPropertyName name(cx, fun->name());
+        return HandleDynamicLinkFailure(cx, args, module, name);
+    }
+
+    // Link-time validation succeeded, so wrap all the exported functions with
+    // CallAsmJS builtins that trampoline into the generated code.
+    JSObject* obj = CreateExportObject(cx, moduleObj);
+    if (!obj)
+        return false;
+
+    args.rval().set(ObjectValue(*obj));
+    return true;
+}
+
+static JSFunction*
+NewModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject moduleObj)
+{
+    RootedPropertyName name(cx, origFun->name());
+
+    JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
+                                                  : JSFunction::ASMJS_CTOR;
+    JSFunction* moduleFun =
+        NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
+                             gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
+                             flags);
+    if (!moduleFun)
+        return nullptr;
+
+    moduleFun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    return moduleFun;
+}
+
+/*****************************************************************************/
+// Caching and cloning
+
+uint8_t*
+AsmJSModule::Global::serialize(uint8_t* cursor) const
+{
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    cursor = SerializeName(cursor, name_);
+    return cursor;
+}
+
+size_t
+AsmJSModule::Global::serializedSize() const
+{
+    return sizeof(pod) +
+           SerializedNameSize(name_);
+}
+
+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(JSContext* cx, Global* out) const
+{
+    *out = *this;
+    return true;
+}
+
+uint8_t*
+AsmJSModule::Export::serialize(uint8_t* cursor) const
+{
+    cursor = SerializeName(cursor, name_);
+    cursor = SerializeName(cursor, maybeFieldName_);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    return cursor;
+}
+
+size_t
+AsmJSModule::Export::serializedSize() const
+{
+    return SerializedNameSize(name_) +
+           SerializedNameSize(maybeFieldName_) +
+           sizeof(pod);
+}
+
+const uint8_t*
+AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    (cursor = DeserializeName(cx, cursor, &name_)) &&
+    (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
+    return cursor;
+}
+
+bool
+AsmJSModule::Export::clone(JSContext* cx, Export* out) const
+{
+    out->name_ = name_;
+    out->maybeFieldName_ = maybeFieldName_;
+    out->pod = pod;
+    return true;
+}
+
+size_t
+AsmJSModule::serializedSize() const
+{
+    MOZ_ASSERT(isFinished());
+    return wasmModule_->serializedSize() +
+           linkData_->serializedSize() +
+           sizeof(pod) +
+           SerializedVectorSize(globals_) +
+           SerializedPodVectorSize(imports_) +
+           SerializedVectorSize(exports_) +
+           SerializedNameSize(globalArgumentName_) +
+           SerializedNameSize(importArgumentName_) +
+           SerializedNameSize(bufferArgumentName_);
+}
+
+uint8_t*
+AsmJSModule::serialize(uint8_t* cursor) const
+{
+    MOZ_ASSERT(isFinished());
+    cursor = wasmModule_->serialize(cursor);
+    cursor = linkData_->serialize(cursor);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    cursor = SerializeVector(cursor, globals_);
+    cursor = SerializePodVector(cursor, imports_);
+    cursor = SerializeVector(cursor, exports_);
+    cursor = SerializeName(cursor, globalArgumentName_);
+    cursor = SerializeName(cursor, importArgumentName_);
+    cursor = SerializeName(cursor, bufferArgumentName_);
+    return cursor;
+}
+
+const uint8_t*
+AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    linkData_ = cx->make_unique<StaticLinkData>();
+    if (!linkData_)
+        return nullptr;
+
+    // To avoid GC-during-deserialization corner cases, prevent atoms from
+    // being collected.
+    AutoKeepAtoms aka(cx->perThreadData);
+
+    (cursor = Module::deserialize(cx, cursor, &wasmModule_)) &&
+    (cursor = linkData_->deserialize(cx, cursor)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
+    (cursor = DeserializeVector(cx, cursor, &globals_)) &&
+    (cursor = DeserializePodVector(cx, cursor, &imports_)) &&
+    (cursor = DeserializeVector(cx, cursor, &exports_)) &&
+    (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
+    (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
+    (cursor = DeserializeName(cx, cursor, &bufferArgumentName_));
+
+    return cursor;
+}
+
+bool
+AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const
+{
+    auto out = cx->new_<AsmJSModule>(scriptSource(), srcStart_, srcBodyStart_, pod.strict_);
+    if (!out)
+        return false;
+
+    obj->setModule(out);
+
+    out->wasmModule_ = wasmModule_->clone(cx, *linkData_);
+    if (!out->wasmModule_)
+        return false;
+
+    out->linkData_ = cx->make_unique<StaticLinkData>();
+    if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get()))
+        return false;
+
+    out->pod = pod;
+
+    if (!CloneVector(cx, globals_, &out->globals_) ||
+        !ClonePodVector(cx, imports_, &out->imports_) ||
+        !CloneVector(cx, exports_, &out->exports_))
+    {
+        return false;
+    }
+
+    out->globalArgumentName_ = globalArgumentName_;
+    out->importArgumentName_ = importArgumentName_;
+    out->bufferArgumentName_ = bufferArgumentName_;
+    return true;
+}
+
+void
+AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+    if (wasmModule_)
+        wasmModule_->addSizeOfMisc(mallocSizeOf, code, data);
+
+    if (linkData_)
+        *data += linkData_->sizeOfExcludingThis(mallocSizeOf);
+
+    *data += mallocSizeOf(this) +
+             globals_.sizeOfExcludingThis(mallocSizeOf) +
+             imports_.sizeOfExcludingThis(mallocSizeOf) +
+             exports_.sizeOfExcludingThis(mallocSizeOf);
+}
+
+namespace {
+
+struct PropertyNameWrapper
+{
+    PropertyName* name;
+
+    PropertyNameWrapper()
+      : name(nullptr)
+    {}
+    explicit PropertyNameWrapper(PropertyName* name)
+      : name(name)
+    {}
+    size_t serializedSize() const {
+        return SerializedNameSize(name);
+    }
+    uint8_t* serialize(uint8_t* cursor) const {
+        return SerializeName(cursor, name);
+    }
+    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+        return DeserializeName(cx, cursor, &name);
+    }
+};
+
+class ModuleChars
+{
+  protected:
+    uint32_t isFunCtor_;
+    Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
+
+  public:
+    static uint32_t beginOffset(AsmJSParser& parser) {
+        return parser.pc->maybeFunction->pn_pos.begin;
+    }
+
+    static uint32_t endOffset(AsmJSParser& parser) {
+        TokenPos pos(0, 0);  // initialize to silence GCC warning
+        MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos, TokenStream::Operand));
+        return pos.end;
+    }
+};
+
+class ModuleCharsForStore : ModuleChars
+{
+    uint32_t uncompressedSize_;
+    uint32_t compressedSize_;
+    Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
+
+  public:
+    bool init(AsmJSParser& parser) {
+        MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
+
+        uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
+        size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
+        if (maxCompressedSize < uncompressedSize_)
+            return false;
+
+        if (!compressedBuffer_.resize(maxCompressedSize))
+            return false;
+
+        const char16_t* chars = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
+        const char* source = reinterpret_cast<const char*>(chars);
+        size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
+        if (!compressedSize || compressedSize > UINT32_MAX)
+            return false;
+
+        compressedSize_ = compressedSize;
+
+        // For a function statement or named function expression:
+        //   function f(x,y,z) { abc }
+        // the range [beginOffset, endOffset) captures the source:
+        //   f(x,y,z) { abc }
+        // An unnamed function expression captures the same thing, sans 'f'.
+        // Since asm.js modules do not contain any free variables, equality of
+        // [beginOffset, endOffset) is sufficient to guarantee identical code
+        // generation, modulo MachineId.
+        //
+        // For functions created with 'new Function', function arguments are
+        // not present in the source so we must manually explicitly serialize
+        // and match the formals as a Vector of PropertyName.
+        isFunCtor_ = parser.pc->isFunctionConstructorBody();
+        if (isFunCtor_) {
+            unsigned numArgs;
+            ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
+            for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
+                if (!funCtorArgs_.append(arg->name()))
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    size_t serializedSize() const {
+        return sizeof(uint32_t) +
+               sizeof(uint32_t) +
+               compressedSize_ +
+               sizeof(uint32_t) +
+               (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
+    }
+
+    uint8_t* serialize(uint8_t* cursor) const {
+        cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
+        cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
+        cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
+        cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
+        if (isFunCtor_)
+            cursor = SerializeVector(cursor, funCtorArgs_);
+        return cursor;
+    }
+};
+
+class ModuleCharsForLookup : ModuleChars
+{
+    Vector<char16_t, 0, SystemAllocPolicy> chars_;
+
+  public:
+    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+        uint32_t uncompressedSize;
+        cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
+
+        uint32_t compressedSize;
+        cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
+
+        if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
+            return nullptr;
+
+        const char* source = reinterpret_cast<const char*>(cursor);
+        char* dest = reinterpret_cast<char*>(chars_.begin());
+        if (!LZ4::decompress(source, dest, uncompressedSize))
+            return nullptr;
+
+        cursor += compressedSize;
+
+        cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
+        if (isFunCtor_)
+            cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
+
+        return cursor;
+    }
+
+    bool match(AsmJSParser& parser) const {
+        const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
+        const char16_t* parseLimit = parser.tokenStream.rawLimit();
+        MOZ_ASSERT(parseLimit >= parseBegin);
+        if (uint32_t(parseLimit - parseBegin) < chars_.length())
+            return false;
+        if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
+            return false;
+        if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
+            return false;
+        if (isFunCtor_) {
+            // For function statements, the closing } is included as the last
+            // character of the matched source. For Function constructor,
+            // parsing terminates with EOF which we must explicitly check. This
+            // prevents
+            //   new Function('"use asm"; function f() {} return f')
+            // from incorrectly matching
+            //   new Function('"use asm"; function f() {} return ff')
+            if (parseBegin + chars_.length() != parseLimit)
+                return false;
+            unsigned numArgs;
+            ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
+            if (funCtorArgs_.length() != numArgs)
+                return false;
+            for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
+                if (funCtorArgs_[i].name != arg->name())
+                    return false;
+            }
+        }
+        return true;
+    }
+};
+
+} // unnamed namespace
+
+static JS::AsmJSCacheResult
+StoreAsmJSModuleInCache(AsmJSParser& parser, const AsmJSModule& module, ExclusiveContext* cx)
+{
+    MachineId machineId;
+    if (!machineId.extractCurrentState(cx))
+        return JS::AsmJSCache_InternalError;
+
+    ModuleCharsForStore moduleChars;
+    if (!moduleChars.init(parser))
+        return JS::AsmJSCache_InternalError;
+
+    size_t serializedSize = machineId.serializedSize() +
+                            moduleChars.serializedSize() +
+                            module.serializedSize();
+
+    JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
+    if (!open)
+        return JS::AsmJSCache_Disabled_Internal;
+
+    const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
+    const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
+    bool installed = parser.options().installedFile;
+
+    ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
+    JS::AsmJSCacheResult openResult =
+        open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
+    if (openResult != JS::AsmJSCache_Success)
+        return openResult;
+
+    uint8_t* cursor = entry.memory;
+    cursor = machineId.serialize(cursor);
+    cursor = moduleChars.serialize(cursor);
+    cursor = module.serialize(cursor);
+
+    MOZ_ASSERT(cursor == entry.memory + serializedSize);
+    return JS::AsmJSCache_Success;
+}
+
+static bool
+LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
+                         bool* loadedFromCache, UniqueChars* compilationTimeReport)
+{
+    int64_t usecBefore = PRMJ_Now();
+
+    *loadedFromCache = false;
+
+    MachineId machineId;
+    if (!machineId.extractCurrentState(cx))
+        return true;
+
+    JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
+    if (!open)
+        return true;
+
+    const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
+    const char16_t* limit = parser.tokenStream.rawLimit();
+
+    ScopedCacheEntryOpenedForRead entry(cx);
+    if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
+        return true;
+
+    const uint8_t* cursor = entry.memory;
+
+    MachineId cachedMachineId;
+    cursor = cachedMachineId.deserialize(cx, cursor);
+    if (!cursor)
+        return false;
+    if (machineId != cachedMachineId)
+        return true;
+
+    ModuleCharsForLookup moduleChars;
+    cursor = moduleChars.deserialize(cx, cursor);
+    if (!moduleChars.match(parser))
+        return true;
+
+    uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
+    uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
+    bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
+
+    AsmJSModule* module = cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict);
+    if (!module)
+        return false;
+
+    moduleObj->setModule(module);
+
+    cursor = module->deserialize(cx, cursor);
+    if (!cursor)
+        return false;
+
+    bool atEnd = cursor == entry.memory + entry.serializedSize;
+    MOZ_ASSERT(atEnd, "Corrupt cache file");
+    if (!atEnd)
+        return true;
+
+    if (module->wasmModule().compileArgs() != CompileArgs(cx))
+        return true;
+
+    module->staticallyLink(cx);
+
+    if (!parser.tokenStream.advance(module->srcEndBeforeCurly()))
+        return false;
+
+    *loadedFromCache = true;
+
+    int64_t usecAfter = PRMJ_Now();
+    int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
+    *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
+    return true;
+}
+
+/*****************************************************************************/
+// Top-level js::CompileAsmJS
+
+static bool
+NoExceptionPending(ExclusiveContext* cx)
+{
+    return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
+}
+
+static bool
+Warn(AsmJSParser& parser, int errorNumber, const char* str)
+{
+    ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
+                                 errorNumber == JSMSG_USE_ASM_TYPE_FAIL
+                                 ? ParseError
+                                 : ParseWarning;
+    parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
+    return false;
+}
+
+static bool
+EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
+{
+#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
+    return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of a JIT compiler");
+#endif
+
+    if (!cx->jitSupportsFloatingPoint())
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
+
+    if (cx->gcSystemPageSize() != AsmJSPageSize)
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
+
+    switch (parser.options().asmJSOption) {
+      case AsmJSOption::Disabled:
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
+      case AsmJSOption::DisabledByDebugger:
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
+      case AsmJSOption::Enabled:
+        break;
+    }
+
+    if (parser.pc->isGenerator())
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
+
+    if (parser.pc->isArrowFunction())
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
+
+    // Class constructors are also methods
+    if (parser.pc->isMethod())
+        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
+
+    return true;
+}
+
 static UniqueChars
 BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module, unsigned time,
                     const SlowFunctionVector& slowFuncs, JS::AsmJSCacheResult cacheResult)
 {
 #ifndef JS_MORE_DETERMINISTIC
     UniqueChars slowText;
     if (!slowFuncs.empty()) {
         slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length()));
@@ -6994,78 +9010,26 @@ BuildConsoleMessage(ExclusiveContext* cx
 
     return UniqueChars(JS_smprintf("total compilation time %dms; %s%s",
                                    time, cacheString, slowText ? slowText.get() : ""));
 #else
     return make_string_copy("");
 #endif
 }
 
-static bool
-Warn(AsmJSParser& parser, int errorNumber, const char* str)
-{
-    ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
-                                 errorNumber == JSMSG_USE_ASM_TYPE_FAIL
-                                 ? ParseError
-                                 : ParseWarning;
-    parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
-    return false;
-}
-
-static bool
-EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
-{
-#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
-    return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of a JIT compiler");
-#endif
-
-    if (!cx->jitSupportsFloatingPoint())
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
-
-    if (cx->gcSystemPageSize() != AsmJSPageSize)
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
-
-    switch (parser.options().asmJSOption) {
-      case AsmJSOption::Disabled:
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
-      case AsmJSOption::DisabledByDebugger:
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
-      case AsmJSOption::Enabled:
-        break;
-    }
-
-    if (parser.pc->isGenerator())
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
-
-    if (parser.pc->isArrowFunction())
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
-
-    // Class constructors are also methods
-    if (parser.pc->isMethod())
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
-
-    return true;
-}
-
-static bool
-NoExceptionPending(ExclusiveContext* cx)
-{
-    return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
-}
-
 bool
-js::ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
+js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
 {
     *validated = false;
 
     // Various conditions disable asm.js optimizations.
     if (!EstablishPreconditions(cx, parser))
         return NoExceptionPending(cx);
 
-    Rooted<AsmJSModuleObject*> moduleObj(cx, AsmJSModuleObject::create(cx));
+    Rooted<AsmJSModuleObject*> moduleObj(cx, NewAsmJSModuleObject(cx));
     if (!moduleObj)
         return false;
 
     // Before spending any time parsing the module, try to look it up in the
     // embedding's cache using the chars about to be parsed as the key.
     bool loadedFromCache;
     UniqueChars message;
     if (!LookupAsmJSModuleInCache(cx, parser, moduleObj, &loadedFromCache, &message))
@@ -7093,33 +9057,57 @@ js::ValidateAsmJS(ExclusiveContext* cx, 
         message = BuildConsoleMessage(cx, module, time, slowFuncs, cacheResult);
         if (!message)
             return NoExceptionPending(cx);
     }
 
     // The module function dynamically links the AsmJSModule when called and
     // generates a set of functions wrapping all the exports.
     FunctionBox* funbox = parser.pc->maybeFunction->pn_funbox;
-    RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj));
+    RootedFunction moduleFun(cx, NewModuleFunction(cx, funbox->function(), moduleObj));
     if (!moduleFun)
         return false;
 
     // Finished! Clobber the default function created by the parser with the new
     // asm.js module function. Special cases in the bytecode emitter avoid
     // generating bytecode for asm.js functions, allowing this asm.js module
     // function to be the finished result.
     MOZ_ASSERT(funbox->function()->isInterpreted());
     funbox->object = moduleFun;
 
     // Success! Write to the console with a "warning" message.
     *validated = true;
     Warn(parser, JSMSG_USE_ASM_TYPE_OK, message.get());
     return NoExceptionPending(cx);
 }
 
+/*****************************************************************************/
+// asm.js module/export queries
+
+bool
+js::IsAsmJSModuleNative(Native native)
+{
+    return native == LinkAsmJS;
+}
+
+bool
+js::IsAsmJSModule(JSFunction* fun)
+{
+    return fun->isNative() && fun->maybeNative() == LinkAsmJS;
+}
+
+bool
+js::IsAsmJSFunction(JSFunction* fun)
+{
+    return fun->isNative() && fun->maybeNative() == CallAsmJS;
+}
+
+/*****************************************************************************/
+// asm.js testing natives:
+
 bool
 js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // See EstablishPreconditions.
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     bool available = false;
@@ -7128,8 +9116,288 @@ js::IsAsmJSCompilationAvailable(JSContex
                      cx->gcSystemPageSize() == AsmJSPageSize &&
                      cx->runtime()->options().asmJS();
 #endif
 
     args.rval().set(BooleanValue(available));
     return true;
 }
 
+static bool
+IsMaybeWrappedNativeFunction(const Value& v, Native native, JSFunction** fun = nullptr)
+{
+    if (!v.isObject())
+        return false;
+
+    JSObject* obj = CheckedUnwrap(&v.toObject());
+    if (!obj)
+        return false;
+
+    if (!obj->is<JSFunction>())
+        return false;
+
+    if (fun)
+        *fun = &obj->as<JSFunction>();
+
+    return obj->as<JSFunction>().maybeNative() == native;
+}
+
+bool
+js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
+    args.rval().set(BooleanValue(rval));
+    return true;
+}
+
+bool
+js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
+    args.rval().set(BooleanValue(rval));
+    return true;
+}
+
+bool
+js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSFunction* fun;
+    if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
+                             "argument passed to isAsmJSModuleLoadedFromCache is not a "
+                             "validated asm.js module");
+        return false;
+    }
+
+    bool loadedFromCache = FunctionToModuleObject(fun).module().wasmModule().loadedFromCache();
+
+    args.rval().set(BooleanValue(loadedFromCache));
+    return true;
+}
+
+/*****************************************************************************/
+// asm.js toString/toSource support
+
+static bool
+AppendUseStrictSource(JSContext* cx, HandleFunction fun, Handle<JSFlatString*> src, StringBuffer& out)
+{
+    // We need to add "use strict" in the body right after the opening
+    // brace.
+    size_t bodyStart = 0, bodyEnd;
+
+    // No need to test for functions created with the Function ctor as
+    // these don't implicitly inherit the "use strict" context. Strict mode is
+    // enabled for functions created with the Function ctor only if they begin with
+    // the "use strict" directive, but these functions won't validate as asm.js
+    // modules.
+
+    if (!FindBody(cx, fun, src, &bodyStart, &bodyEnd))
+        return false;
+
+    return out.appendSubstring(src, 0, bodyStart) &&
+           out.append("\n\"use strict\";\n") &&
+           out.appendSubstring(src, bodyStart, src->length() - bodyStart);
+}
+
+JSString*
+js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
+{
+    AsmJSModule& module = FunctionToModuleObject(fun).module();
+
+    uint32_t begin = module.srcStart();
+    uint32_t end = module.srcEndAfterCurly();
+    ScriptSource* source = module.scriptSource();
+    StringBuffer out(cx);
+
+    if (addParenToLambda && fun->isLambda() && !out.append("("))
+        return nullptr;
+
+    if (!out.append("function "))
+        return nullptr;
+
+    if (fun->atom() && !out.append(fun->atom()))
+        return nullptr;
+
+    bool haveSource = source->hasSourceData();
+    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+        return nullptr;
+
+    if (!haveSource) {
+        if (!out.append("() {\n    [sourceless code]\n}"))
+            return nullptr;
+    } else {
+        // Whether the function has been created with a Function ctor
+        bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
+        if (funCtor) {
+            // Functions created with the function constructor don't have arguments in their source.
+            if (!out.append("("))
+                return nullptr;
+
+            if (PropertyName* argName = module.globalArgumentName()) {
+                if (!out.append(argName))
+                    return nullptr;
+            }
+            if (PropertyName* argName = module.importArgumentName()) {
+                if (!out.append(", ") || !out.append(argName))
+                    return nullptr;
+            }
+            if (PropertyName* argName = module.bufferArgumentName()) {
+                if (!out.append(", ") || !out.append(argName))
+                    return nullptr;
+            }
+
+            if (!out.append(") {\n"))
+                return nullptr;
+        }
+
+        Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+        if (!src)
+            return nullptr;
+
+        if (module.strict()) {
+            if (!AppendUseStrictSource(cx, fun, src, out))
+                return nullptr;
+        } else {
+            if (!out.append(src))
+                return nullptr;
+        }
+
+        if (funCtor && !out.append("\n}"))
+            return nullptr;
+    }
+
+    if (addParenToLambda && fun->isLambda() && !out.append(")"))
+        return nullptr;
+
+    return out.finishString();
+}
+
+JSString*
+js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
+{
+    AsmJSModule& module = FunctionToModuleObject(fun).module();
+    const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)];
+    uint32_t begin = module.srcStart() + f.startOffsetInModule();
+    uint32_t end = module.srcStart() + f.endOffsetInModule();
+
+    ScriptSource* source = module.scriptSource();
+    StringBuffer out(cx);
+
+    if (!out.append("function "))
+        return nullptr;
+
+    bool haveSource = source->hasSourceData();
+    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+        return nullptr;
+
+    if (!haveSource) {
+        // asm.js functions can't be anonymous
+        MOZ_ASSERT(fun->atom());
+        if (!out.append(fun->atom()))
+            return nullptr;
+        if (!out.append("() {\n    [sourceless code]\n}"))
+            return nullptr;
+    } else {
+        // asm.js functions cannot have been created with a Function constructor
+        // as they belong within a module.
+        MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
+
+        if (module.strict()) {
+            // AppendUseStrictSource expects its input to start right after the
+            // function name, so split the source chars from the src into two parts:
+            // the function name and the rest (arguments + body).
+
+            // asm.js functions can't be anonymous
+            MOZ_ASSERT(fun->atom());
+            if (!out.append(fun->atom()))
+                return nullptr;
+
+            size_t nameEnd = begin + fun->atom()->length();
+            Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end));
+            if (!src || !AppendUseStrictSource(cx, fun, src, out))
+                return nullptr;
+        } else {
+            Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+            if (!src)
+                return nullptr;
+            if (!out.append(src))
+                return nullptr;
+        }
+    }
+
+    return out.finishString();
+}
+
+/*****************************************************************************/
+// asm.js heap
+
+static const size_t MinHeapLength = 64 * 1024;
+static_assert(MinHeapLength % AsmJSPageSize == 0, "Invalid page size");
+
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+
+// Targets define AsmJSImmediateRange to be the size of an address immediate,
+// and AsmJSCheckedImmediateRange, to be the size of an address immediate that
+// can be supported by signal-handler OOB handling.
+static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
+              "AsmJSImmediateRange should be the size of an unconstrained "
+              "address immediate");
+
+// To support the use of signal handlers for catching Out Of Bounds accesses,
+// the internal ArrayBuffer data array is inflated to 4GiB (only the
+// byteLength portion of which is accessible) so that out-of-bounds accesses
+// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
+// Then, an additional extent is added to permit folding of immediate
+// values into addresses. And finally, unaligned accesses and mask optimizations
+// might also try to access a few bytes after this limit, so just inflate it by
+// AsmJSPageSize.
+const size_t js::AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
+                                   jit::AsmJSImmediateRange +
+                                   AsmJSPageSize;
+
+#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
+
+// From the asm.js spec Linking section:
+//  the heap object's byteLength must be either
+//    2^n for n in [12, 24)
+//  or
+//    2^24 * n for n >= 1.
+
+bool
+js::IsValidAsmJSHeapLength(uint32_t length)
+{
+    bool valid = length >= MinHeapLength &&
+                 (IsPowerOfTwo(length) ||
+                  (length & 0x00ffffff) == 0);
+
+    MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
+    MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
+
+    return valid;
+}
+
+uint32_t
+js::RoundUpToNextValidAsmJSHeapLength(uint32_t length)
+{
+    if (length <= MinHeapLength)
+        return MinHeapLength;
+
+    if (length <= 16 * 1024 * 1024)
+        return mozilla::RoundUpPow2(length);
+
+    MOZ_ASSERT(length <= 0xff000000);
+    return (length + 0x00ffffff) & ~0x00ffffff;
+}
+
+bool
+js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
+{
+    for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
+        if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
+            return false;
+    }
+    return true;
+}
+
rename from js/src/asmjs/AsmJSValidate.h
rename to js/src/asmjs/AsmJS.h
--- a/js/src/asmjs/AsmJSValidate.h
+++ b/js/src/asmjs/AsmJS.h
@@ -11,117 +11,118 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#ifndef asmjs_AsmJSValidate_h
-#define asmjs_AsmJSValidate_h
-
-#include "mozilla/MathAlgorithms.h"
+#ifndef asmjs_asmjs_h
+#define asmjs_asmjs_h
 
-#include <stddef.h>
-
-#include "jsutil.h"
-
-#include "jit/Registers.h"
-#include "js/TypeDecls.h"
+#include "vm/NativeObject.h"
 
 namespace js {
 
+class AsmJSModule;
 class ExclusiveContext;
 namespace frontend {
     template <typename ParseHandler> class Parser;
     template <typename ParseHandler> struct ParseContext;
     class FullParseHandler;
     class ParseNode;
-} // namespace frontend
-
+}
 typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
 typedef frontend::ParseContext<frontend::FullParseHandler> AsmJSParseContext;
 
-// Takes over parsing of a function starting with "use asm". The return value
-// indicates whether an error was reported which the caller should propagate.
-// If no error was reported, the function may still fail to validate as asm.js.
-// In this case, the parser.tokenStream has been advanced an indeterminate
-// amount and the entire function should be reparsed from the beginning.
+// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
+// directly to user script) which traces and owns an AsmJSModule. The
+// AsmJSModuleObject is referenced by the extended slots of the content-visible
+// module and export JSFunctions.
+
+class AsmJSModuleObject : public NativeObject
+{
+    static const unsigned MODULE_SLOT = 0;
+
+  public:
+    static const unsigned RESERVED_SLOTS = 1;
+
+    bool hasModule() const;
+    void setModule(AsmJSModule* module);
+    AsmJSModule& module() const;
+
+    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+
+    static const Class class_;
+};
+
+typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
+
+// This function takes over parsing of a function starting with "use asm". The
+// return value indicates whether an error was reported which the caller should
+// propagate. If no error was reported, the function may still fail to validate
+// as asm.js. In this case, the parser.tokenStream has been advanced an
+// indeterminate amount and the entire function should be reparsed from the
+// beginning.
+
 extern bool
-ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
+CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
              bool* validated);
 
-// The minimum heap length for asm.js.
-const size_t AsmJSMinHeapLength = 64 * 1024;
-
-// The assumed page size; dynamically checked in ValidateAsmJS.
-#ifdef _MIPS_ARCH_LOONGSON3A
-const size_t AsmJSPageSize = 16384;
-#else
-const size_t AsmJSPageSize = 4096;
-#endif
-
-static_assert(AsmJSMinHeapLength % AsmJSPageSize == 0, "Invalid page size");
-
-#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+// asm.js module/export queries:
 
-// Targets define AsmJSImmediateRange to be the size of an address immediate,
-// and AsmJSCheckedImmediateRange, to be the size of an address immediate that
-// can be supported by signal-handler OOB handling.
-static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
-              "AsmJSImmediateRange should be the size of an unconstrained "
-              "address immediate");
-
-// To support the use of signal handlers for catching Out Of Bounds accesses,
-// the internal ArrayBuffer data array is inflated to 4GiB (only the
-// byteLength portion of which is accessible) so that out-of-bounds accesses
-// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
-// Then, an additional extent is added to permit folding of immediate
-// values into addresses. And finally, unaligned accesses and mask optimizations
-// might also try to access a few bytes after this limit, so just inflate it by
-// AsmJSPageSize.
-static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
-                                      jit::AsmJSImmediateRange +
-                                      AsmJSPageSize;
-
-#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+extern bool
+IsAsmJSModuleNative(JSNative native);
 
-// From the asm.js spec Linking section:
-//  the heap object's byteLength must be either
-//    2^n for n in [12, 24)
-//  or
-//    2^24 * n for n >= 1.
-
-inline uint32_t
-RoundUpToNextValidAsmJSHeapLength(uint32_t length)
-{
-    if (length <= AsmJSMinHeapLength)
-        return AsmJSMinHeapLength;
-
-    if (length <= 16 * 1024 * 1024)
-        return mozilla::RoundUpPow2(length);
+extern bool
+IsAsmJSModule(JSFunction* fun);
 
-    MOZ_ASSERT(length <= 0xff000000);
-    return (length + 0x00ffffff) & ~0x00ffffff;
-}
+extern bool
+IsAsmJSFunction(JSFunction* fun);
 
-inline bool
-IsValidAsmJSHeapLength(uint32_t length)
-{
-    bool valid = length >= AsmJSMinHeapLength &&
-                 (IsPowerOfTwo(length) ||
-                  (length & 0x00ffffff) == 0);
+// asm.js testing natives:
 
-    MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
-    MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
-
-    return valid;
-}
-
-// Return whether asm.js optimization is inhibited by the platform or
-// dynamically disabled:
 extern bool
 IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
 
+extern bool
+IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
+
+extern bool
+IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// asm.js toString/toSource support:
+
+extern JSString*
+AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
+
+extern JSString*
+AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
+
+// asm.js heap:
+
+extern bool
+IsValidAsmJSHeapLength(uint32_t length);
+
+extern uint32_t
+RoundUpToNextValidAsmJSHeapLength(uint32_t length);
+
+extern bool
+OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
+
+// The assumed page size; dynamically checked in CompileAsmJS.
+#ifdef _MIPS_ARCH_LOONGSON3A
+static const size_t AsmJSPageSize = 16384;
+#else
+static const size_t AsmJSPageSize = 4096;
+#endif
+
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+extern const size_t AsmJSMappedSize;
+#endif
+
 } // namespace js
 
-#endif // asmjs_AsmJSValidate_h
+#endif // asmjs_asmjs_h
deleted file mode 100644
--- a/js/src/asmjs/AsmJSLink.cpp
+++ /dev/null
@@ -1,1218 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asmjs/AsmJSLink.h"
-
-#include "mozilla/PodOperations.h"
-
-#include "jscntxt.h"
-#include "jsmath.h"
-#include "jsprf.h"
-#include "jswrapper.h"
-
-#include "asmjs/AsmJSModule.h"
-#include "builtin/AtomicsObject.h"
-#include "builtin/SIMD.h"
-#include "frontend/BytecodeCompiler.h"
-#include "jit/Ion.h"
-#include "jit/JitCommon.h"
-#include "vm/ArrayBufferObject.h"
-#include "vm/SharedArrayObject.h"
-#include "vm/StringBuffer.h"
-
-#include "jsobjinlines.h"
-
-#include "vm/ArrayBufferObject-inl.h"
-#include "vm/NativeObject-inl.h"
-
-using namespace js;
-using namespace js::jit;
-using namespace js::wasm;
-
-using mozilla::IsNaN;
-using mozilla::PodZero;
-
-static bool
-LinkFail(JSContext* cx, const char* str)
-{
-    JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
-                                 nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
-    return false;
-}
-
-static bool
-GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
-{
-    if (!objVal.isObject())
-        return LinkFail(cx, "accessing property of non-object");
-
-    RootedObject obj(cx, &objVal.toObject());
-    if (IsScriptedProxy(obj))
-        return LinkFail(cx, "accessing property of a Proxy");
-
-    Rooted<PropertyDescriptor> desc(cx);
-    RootedId id(cx, NameToId(field));
-    if (!GetPropertyDescriptor(cx, obj, id, &desc))
-        return false;
-
-    if (!desc.object())
-        return LinkFail(cx, "property not present on object");
-
-    if (!desc.isDataDescriptor())
-        return LinkFail(cx, "property is not a data property");
-
-    v.set(desc.value());
-    return true;
-}
-
-static bool
-HasPureCoercion(JSContext* cx, HandleValue v)
-{
-    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v))
-        return true;
-
-    // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
-    // bug that generates code that passes functions for some imports. To avoid
-    // breaking all the code that contains this bug, we make an exception for
-    // functions that don't have user-defined valueOf or toString, for their
-    // coercions are not observable and coercion via ToNumber/ToInt32
-    // definitely produces NaN/0. We should remove this special case later once
-    // most apps have been built with newer Emscripten.
-    jsid toString = NameToId(cx->names().toString);
-    if (v.toObject().is<JSFunction>() &&
-        HasObjectValueOf(&v.toObject(), cx) &&
-        ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
-    {
-        return true;
-    }
-
-    return false;
-}
-
-static bool
-ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
-                       HandleValue importVal)
-{
-    void* datum = globalData + global.varGlobalDataOffset();
-
-    switch (global.varInitKind()) {
-      case AsmJSModule::Global::InitConstant: {
-        Val v = global.varInitVal();
-        switch (v.type()) {
-          case ValType::I32:
-            *(int32_t*)datum = v.i32();
-            break;
-          case ValType::I64:
-            MOZ_CRASH("int64");
-          case ValType::F32:
-            *(float*)datum = v.f32();
-            break;
-          case ValType::F64:
-            *(double*)datum = v.f64();
-            break;
-          case ValType::I32x4:
-          case ValType::B32x4:
-            // Bool32x4 uses the same data layout as Int32x4.
-            memcpy(datum, v.i32x4(), Simd128DataSize);
-            break;
-          case ValType::F32x4:
-            memcpy(datum, v.f32x4(), Simd128DataSize);
-            break;
-        }
-        break;
-      }
-
-      case AsmJSModule::Global::InitImport: {
-        RootedPropertyName field(cx, global.varImportField());
-        RootedValue v(cx);
-        if (!GetDataProperty(cx, importVal, field, &v))
-            return false;
-
-        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
-            return LinkFail(cx, "Imported values must be primitives");
-
-        switch (global.varInitImportType()) {
-          case ValType::I32:
-            if (!ToInt32(cx, v, (int32_t*)datum))
-                return false;
-            break;
-          case ValType::I64:
-            MOZ_CRASH("int64");
-          case ValType::F32:
-            if (!RoundFloat32(cx, v, (float*)datum))
-                return false;
-            break;
-          case ValType::F64:
-            if (!ToNumber(cx, v, (double*)datum))
-                return false;
-            break;
-          case ValType::I32x4: {
-            SimdConstant simdConstant;
-            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
-                return false;
-            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
-            break;
-          }
-          case ValType::F32x4: {
-            SimdConstant simdConstant;
-            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
-                return false;
-            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
-            break;
-          }
-          case ValType::B32x4: {
-            SimdConstant simdConstant;
-            if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
-                return false;
-            // Bool32x4 uses the same data layout as Int32x4.
-            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
-            break;
-          }
-        }
-        break;
-      }
-    }
-
-    return true;
-}
-
-static bool
-ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
-            AutoVectorRooter<JSFunction*>* ffis)
-{
-    RootedPropertyName field(cx, global.ffiField());
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, importVal, field, &v))
-        return false;
-
-    if (!v.isObject() || !v.toObject().is<JSFunction>())
-        return LinkFail(cx, "FFI imports must be functions");
-
-    (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
-    return true;
-}
-
-static bool
-ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    RootedPropertyName field(cx, global.maybeViewName());
-    if (!field)
-        return true;
-
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, field, &v))
-        return false;
-
-    bool tac = IsTypedArrayConstructor(v, global.viewType());
-    if (!tac)
-        return LinkFail(cx, "bad typed array constructor");
-
-    return true;
-}
-
-static bool
-ValidateByteLength(JSContext* cx, HandleValue globalVal)
-{
-    RootedPropertyName field(cx, cx->names().byteLength);
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, field, &v))
-        return false;
-
-    if (!v.isObject() || !v.toObject().isBoundFunction())
-        return LinkFail(cx, "byteLength must be a bound function object");
-
-    RootedFunction fun(cx, &v.toObject().as<JSFunction>());
-
-    RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
-    if (!IsNativeFunction(boundTarget, fun_call))
-        return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
-
-    RootedValue boundThis(cx, fun->getBoundFunctionThis());
-    if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
-        return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
-
-    return true;
-}
-
-static bool
-ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
-        return false;
-
-    RootedPropertyName field(cx, global.mathName());
-    if (!GetDataProperty(cx, v, field, &v))
-        return false;
-
-    Native native = nullptr;
-    switch (global.mathBuiltinFunction()) {
-      case AsmJSMathBuiltin_sin: native = math_sin; break;
-      case AsmJSMathBuiltin_cos: native = math_cos; break;
-      case AsmJSMathBuiltin_tan: native = math_tan; break;
-      case AsmJSMathBuiltin_asin: native = math_asin; break;
-      case AsmJSMathBuiltin_acos: native = math_acos; break;
-      case AsmJSMathBuiltin_atan: native = math_atan; break;
-      case AsmJSMathBuiltin_ceil: native = math_ceil; break;
-      case AsmJSMathBuiltin_floor: native = math_floor; break;
-      case AsmJSMathBuiltin_exp: native = math_exp; break;
-      case AsmJSMathBuiltin_log: native = math_log; break;
-      case AsmJSMathBuiltin_pow: native = math_pow; break;
-      case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
-      case AsmJSMathBuiltin_min: native = math_min; break;
-      case AsmJSMathBuiltin_max: native = math_max; break;
-      case AsmJSMathBuiltin_abs: native = math_abs; break;
-      case AsmJSMathBuiltin_atan2: native = math_atan2; break;
-      case AsmJSMathBuiltin_imul: native = math_imul; break;
-      case AsmJSMathBuiltin_clz32: native = math_clz32; break;
-      case AsmJSMathBuiltin_fround: native = math_fround; break;
-    }
-
-    if (!IsNativeFunction(v, native))
-        return LinkFail(cx, "bad Math.* builtin function");
-
-    return true;
-}
-
-static PropertyName*
-SimdTypeToName(JSContext* cx, AsmJSSimdType type)
-{
-    switch (type) {
-      case AsmJSSimdType_int32x4:   return cx->names().int32x4;
-      case AsmJSSimdType_float32x4: return cx->names().float32x4;
-      case AsmJSSimdType_bool32x4:  return cx->names().bool32x4;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
-}
-
-static SimdTypeDescr::Type
-AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
-{
-    switch (type) {
-      case AsmJSSimdType_int32x4: return Int32x4::type;
-      case AsmJSSimdType_float32x4: return Float32x4::type;
-      case AsmJSSimdType_bool32x4: return Bool32x4::type;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
-}
-
-static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
-                 MutableHandleValue out)
-{
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
-        return false;
-
-    AsmJSSimdType type;
-    if (global.which() == AsmJSModule::Global::SimdCtor)
-        type = global.simdCtorType();
-    else
-        type = global.simdOperationType();
-
-    RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
-    if (!GetDataProperty(cx, v, simdTypeName, &v))
-        return false;
-
-    if (!v.isObject())
-        return LinkFail(cx, "bad SIMD type");
-
-    RootedObject simdDesc(cx, &v.toObject());
-    if (!simdDesc->is<SimdTypeDescr>())
-        return LinkFail(cx, "bad SIMD type");
-
-    if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
-        return LinkFail(cx, "bad SIMD type");
-
-    out.set(v);
-    return true;
-}
-
-static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    RootedValue _(cx);
-    return ValidateSimdType(cx, global, globalVal, &_);
-}
-
-static bool
-ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    // SIMD operations are loaded from the SIMD type, so the type must have been
-    // validated before the operation.
-    RootedValue v(cx);
-    JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
-
-    RootedPropertyName opName(cx, global.simdOperationName());
-    if (!GetDataProperty(cx, v, opName, &v))
-        return false;
-
-    Native native = nullptr;
-    switch (global.simdOperationType()) {
-#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
-#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
-#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break;
-#define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
-      case AsmJSSimdType_int32x4:
-        switch (global.simdOperation()) {
-          FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
-          default:
-            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
-                                                    "place");
-        }
-        break;
-      case AsmJSSimdType_float32x4:
-        switch (global.simdOperation()) {
-          FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)
-          default:
-             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
-                                                     "place");
-        }
-        break;
-      case AsmJSSimdType_bool32x4:
-        switch (global.simdOperation()) {
-          FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4)
-          default:
-             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
-                                                     "place");
-        }
-        break;
-#undef FALLTHROUGH
-#undef SET_NATIVE_FLOAT32X4
-#undef SET_NATIVE_INT32X4
-#undef SET_NATIVE_BOOL32X4
-#undef SET_NATIVE
-    }
-    if (!native || !IsNativeFunction(v, native))
-        return LinkFail(cx, "bad SIMD.type.* operation");
-    return true;
-}
-
-static bool
-ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
-        return false;
-    RootedPropertyName field(cx, global.atomicsName());
-    if (!GetDataProperty(cx, v, field, &v))
-        return false;
-
-    Native native = nullptr;
-    switch (global.atomicsBuiltinFunction()) {
-      case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
-      case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
-      case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
-      case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
-      case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break;
-      case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
-      case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
-      case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
-      case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
-      case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
-      case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
-    }
-
-    if (!IsNativeFunction(v, native))
-        return LinkFail(cx, "bad Atomics.* builtin function");
-
-    return true;
-}
-
-static bool
-ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
-    RootedPropertyName field(cx, global.constantName());
-    RootedValue v(cx, globalVal);
-
-    if (global.constantKind() == AsmJSModule::Global::MathConstant) {
-        if (!GetDataProperty(cx, v, cx->names().Math, &v))
-            return false;
-    }
-
-    if (!GetDataProperty(cx, v, field, &v))
-        return false;
-
-    if (!v.isNumber())
-        return LinkFail(cx, "math / global constant value needs to be a number");
-
-    // NaN != NaN
-    if (IsNaN(global.constantValue())) {
-        if (!IsNaN(v.toNumber()))
-            return LinkFail(cx, "global constant value needs to be NaN");
-    } else {
-        if (v.toNumber() != global.constantValue())
-            return LinkFail(cx, "global constant value mismatch");
-    }
-
-    return true;
-}
-
-static bool
-CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
-            MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
-{
-    if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
-        return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
-
-    if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
-        return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
-
-    buffer.set(&AsAnyArrayBuffer(bufferVal));
-    uint32_t heapLength = buffer->byteLength();
-
-    if (!IsValidAsmJSHeapLength(heapLength)) {
-        UniqueChars msg(
-            JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
-                        "valid length is 0x%x",
-                        heapLength,
-                        RoundUpToNextValidAsmJSHeapLength(heapLength)));
-        return LinkFail(cx, msg.get());
-    }
-
-    // This check is sufficient without considering the size of the loaded datum because heap
-    // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
-    MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
-    if (heapLength < module.minHeapLength()) {
-        UniqueChars msg(
-            JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
-                        "by const heap accesses and/or change-heap minimum-length requirements).",
-                        heapLength,
-                        module.minHeapLength()));
-        return LinkFail(cx, msg.get());
-    }
-
-    if (heapLength > module.maxHeapLength()) {
-        UniqueChars msg(
-            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
-                        heapLength,
-                        module.maxHeapLength()));
-        return LinkFail(cx, msg.get());
-    }
-
-    // Shell builtins may have disabled signal handlers since the module we're
-    // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
-    // as well for the caching case.
-    if (module.wasm().compileArgs() != CompileArgs(cx))
-        return LinkFail(cx, "Signals have been toggled since compilation");
-
-    if (buffer->is<ArrayBufferObject>()) {
-        Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
-        bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
-        if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
-            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
-    }
-
-    return true;
-}
-
-static bool
-DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
-{
-    HandleValue globalVal = args.get(0);
-    HandleValue importVal = args.get(1);
-    HandleValue bufferVal = args.get(2);
-
-    Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
-    if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer))
-        return false;
-
-    AutoVectorRooter<JSFunction*> ffis(cx);
-    if (!ffis.resize(module.numFFIs()))
-        return false;
-
-    for (const AsmJSModule::Global& global : module.globals()) {
-        switch (global.which()) {
-          case AsmJSModule::Global::Variable:
-            if (!ValidateGlobalVariable(cx, global, module.wasm().globalData(), importVal))
-                return false;
-            break;
-          case AsmJSModule::Global::FFI:
-            if (!ValidateFFI(cx, global, importVal, &ffis))
-                return false;
-            break;
-          case AsmJSModule::Global::ArrayView:
-          case AsmJSModule::Global::ArrayViewCtor:
-            if (!ValidateArrayView(cx, global, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::ByteLength:
-            if (!ValidateByteLength(cx, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::MathBuiltinFunction:
-            if (!ValidateMathBuiltinFunction(cx, global, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::AtomicsBuiltinFunction:
-            if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::Constant:
-            if (!ValidateConstant(cx, global, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::SimdCtor:
-            if (!ValidateSimdType(cx, global, globalVal))
-                return false;
-            break;
-          case AsmJSModule::Global::SimdOperation:
-            if (!ValidateSimdOperation(cx, global, globalVal))
-                return false;
-            break;
-        }
-    }
-
-    AutoVectorRooter<JSFunction*> imports(cx);
-    for (const AsmJSModule::Import& import : module.imports()) {
-        if (!imports.append(ffis[import.ffiIndex()]))
-            return false;
-    }
-
-    return module.wasm().dynamicallyLink(cx, buffer, imports);
-}
-
-static bool
-ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
-{
-    HandleValue bufferArg = args.get(0);
-    if (!IsArrayBuffer(bufferArg)) {
-        ReportIncompatible(cx, args);
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
-    uint32_t heapLength = newBuffer->byteLength();
-    if (heapLength & module.heapLengthMask() ||
-        heapLength < module.minHeapLength() ||
-        heapLength > module.maxHeapLength())
-    {
-        args.rval().set(BooleanValue(false));
-        return true;
-    }
-
-    if (!module.hasArrayView()) {
-        args.rval().set(BooleanValue(true));
-        return true;
-    }
-
-    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
-
-    bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
-    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
-        return false;
-
-    args.rval().set(BooleanValue(module.wasm().changeHeap(newBuffer, cx)));
-    return true;
-}
-
-// An asm.js function stores, in its extended slots:
-//  - a pointer to the module from which it was returned
-//  - its index in the ordered list of exported functions
-static const unsigned ASM_MODULE_SLOT = 0;
-static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
-
-static unsigned
-FunctionToExportIndex(HandleFunction fun)
-{
-    MOZ_ASSERT(IsAsmJSFunction(fun));
-    Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
-    return v.toInt32();
-}
-
-static AsmJSModule&
-FunctionToEnclosingModule(HandleFunction fun)
-{
-    return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
-}
-
-// This is the js::Native for functions exported by an asm.js module.
-static bool
-CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs callArgs = CallArgsFromVp(argc, vp);
-    RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());
-
-    // The heap-changing function is a special-case and is implemented by C++.
-    AsmJSModule& asmJSModule = FunctionToEnclosingModule(callee);
-    const AsmJSModule::Export& asmJSFunc = asmJSModule.exports()[FunctionToExportIndex(callee)];
-    if (asmJSFunc.isChangeHeap())
-        return ChangeHeap(cx, asmJSModule, callArgs);
-
-    Module& module = asmJSModule.wasm();
-    const Export& func = module.exports()[asmJSFunc.wasmIndex()];
-
-    // Enable/disable profiling in the asm.js module to match the current global
-    // profiling state. Don't do this if the module is already active on the
-    // stack since this would leave the module in a state where profiling is
-    // enabled but the stack isn't unwindable.
-    if (module.profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !module.active())
-        module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
-
-    // The calling convention for an external call into asm.js is to pass an
-    // array of 16-byte values where each value contains either a coerced int32
-    // (in the low word), a double value (in the low dword) or a SIMD vector
-    // value, with the coercions specified by the asm.js signature. The
-    // external entry point unpacks this array into the system-ABI-specified
-    // registers and stack memory and then calls into the internal entry point.
-    // The return value is stored in the first element of the array (which,
-    // therefore, must have length >= 1).
-    Vector<Module::EntryArg, 8> coercedArgs(cx);
-    if (!coercedArgs.resize(Max<size_t>(1, func.sig().args().length())))
-        return false;
-
-    RootedValue v(cx);
-    for (unsigned i = 0; i < func.sig().args().length(); ++i) {
-        v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
-        switch (func.sig().arg(i)) {
-          case ValType::I32:
-            if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
-                return false;
-            break;
-          case ValType::I64:
-            MOZ_CRASH("int64");
-          case ValType::F32:
-            if (!RoundFloat32(cx, v, (float*)&coercedArgs[i]))
-                return false;
-            break;
-          case ValType::F64:
-            if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
-                return false;
-            break;
-          case ValType::I32x4: {
-            SimdConstant simd;
-            if (!ToSimdConstant<Int32x4>(cx, v, &simd))
-                return false;
-            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
-            break;
-          }
-          case ValType::F32x4: {
-            SimdConstant simd;
-            if (!ToSimdConstant<Float32x4>(cx, v, &simd))
-                return false;
-            memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
-            break;
-          }
-          case ValType::B32x4: {
-            SimdConstant simd;
-            if (!ToSimdConstant<Bool32x4>(cx, v, &simd))
-                return false;
-            // Bool32x4 uses the same representation as Int32x4.
-            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
-            break;
-          }
-        }
-    }
-
-    // The correct way to handle this situation would be to allocate a new range
-    // of PROT_NONE memory and module.changeHeap to this memory. That would
-    // cause every access to take the out-of-bounds signal-handler path which
-    // does the right thing. For now, just throw an out-of-memory exception
-    // since these can technically pop out anywhere and the full fix may
-    // actually OOM when trying to allocate the PROT_NONE memory.
-    if (module.hasDetachedHeap()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
-        return false;
-    }
-
-    {
-        // Push a WasmActivation to describe the asm.js frames we're about to
-        // push when running this module. Additionally, push a JitActivation so
-        // that the optimized asm.js-to-Ion FFI call path (which we want to be
-        // very fast) can avoid doing so. The JitActivation is marked as
-        // inactive so stack iteration will skip over it.
-        WasmActivation activation(cx, module);
-        JitActivation jitActivation(cx, /* active */ false);
-
-        // Call the per-exported-function trampoline created by GenerateEntry.
-        Module::EntryFuncPtr enter = module.entryTrampoline(func);
-        if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData()))
-            return false;
-    }
-
-    if (callArgs.isConstructing()) {
-        // By spec, when a function is called as a constructor and this function
-        // returns a primary type, which is the case for all asm.js exported
-        // functions, the returned value is discarded and an empty object is
-        // returned instead.
-        PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx);
-        if (!obj)
-            return false;
-        callArgs.rval().set(ObjectValue(*obj));
-        return true;
-    }
-
-    JSObject* simdObj;
-    switch (func.sig().ret()) {
-      case ExprType::Void:
-        callArgs.rval().set(UndefinedValue());
-        break;
-      case ExprType::I32:
-        callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
-        break;
-      case ExprType::I64:
-        MOZ_CRASH("int64");
-      case ExprType::F32:
-      case ExprType::F64:
-        callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
-        break;
-      case ExprType::I32x4:
-        simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]);
-        if (!simdObj)
-            return false;
-        callArgs.rval().set(ObjectValue(*simdObj));
-        break;
-      case ExprType::F32x4:
-        simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
-        if (!simdObj)
-            return false;
-        callArgs.rval().set(ObjectValue(*simdObj));
-        break;
-      case ExprType::B32x4:
-        simdObj = CreateSimd<Bool32x4>(cx, (int32_t*)&coercedArgs[0]);
-        if (!simdObj)
-            return false;
-        callArgs.rval().set(ObjectValue(*simdObj));
-        break;
-    }
-
-    return true;
-}
-
-static JSFunction*
-NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
-                    HandleObject moduleObj, unsigned exportIndex)
-{
-    unsigned numArgs = func.isChangeHeap()
-                       ? 1
-                       : module.wasm().exports()[func.wasmIndex()].sig().args().length();
-
-    RootedPropertyName name(cx, func.name());
-    JSFunction* fun =
-        NewNativeConstructor(cx, CallAsmJS, numArgs, name,
-                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
-                             JSFunction::ASMJS_CTOR);
-    if (!fun)
-        return nullptr;
-
-    fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj));
-    fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
-    return fun;
-}
-
-static bool
-HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
-                         HandlePropertyName name)
-{
-    if (cx->isExceptionPending())
-        return false;
-
-    ScriptSource* source = module.scriptSource();
-
-    // Source discarding is allowed to affect JS semantics because it is never
-    // enabled for normal JS content.
-    bool haveSource = source->hasSourceData();
-    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
-        return false;
-    if (!haveSource) {
-        JS_ReportError(cx, "asm.js link failure with source discarding enabled");
-        return false;
-    }
-
-    uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
-    uint32_t end = module.srcEndBeforeCurly();
-    Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
-    if (!src)
-        return false;
-
-    RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
-                                               name, gc::AllocKind::FUNCTION,
-                                               TenuredObject));
-    if (!fun)
-        return false;
-
-    Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
-    if (!formals.reserve(3))
-        return false;
-
-    if (module.globalArgumentName())
-        formals.infallibleAppend(module.globalArgumentName());
-    if (module.importArgumentName())
-        formals.infallibleAppend(module.importArgumentName());
-    if (module.bufferArgumentName())
-        formals.infallibleAppend(module.bufferArgumentName());
-
-    CompileOptions options(cx);
-    options.setMutedErrors(source->mutedErrors())
-           .setFile(source->filename())
-           .setNoScriptRval(false);
-
-    // The exported function inherits an implicit strict context if the module
-    // also inherited it somehow.
-    if (module.strict())
-        options.strictOption = true;
-
-    AutoStableStringChars stableChars(cx);
-    if (!stableChars.initTwoByte(cx, src))
-        return false;
-
-    const char16_t* chars = stableChars.twoByteRange().start().get();
-    SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
-                                              ? SourceBufferHolder::GiveOwnership
-                                              : SourceBufferHolder::NoOwnership;
-    SourceBufferHolder srcBuf(chars, end - begin, ownership);
-    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
-        return false;
-
-    // Call the function we just recompiled.
-    args.setCallee(ObjectValue(*fun));
-    return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
-}
-
-static JSObject*
-CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj)
-{
-    AsmJSModule& module = moduleObj->module();
-    const AsmJSModule::ExportVector& exports = module.exports();
-
-    if (exports.length() == 1) {
-        const AsmJSModule::Export& func = exports[0];
-        if (!func.maybeFieldName())
-            return NewExportedFunction(cx, module, func, moduleObj, 0);
-    }
-
-    gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length());
-    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
-    if (!obj)
-        return nullptr;
-
-    for (unsigned i = 0; i < exports.length(); i++) {
-        const AsmJSModule::Export& func = exports[i];
-
-        RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i));
-        if (!fun)
-            return nullptr;
-
-        MOZ_ASSERT(func.maybeFieldName() != nullptr);
-        RootedId id(cx, NameToId(func.maybeFieldName()));
-        RootedValue val(cx, ObjectValue(*fun));
-        if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
-            return nullptr;
-    }
-
-    return obj;
-}
-
-static const unsigned MODULE_FUN_SLOT = 0;
-
-static AsmJSModuleObject&
-ModuleFunctionToModuleObject(JSFunction* fun)
-{
-    return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>();
-}
-
-// 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>());
-    Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun));
-
-    // 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().wasm().dynamicallyLinked()) {
-        Rooted<AsmJSModuleObject*> clone(cx, AsmJSModuleObject::create(cx));
-        if (!clone)
-            return false;
-
-        if (!moduleObj->module().clone(cx, clone))
-            return false;
-
-        moduleObj = clone;
-    }
-
-    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.
-        RootedPropertyName name(cx, fun->name());
-        return HandleDynamicLinkFailure(cx, args, module, name);
-    }
-
-    // Link-time validation succeeded, so wrap all the exported functions with
-    // CallAsmJS builtins that trampoline into the generated code.
-    JSObject* obj = CreateExportObject(cx, moduleObj);
-    if (!obj)
-        return false;
-
-    args.rval().set(ObjectValue(*obj));
-    return true;
-}
-
-JSFunction*
-js::NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject moduleObj)
-{
-    RootedPropertyName name(cx, origFun->name());
-
-    JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
-                                                  : JSFunction::ASMJS_CTOR;
-    JSFunction* moduleFun =
-        NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
-                             gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
-                             flags);
-    if (!moduleFun)
-        return nullptr;
-
-    moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj));
-    return moduleFun;
-}
-
-bool
-js::IsAsmJSModuleNative(js::Native native)
-{
-    return native == LinkAsmJS;
-}
-
-static bool
-IsMaybeWrappedNativeFunction(const Value& v, Native native, JSFunction** fun = nullptr)
-{
-    if (!v.isObject())
-        return false;
-
-    JSObject* obj = CheckedUnwrap(&v.toObject());
-    if (!obj)
-        return false;
-
-    if (!obj->is<JSFunction>())
-        return false;
-
-    if (fun)
-        *fun = &obj->as<JSFunction>();
-
-    return obj->as<JSFunction>().maybeNative() == native;
-}
-
-bool
-js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
-    args.rval().set(BooleanValue(rval));
-    return true;
-}
-
-bool
-js::IsAsmJSModule(HandleFunction fun)
-{
-    return fun->isNative() && fun->maybeNative() == LinkAsmJS;
-}
-
-static bool
-AppendUseStrictSource(JSContext* cx, HandleFunction fun, Handle<JSFlatString*> src, StringBuffer& out)
-{
-    // We need to add "use strict" in the body right after the opening
-    // brace.
-    size_t bodyStart = 0, bodyEnd;
-
-    // No need to test for functions created with the Function ctor as
-    // these don't implicitly inherit the "use strict" context. Strict mode is
-    // enabled for functions created with the Function ctor only if they begin with
-    // the "use strict" directive, but these functions won't validate as asm.js
-    // modules.
-
-    if (!FindBody(cx, fun, src, &bodyStart, &bodyEnd))
-        return false;
-
-    return out.appendSubstring(src, 0, bodyStart) &&
-           out.append("\n\"use strict\";\n") &&
-           out.appendSubstring(src, bodyStart, src->length() - bodyStart);
-}
-
-JSString*
-js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
-{
-    AsmJSModule& module = ModuleFunctionToModuleObject(fun).module();
-
-    uint32_t begin = module.srcStart();
-    uint32_t end = module.srcEndAfterCurly();
-    ScriptSource* source = module.scriptSource();
-    StringBuffer out(cx);
-
-    if (addParenToLambda && fun->isLambda() && !out.append("("))
-        return nullptr;
-
-    if (!out.append("function "))
-        return nullptr;
-
-    if (fun->atom() && !out.append(fun->atom()))
-        return nullptr;
-
-    bool haveSource = source->hasSourceData();
-    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
-        return nullptr;
-
-    if (!haveSource) {
-        if (!out.append("() {\n    [sourceless code]\n}"))
-            return nullptr;
-    } else {
-        // Whether the function has been created with a Function ctor
-        bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
-        if (funCtor) {
-            // Functions created with the function constructor don't have arguments in their source.
-            if (!out.append("("))
-                return nullptr;
-
-            if (PropertyName* argName = module.globalArgumentName()) {
-                if (!out.append(argName))
-                    return nullptr;
-            }
-            if (PropertyName* argName = module.importArgumentName()) {
-                if (!out.append(", ") || !out.append(argName))
-                    return nullptr;
-            }
-            if (PropertyName* argName = module.bufferArgumentName()) {
-                if (!out.append(", ") || !out.append(argName))
-                    return nullptr;
-            }
-
-            if (!out.append(") {\n"))
-                return nullptr;
-        }
-
-        Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
-        if (!src)
-            return nullptr;
-
-        if (module.strict()) {
-            if (!AppendUseStrictSource(cx, fun, src, out))
-                return nullptr;
-        } else {
-            if (!out.append(src))
-                return nullptr;
-        }
-
-        if (funCtor && !out.append("\n}"))
-            return nullptr;
-    }
-
-    if (addParenToLambda && fun->isLambda() && !out.append(")"))
-        return nullptr;
-
-    return out.finishString();
-}
-
-bool
-js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    JSFunction* fun;
-    if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
-                             "argument passed to isAsmJSModuleLoadedFromCache is not a "
-                             "validated asm.js module");
-        return false;
-    }
-
-    bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().wasm().loadedFromCache();
-
-    args.rval().set(BooleanValue(loadedFromCache));
-    return true;
-}
-
-bool
-js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
-    args.rval().set(BooleanValue(rval));
-    return true;
-}
-
-bool
-js::IsAsmJSFunction(HandleFunction fun)
-{
-    return fun->isNative() && fun->maybeNative() == CallAsmJS;
-}
-
-JSString*
-js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
-{
-    AsmJSModule& module = FunctionToEnclosingModule(fun);
-    const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)];
-    uint32_t begin = module.srcStart() + f.startOffsetInModule();
-    uint32_t end = module.srcStart() + f.endOffsetInModule();
-
-    ScriptSource* source = module.scriptSource();
-    StringBuffer out(cx);
-
-    if (!out.append("function "))
-        return nullptr;
-
-    bool haveSource = source->hasSourceData();
-    if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
-        return nullptr;
-
-    if (!haveSource) {
-        // asm.js functions can't be anonymous
-        MOZ_ASSERT(fun->atom());
-        if (!out.append(fun->atom()))
-            return nullptr;
-        if (!out.append("() {\n    [sourceless code]\n}"))
-            return nullptr;
-    } else {
-        // asm.js functions cannot have been created with a Function constructor
-        // as they belong within a module.
-        MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
-
-        if (module.strict()) {
-            // AppendUseStrictSource expects its input to start right after the
-            // function name, so split the source chars from the src into two parts:
-            // the function name and the rest (arguments + body).
-
-            // asm.js functions can't be anonymous
-            MOZ_ASSERT(fun->atom());
-            if (!out.append(fun->atom()))
-                return nullptr;
-
-            size_t nameEnd = begin + fun->atom()->length();
-            Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end));
-            if (!src || !AppendUseStrictSource(cx, fun, src, out))
-                return nullptr;
-        } else {
-            Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
-            if (!src)
-                return nullptr;
-            if (!out.append(src))
-                return nullptr;
-        }
-    }
-
-    return out.finishString();
-}
deleted file mode 100644
--- a/js/src/asmjs/AsmJSLink.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef asmjs_AsmJSLink_h
-#define asmjs_AsmJSLink_h
-
-#include "NamespaceImports.h"
-
-namespace js {
-
-// Create a new JSFunction to replace originalFun as the representation of the
-// function defining the succesfully-validated module 'moduleObj'.
-extern JSFunction*
-NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* originalFun, HandleObject moduleObj);
-
-// Return whether this is the js::Native returned by NewAsmJSModuleFunction.
-extern bool
-IsAsmJSModuleNative(JSNative native);
-
-// Return whether the given value is a function containing "use asm" that has
-// been validated according to the asm.js spec.
-extern bool
-IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
-extern bool
-IsAsmJSModule(HandleFunction fun);
-
-extern JSString*
-AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
-
-// Return whether the given value is a function containing "use asm" that was
-// loaded directly from the cache (and hence was validated previously).
-extern bool
-IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
-
-// Return whether the given value is a nested function in an asm.js module that
-// has been both compile- and link-time validated.
-extern bool
-IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
-extern bool
-IsAsmJSFunction(HandleFunction fun);
-
-extern JSString*
-AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
-
-} // namespace js
-
-#endif // asmjs_AsmJSLink_h
deleted file mode 100644
--- a/js/src/asmjs/AsmJSModule.cpp
+++ /dev/null
@@ -1,622 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asmjs/AsmJSModule.h"
-
-#include "mozilla/Compression.h"
-#include "mozilla/EnumeratedRange.h"
-#include "mozilla/PodOperations.h"
-
-#include "jsprf.h"
-
-#include "asmjs/WasmSerialize.h"
-#include "frontend/Parser.h"
-#include "js/Class.h"
-#include "js/MemoryMetrics.h"
-
-#include "jsobjinlines.h"
-
-#include "frontend/ParseNode-inl.h"
-
-using namespace js;
-using namespace js::frontend;
-using namespace js::jit;
-using namespace js::wasm;
-using mozilla::PodZero;
-using mozilla::PodEqual;
-using mozilla::Compression::LZ4;
-
-AsmJSModule::AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict)
-  : scriptSource_(scriptSource),
-    srcStart_(srcStart),
-    srcBodyStart_(srcBodyStart),
-    globalArgumentName_(nullptr),
-    importArgumentName_(nullptr),
-    bufferArgumentName_(nullptr)
-{
-    mozilla::PodZero(&pod);
-    pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
-    pod.maxHeapLength_ = 0x80000000;
-    pod.strict_ = strict;
-
-    // AsmJSCheckedImmediateRange should be defined to be at most the minimum
-    // heap length so that offsets can be folded into bounds checks.
-    MOZ_ASSERT(pod.minHeapLength_ - AsmJSCheckedImmediateRange <= pod.minHeapLength_);
-}
-
-void
-AsmJSModule::trace(JSTracer* trc)
-{
-    if (wasm_)
-        wasm_->trace(trc);
-    for (Global& global : globals_)
-        global.trace(trc);
-    for (Export& exp : exports_)
-        exp.trace(trc);
-    if (globalArgumentName_)
-        TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
-    if (importArgumentName_)
-        TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
-    if (bufferArgumentName_)
-        TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
-}
-
-void
-AsmJSModule::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
-                           size_t* asmJSModuleData)
-{
-    if (wasm_)
-        wasm_->addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData);
-
-    if (linkData_)
-        *asmJSModuleData += linkData_->sizeOfExcludingThis(mallocSizeOf);
-
-    *asmJSModuleData += mallocSizeOf(this) +
-                        globals_.sizeOfExcludingThis(mallocSizeOf) +
-                        imports_.sizeOfExcludingThis(mallocSizeOf) +
-                        exports_.sizeOfExcludingThis(mallocSizeOf);
-}
-
-void
-AsmJSModule::finish(Module* wasm, wasm::UniqueStaticLinkData linkData,
-                    uint32_t endBeforeCurly, uint32_t endAfterCurly)
-{
-    MOZ_ASSERT(!isFinished());
-
-    wasm_.reset(wasm);
-    linkData_ = Move(linkData);
-
-    MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
-    MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
-    pod.srcLength_ = endBeforeCurly - srcStart_;
-    pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
-
-    MOZ_ASSERT(isFinished());
-}
-
-bool
-js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
-{
-    for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
-        if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
-            return false;
-    }
-    return true;
-}
-
-static void
-AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
-{
-    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
-    if (moduleObj.hasModule())
-        fop->delete_(&moduleObj.module());
-}
-
-static void
-AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
-{
-    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
-    if (moduleObj.hasModule())
-        moduleObj.module().trace(trc);
-}
-
-const Class AsmJSModuleObject::class_ = {
-    "AsmJSModuleObject",
-    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
-    JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    AsmJSModuleObject_finalize,
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    AsmJSModuleObject_trace
-};
-
-AsmJSModuleObject*
-AsmJSModuleObject::create(ExclusiveContext* cx)
-{
-    AutoSetNewObjectMetadata metadata(cx);
-    JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
-    if (!obj)
-        return nullptr;
-    return &obj->as<AsmJSModuleObject>();
-}
-
-bool
-AsmJSModuleObject::hasModule() const
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    return !getReservedSlot(MODULE_SLOT).isUndefined();
-}
-
-void
-AsmJSModuleObject::setModule(AsmJSModule* newModule)
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    if (hasModule())
-        js_delete(&module());
-    setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
-}
-
-AsmJSModule&
-AsmJSModuleObject::module() const
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
-}
-
-uint8_t*
-AsmJSModule::Global::serialize(uint8_t* cursor) const
-{
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    cursor = SerializeName(cursor, name_);
-    return cursor;
-}
-
-size_t
-AsmJSModule::Global::serializedSize() const
-{
-    return sizeof(pod) +
-           SerializedNameSize(name_);
-}
-
-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(JSContext* cx, Global* out) const
-{
-    *out = *this;
-    return true;
-}
-
-uint8_t*
-AsmJSModule::Export::serialize(uint8_t* cursor) const
-{
-    cursor = SerializeName(cursor, name_);
-    cursor = SerializeName(cursor, maybeFieldName_);
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    return cursor;
-}
-
-size_t
-AsmJSModule::Export::serializedSize() const
-{
-    return SerializedNameSize(name_) +
-           SerializedNameSize(maybeFieldName_) +
-           sizeof(pod);
-}
-
-const uint8_t*
-AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    (cursor = DeserializeName(cx, cursor, &name_)) &&
-    (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
-    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
-    return cursor;
-}
-
-bool
-AsmJSModule::Export::clone(JSContext* cx, Export* out) const
-{
-    out->name_ = name_;
-    out->maybeFieldName_ = maybeFieldName_;
-    out->pod = pod;
-    return true;
-}
-
-size_t
-AsmJSModule::serializedSize() const
-{
-    MOZ_ASSERT(isFinished());
-    return wasm_->serializedSize() +
-           linkData_->serializedSize() +
-           sizeof(pod) +
-           SerializedVectorSize(globals_) +
-           SerializedPodVectorSize(imports_) +
-           SerializedVectorSize(exports_) +
-           SerializedNameSize(globalArgumentName_) +
-           SerializedNameSize(importArgumentName_) +
-           SerializedNameSize(bufferArgumentName_);
-}
-
-uint8_t*
-AsmJSModule::serialize(uint8_t* cursor) const
-{
-    MOZ_ASSERT(isFinished());
-    cursor = wasm_->serialize(cursor);
-    cursor = linkData_->serialize(cursor);
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    cursor = SerializeVector(cursor, globals_);
-    cursor = SerializePodVector(cursor, imports_);
-    cursor = SerializeVector(cursor, exports_);
-    cursor = SerializeName(cursor, globalArgumentName_);
-    cursor = SerializeName(cursor, importArgumentName_);
-    cursor = SerializeName(cursor, bufferArgumentName_);
-    return cursor;
-}
-
-const uint8_t*
-AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    linkData_ = cx->make_unique<StaticLinkData>();
-    if (!linkData_)
-        return nullptr;
-
-    // To avoid GC-during-deserialization corner cases, prevent atoms from
-    // being collected.
-    AutoKeepAtoms aka(cx->perThreadData);
-
-    (cursor = Module::deserialize(cx, cursor, &wasm_)) &&
-    (cursor = linkData_->deserialize(cx, cursor)) &&
-    (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
-    (cursor = DeserializeVector(cx, cursor, &globals_)) &&
-    (cursor = DeserializePodVector(cx, cursor, &imports_)) &&
-    (cursor = DeserializeVector(cx, cursor, &exports_)) &&
-    (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
-    (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
-    (cursor = DeserializeName(cx, cursor, &bufferArgumentName_));
-
-    return cursor;
-}
-
-bool
-AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const
-{
-    auto out = cx->new_<AsmJSModule>(scriptSource(), srcStart_, srcBodyStart_, pod.strict_);
-    if (!out)
-        return false;
-
-    obj->setModule(out);
-
-    out->wasm_ = wasm_->clone(cx, *linkData_);
-    if (!out->wasm_)
-        return false;
-
-    out->linkData_ = cx->make_unique<StaticLinkData>();
-    if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get()))
-        return false;
-
-    out->pod = pod;
-
-    if (!CloneVector(cx, globals_, &out->globals_) ||
-        !ClonePodVector(cx, imports_, &out->imports_) ||
-        !CloneVector(cx, exports_, &out->exports_))
-    {
-        return false;
-    }
-
-    out->globalArgumentName_ = globalArgumentName_;
-    out->importArgumentName_ = importArgumentName_;
-    out->bufferArgumentName_ = bufferArgumentName_;
-    return true;
-}
-
-struct PropertyNameWrapper
-{
-    PropertyName* name;
-
-    PropertyNameWrapper()
-      : name(nullptr)
-    {}
-    explicit PropertyNameWrapper(PropertyName* name)
-      : name(name)
-    {}
-    size_t serializedSize() const {
-        return SerializedNameSize(name);
-    }
-    uint8_t* serialize(uint8_t* cursor) const {
-        return SerializeName(cursor, name);
-    }
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
-        return DeserializeName(cx, cursor, &name);
-    }
-};
-
-class ModuleChars
-{
-  protected:
-    uint32_t isFunCtor_;
-    Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
-
-  public:
-    static uint32_t beginOffset(AsmJSParser& parser) {
-        return parser.pc->maybeFunction->pn_pos.begin;
-    }
-
-    static uint32_t endOffset(AsmJSParser& parser) {
-        TokenPos pos(0, 0);  // initialize to silence GCC warning
-        MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos, TokenStream::Operand));
-        return pos.end;
-    }
-};
-
-class ModuleCharsForStore : ModuleChars
-{
-    uint32_t uncompressedSize_;
-    uint32_t compressedSize_;
-    Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
-
-  public:
-    bool init(AsmJSParser& parser) {
-        MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
-
-        uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
-        size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
-        if (maxCompressedSize < uncompressedSize_)
-            return false;
-
-        if (!compressedBuffer_.resize(maxCompressedSize))
-            return false;
-
-        const char16_t* chars = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
-        const char* source = reinterpret_cast<const char*>(chars);
-        size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
-        if (!compressedSize || compressedSize > UINT32_MAX)
-            return false;
-
-        compressedSize_ = compressedSize;
-
-        // For a function statement or named function expression:
-        //   function f(x,y,z) { abc }
-        // the range [beginOffset, endOffset) captures the source:
-        //   f(x,y,z) { abc }
-        // An unnamed function expression captures the same thing, sans 'f'.
-        // Since asm.js modules do not contain any free variables, equality of
-        // [beginOffset, endOffset) is sufficient to guarantee identical code
-        // generation, modulo MachineId.
-        //
-        // For functions created with 'new Function', function arguments are
-        // not present in the source so we must manually explicitly serialize
-        // and match the formals as a Vector of PropertyName.
-        isFunCtor_ = parser.pc->isFunctionConstructorBody();
-        if (isFunCtor_) {
-            unsigned numArgs;
-            ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
-            for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
-                if (!funCtorArgs_.append(arg->name()))
-                    return false;
-            }
-        }
-
-        return true;
-    }
-
-    size_t serializedSize() const {
-        return sizeof(uint32_t) +
-               sizeof(uint32_t) +
-               compressedSize_ +
-               sizeof(uint32_t) +
-               (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
-    }
-
-    uint8_t* serialize(uint8_t* cursor) const {
-        cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
-        cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
-        cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
-        cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
-        if (isFunCtor_)
-            cursor = SerializeVector(cursor, funCtorArgs_);
-        return cursor;
-    }
-};
-
-class ModuleCharsForLookup : ModuleChars
-{
-    Vector<char16_t, 0, SystemAllocPolicy> chars_;
-
-  public:
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
-        uint32_t uncompressedSize;
-        cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
-
-        uint32_t compressedSize;
-        cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
-
-        if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
-            return nullptr;
-
-        const char* source = reinterpret_cast<const char*>(cursor);
-        char* dest = reinterpret_cast<char*>(chars_.begin());
-        if (!LZ4::decompress(source, dest, uncompressedSize))
-            return nullptr;
-
-        cursor += compressedSize;
-
-        cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
-        if (isFunCtor_)
-            cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
-
-        return cursor;
-    }
-
-    bool match(AsmJSParser& parser) const {
-        const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
-        const char16_t* parseLimit = parser.tokenStream.rawLimit();
-        MOZ_ASSERT(parseLimit >= parseBegin);
-        if (uint32_t(parseLimit - parseBegin) < chars_.length())
-            return false;
-        if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
-            return false;
-        if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
-            return false;
-        if (isFunCtor_) {
-            // For function statements, the closing } is included as the last
-            // character of the matched source. For Function constructor,
-            // parsing terminates with EOF which we must explicitly check. This
-            // prevents
-            //   new Function('"use asm"; function f() {} return f')
-            // from incorrectly matching
-            //   new Function('"use asm"; function f() {} return ff')
-            if (parseBegin + chars_.length() != parseLimit)
-                return false;
-            unsigned numArgs;
-            ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
-            if (funCtorArgs_.length() != numArgs)
-                return false;
-            for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
-                if (funCtorArgs_[i].name != arg->name())
-                    return false;
-            }
-        }
-        return true;
-    }
-};
-
-JS::AsmJSCacheResult
-js::StoreAsmJSModuleInCache(AsmJSParser& parser, const AsmJSModule& module, ExclusiveContext* cx)
-{
-    MachineId machineId;
-    if (!machineId.extractCurrentState(cx))
-        return JS::AsmJSCache_InternalError;
-
-    ModuleCharsForStore moduleChars;
-    if (!moduleChars.init(parser))
-        return JS::AsmJSCache_InternalError;
-
-    size_t serializedSize = machineId.serializedSize() +
-                            moduleChars.serializedSize() +
-                            module.serializedSize();
-
-    JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
-    if (!open)
-        return JS::AsmJSCache_Disabled_Internal;
-
-    const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
-    const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
-    bool installed = parser.options().installedFile;
-
-    ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
-    JS::AsmJSCacheResult openResult =
-        open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
-    if (openResult != JS::AsmJSCache_Success)
-        return openResult;
-
-    uint8_t* cursor = entry.memory;
-    cursor = machineId.serialize(cursor);
-    cursor = moduleChars.serialize(cursor);
-    cursor = module.serialize(cursor);
-
-    MOZ_ASSERT(cursor == entry.memory + serializedSize);
-    return JS::AsmJSCache_Success;
-}
-
-bool
-js::LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
-                             bool* loadedFromCache, UniqueChars* compilationTimeReport)
-{
-    int64_t usecBefore = PRMJ_Now();
-
-    *loadedFromCache = false;
-
-    MachineId machineId;
-    if (!machineId.extractCurrentState(cx))
-        return true;
-
-    JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
-    if (!open)
-        return true;
-
-    const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
-    const char16_t* limit = parser.tokenStream.rawLimit();
-
-    ScopedCacheEntryOpenedForRead entry(cx);
-    if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
-        return true;
-
-    const uint8_t* cursor = entry.memory;
-
-    MachineId cachedMachineId;
-    cursor = cachedMachineId.deserialize(cx, cursor);
-    if (!cursor)
-        return false;
-    if (machineId != cachedMachineId)
-        return true;
-
-    ModuleCharsForLookup moduleChars;
-    cursor = moduleChars.deserialize(cx, cursor);
-    if (!moduleChars.match(parser))
-        return true;
-
-    uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
-    uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
-    bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
-
-    AsmJSModule* module = cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict);
-    if (!module)
-        return false;
-
-    moduleObj->setModule(module);
-
-    cursor = module->deserialize(cx, cursor);
-    if (!cursor)
-        return false;
-
-    bool atEnd = cursor == entry.memory + entry.serializedSize;
-    MOZ_ASSERT(atEnd, "Corrupt cache file");
-    if (!atEnd)
-        return true;
-
-    if (module->wasm().compileArgs() != CompileArgs(cx))
-        return true;
-
-    module->staticallyLink(cx);
-
-    if (!parser.tokenStream.advance(module->srcEndBeforeCurly()))
-        return false;
-
-    *loadedFromCache = true;
-
-    int64_t usecAfter = PRMJ_Now();
-    int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
-    *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
-    return true;
-}
-
deleted file mode 100644
--- a/js/src/asmjs/AsmJSModule.h
+++ /dev/null
@@ -1,648 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef asmjs_AsmJSModule_h
-#define asmjs_AsmJSModule_h
-
-#include "mozilla/EnumeratedArray.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Move.h"
-#include "mozilla/PodOperations.h"
-
-#include "asmjs/AsmJSValidate.h"
-#include "asmjs/WasmModule.h"
-#include "builtin/SIMD.h"
-#include "gc/Tracer.h"
-#include "vm/TypedArrayObject.h"
-
-namespace js {
-
-class AsmJSModuleObject;
-typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
-
-// The asm.js spec recognizes this set of builtin Math functions.
-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,
-    AsmJSMathBuiltin_clz32
-};
-
-// The asm.js spec will recognize this set of builtin Atomics functions.
-enum AsmJSAtomicsBuiltinFunction
-{
-    AsmJSAtomicsBuiltin_compareExchange,
-    AsmJSAtomicsBuiltin_exchange,
-    AsmJSAtomicsBuiltin_load,
-    AsmJSAtomicsBuiltin_store,
-    AsmJSAtomicsBuiltin_fence,
-    AsmJSAtomicsBuiltin_add,
-    AsmJSAtomicsBuiltin_sub,
-    AsmJSAtomicsBuiltin_and,
-    AsmJSAtomicsBuiltin_or,
-    AsmJSAtomicsBuiltin_xor,
-    AsmJSAtomicsBuiltin_isLockFree
-};
-
-// Set of known global object SIMD's attributes, i.e. types
-enum AsmJSSimdType
-{
-    AsmJSSimdType_int32x4,
-    AsmJSSimdType_float32x4,
-    AsmJSSimdType_bool32x4
-};
-
-static inline bool
-IsSignedIntSimdType(AsmJSSimdType type)
-{
-    switch (type) {
-      case AsmJSSimdType_int32x4:
-        return true;
-      case AsmJSSimdType_float32x4:
-      case AsmJSSimdType_bool32x4:
-        return false;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type");
-}
-
-// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
-enum AsmJSSimdOperation
-{
-#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
-    FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION)
-#undef ASMJSSIMDOPERATION
-};
-
-// An AsmJSModule extends (via containment) a wasm::Module with the extra persistent state
-// necessary to represent a compiled asm.js module.
-class AsmJSModule
-{
-  public:
-    class Global
-    {
-      public:
-        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
-                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
-        enum VarInitKind { InitConstant, InitImport };
-        enum ConstantKind { GlobalConstant, MathConstant };
-
-      private:
-        struct CacheablePod {
-            Which which_;
-            union {
-                struct {
-                    uint32_t globalDataOffset_;
-                    VarInitKind initKind_;
-                    union {
-                        wasm::ValType importType_;
-                        wasm::Val val_;
-                    } u;
-                } var;
-                uint32_t ffiIndex_;
-                Scalar::Type viewType_;
-                AsmJSMathBuiltinFunction mathBuiltinFunc_;
-                AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
-                AsmJSSimdType simdCtorType_;
-                struct {
-                    AsmJSSimdType type_;
-                    AsmJSSimdOperation which_;
-                } simdOp;
-                struct {
-                    ConstantKind kind_;
-                    double value_;
-                } constant;
-            } u;
-        } pod;
-        PropertyName* name_;
-
-        friend class AsmJSModule;
-
-        Global(Which which, PropertyName* name) {
-            mozilla::PodZero(&pod);  // zero padding for Valgrind
-            pod.which_ = which;
-            name_ = name;
-            MOZ_ASSERT_IF(name_, name_->isTenured());
-        }
-
-        void trace(JSTracer* trc) {
-            if (name_)
-                TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
-        }
-
-      public:
-        Global() {}
-        Which which() const {
-            return pod.which_;
-        }
-        uint32_t varGlobalDataOffset() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            return pod.u.var.globalDataOffset_;
-        }
-        VarInitKind varInitKind() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            return pod.u.var.initKind_;
-        }
-        wasm::Val varInitVal() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
-            return pod.u.var.u.val_;
-        }
-        wasm::ValType varInitImportType() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
-            return pod.u.var.u.importType_;
-        }
-        PropertyName* varImportField() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
-            return name_;
-        }
-        PropertyName* ffiField() const {
-            MOZ_ASSERT(pod.which_ == FFI);
-            return name_;
-        }
-        uint32_t ffiIndex() const {
-            MOZ_ASSERT(pod.which_ == FFI);
-            return pod.u.ffiIndex_;
-        }
-        // When a view is created from an imported constructor:
-        //   var I32 = stdlib.Int32Array;
-        //   var i32 = new I32(buffer);
-        // the second import has nothing to validate and thus has a null field.
-        PropertyName* maybeViewName() const {
-            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
-            return name_;
-        }
-        Scalar::Type viewType() const {
-            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
-            return pod.u.viewType_;
-        }
-        PropertyName* mathName() const {
-            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
-            return name_;
-        }
-        PropertyName* atomicsName() const {
-            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
-            return name_;
-        }
-        AsmJSMathBuiltinFunction mathBuiltinFunction() const {
-            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
-            return pod.u.mathBuiltinFunc_;
-        }
-        AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
-            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
-            return pod.u.atomicsBuiltinFunc_;
-        }
-        AsmJSSimdType simdCtorType() const {
-            MOZ_ASSERT(pod.which_ == SimdCtor);
-            return pod.u.simdCtorType_;
-        }
-        PropertyName* simdCtorName() const {
-            MOZ_ASSERT(pod.which_ == SimdCtor);
-            return name_;
-        }
-        PropertyName* simdOperationName() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return name_;
-        }
-        AsmJSSimdOperation simdOperation() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return pod.u.simdOp.which_;
-        }
-        AsmJSSimdType simdOperationType() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return pod.u.simdOp.type_;
-        }
-        PropertyName* constantName() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return name_;
-        }
-        ConstantKind constantKind() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return pod.u.constant.kind_;
-        }
-        double constantValue() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return pod.u.constant.value_;
-        }
-
-        WASM_DECLARE_SERIALIZABLE(Global);
-    };
-
-    typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
-
-    class Import
-    {
-        uint32_t ffiIndex_;
-      public:
-        Import() = default;
-        explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
-        uint32_t ffiIndex() const { return ffiIndex_; }
-    };
-
-    typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
-
-    class Export
-    {
-        PropertyName* name_;
-        PropertyName* maybeFieldName_;
-        struct CacheablePod {
-            uint32_t wasmIndex_;
-            uint32_t startOffsetInModule_;  // Store module-start-relative offsets
-            uint32_t endOffsetInModule_;    // so preserved by serialization.
-        } pod;
-
-      public:
-        Export() {}
-        Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
-               uint32_t startOffsetInModule, uint32_t endOffsetInModule)
-          : name_(name),
-            maybeFieldName_(maybeFieldName)
-        {
-            MOZ_ASSERT(name_->isTenured());
-            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
-            pod.wasmIndex_ = wasmIndex;
-            pod.startOffsetInModule_ = startOffsetInModule;
-            pod.endOffsetInModule_ = endOffsetInModule;
-        }
-
-        void trace(JSTracer* trc) {
-            TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
-            if (maybeFieldName_)
-                TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
-        }
-
-        PropertyName* name() const {
-            return name_;
-        }
-        PropertyName* maybeFieldName() const {
-            return maybeFieldName_;
-        }
-        uint32_t startOffsetInModule() const {
-            return pod.startOffsetInModule_;
-        }
-        uint32_t endOffsetInModule() const {
-            return pod.endOffsetInModule_;
-        }
-        static const uint32_t ChangeHeap = UINT32_MAX;
-        bool isChangeHeap() const {
-            return pod.wasmIndex_ == ChangeHeap;
-        }
-        uint32_t wasmIndex() const {
-            MOZ_ASSERT(!isChangeHeap());
-            return pod.wasmIndex_;
-        }
-
-        WASM_DECLARE_SERIALIZABLE(Export)
-    };
-
-    typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
-
-    typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
-
-  private:
-    UniqueWasmModule            wasm_;
-    wasm::UniqueStaticLinkData  linkData_;
-    struct CacheablePod {
-        uint32_t                minHeapLength_;
-        uint32_t                maxHeapLength_;
-        uint32_t                heapLengthMask_;
-        uint32_t                numFFIs_;
-        uint32_t                srcLength_;
-        uint32_t                srcLengthWithRightBrace_;
-        bool                    strict_;
-        bool                    hasArrayView_;
-        bool                    isSharedView_;
-        bool                    hasFixedMinHeapLength_;
-    } pod;
-    const ScriptSourceHolder    scriptSource_;
-    const uint32_t              srcStart_;
-    const uint32_t              srcBodyStart_;
-    GlobalVector                globals_;
-    ImportVector                imports_;
-    ExportVector                exports_;
-    PropertyName*               globalArgumentName_;
-    PropertyName*               importArgumentName_;
-    PropertyName*               bufferArgumentName_;
-
-  public:
-    explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict);
-    void trace(JSTracer* trc);
-
-    /*************************************************************************/
-    // These functions may be used as soon as the module is constructed:
-
-    ScriptSource* scriptSource() const {
-        return scriptSource_.get();
-    }
-    bool strict() const {
-        return pod.strict_;
-    }
-
-    // srcStart() refers to the offset in the ScriptSource to the beginning of
-    // the asm.js module function. If the function has been created with the
-    // Function constructor, this will be the first character in the function
-    // source. Otherwise, it will be the opening parenthesis of the arguments
-    // list.
-    uint32_t srcStart() const {
-        return srcStart_;
-    }
-
-    // srcBodyStart() refers to the offset in the ScriptSource to the end
-    // of the 'use asm' string-literal token.
-    uint32_t srcBodyStart() const {
-        return srcBodyStart_;
-    }
-
-    // While these functions may be accessed at any time, their values will
-    // change as the module is compiled.
-    uint32_t minHeapLength() const {
-        return pod.minHeapLength_;
-    }
-    uint32_t maxHeapLength() const {
-        return pod.maxHeapLength_;
-    }
-    uint32_t heapLengthMask() const {
-        MOZ_ASSERT(pod.hasFixedMinHeapLength_);
-        return pod.heapLengthMask_;
-    }
-
-    void initGlobalArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        globalArgumentName_ = n;
-    }
-    void initImportArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        importArgumentName_ = n;
-    }
-    void initBufferArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        bufferArgumentName_ = n;
-    }
-    PropertyName* globalArgumentName() const {
-        return globalArgumentName_;
-    }
-    PropertyName* importArgumentName() const {
-        return importArgumentName_;
-    }
-    PropertyName* bufferArgumentName() const {
-        return bufferArgumentName_;
-    }
-
-    bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Variable, nullptr);
-        g.pod.u.var.initKind_ = Global::InitConstant;
-        g.pod.u.var.u.val_ = v;
-        g.pod.u.var.globalDataOffset_ = globalDataOffset;
-        return globals_.append(g);
-    }
-    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Variable, name);
-        g.pod.u.var.initKind_ = Global::InitImport;
-        g.pod.u.var.u.importType_ = importType;
-        g.pod.u.var.globalDataOffset_ = globalDataOffset;
-        return globals_.append(g);
-    }
-    bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
-        MOZ_ASSERT(!isFinished());
-        if (pod.numFFIs_ == UINT32_MAX)
-            return false;
-        Global g(Global::FFI, field);
-        g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
-        return globals_.append(g);
-    }
-    bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
-        MOZ_ASSERT(!isFinished());
-        pod.hasArrayView_ = true;
-        pod.isSharedView_ = false;
-        Global g(Global::ArrayView, maybeField);
-        g.pod.u.viewType_ = vt;
-        return globals_.append(g);
-    }
-    bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(field);
-        pod.isSharedView_ = false;
-        Global g(Global::ArrayViewCtor, field);
-        g.pod.u.viewType_ = vt;
-        return globals_.append(g);
-    }
-    bool addByteLength() {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::ByteLength, nullptr);
-        return globals_.append(g);
-    }
-    bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::MathBuiltinFunction, field);
-        g.pod.u.mathBuiltinFunc_ = func;
-        return globals_.append(g);
-    }
-    bool addMathBuiltinConstant(double value, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Constant, field);
-        g.pod.u.constant.value_ = value;
-        g.pod.u.constant.kind_ = Global::MathConstant;
-        return globals_.append(g);
-    }
-    bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::AtomicsBuiltinFunction, field);
-        g.pod.u.atomicsBuiltinFunc_ = func;
-        return globals_.append(g);
-    }
-    bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::SimdCtor, field);
-        g.pod.u.simdCtorType_ = type;
-        return globals_.append(g);
-    }
-    bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::SimdOperation, field);
-        g.pod.u.simdOp.type_ = type;
-        g.pod.u.simdOp.which_ = op;
-        return globals_.append(g);
-    }
-    bool addGlobalConstant(double value, PropertyName* name) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Constant, name);
-        g.pod.u.constant.value_ = value;
-        g.pod.u.constant.kind_ = Global::GlobalConstant;
-        return globals_.append(g);
-    }
-    bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
-        MOZ_ASSERT(imports_.length() == importIndex);
-        return imports_.emplaceBack(ffiIndex);
-    }
-    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
-                   uint32_t funcSrcBegin, uint32_t funcSrcEnd)
-    {
-        // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
-        // (the entire file) and ExportedFunctions store offsets relative to
-        // the beginning of the module (so that they are caching-invariant).
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(srcStart_ < funcSrcBegin);
-        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
-        return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
-                                    funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
-    }
-    bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
-        MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
-        MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
-        MOZ_ASSERT(max <= pod.maxHeapLength_);
-        MOZ_ASSERT(min <= max);
-        pod.heapLengthMask_ = mask;
-        pod.minHeapLength_ = min;
-        pod.maxHeapLength_ = max;
-        pod.hasFixedMinHeapLength_ = true;
-        return true;
-    }
-
-    const GlobalVector& globals() const {
-        return globals_;
-    }
-    const ImportVector& imports() const {
-        return imports_;
-    }
-    const ExportVector& exports() const {
-        return exports_;
-    }
-
-    void setViewsAreShared() {
-        if (pod.hasArrayView_)
-            pod.isSharedView_ = true;
-    }
-    bool hasArrayView() const {
-        return pod.hasArrayView_;
-    }
-    bool isSharedView() const {
-        return pod.isSharedView_;
-    }
-    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
-        MOZ_ASSERT(!isFinished());
-        if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
-            return false;
-        if (len > pod.maxHeapLength_)
-            return false;
-        len = RoundUpToNextValidAsmJSHeapLength(len);
-        if (len > pod.minHeapLength_)
-            pod.minHeapLength_ = len;
-        return true;
-    }
-
-    /*************************************************************************/
-    // A module isFinished() when compilation completes. After being finished,
-    // a module must be statically and dynamically linked before execution.
-
-    bool isFinished() const {
-        return !!wasm_;
-    }
-    void finish(wasm::Module* wasm, wasm::UniqueStaticLinkData linkData,
-                uint32_t endBeforeCurly, uint32_t endAfterCurly);
-
-    /*************************************************************************/
-    // These accessor functions can only be used after finish():
-
-    wasm::Module& wasm() const {
-        MOZ_ASSERT(isFinished());
-        return *wasm_;
-    }
-    uint32_t numFFIs() const {
-        MOZ_ASSERT(isFinished());
-        return pod.numFFIs_;
-    }
-    uint32_t srcEndBeforeCurly() const {
-        MOZ_ASSERT(isFinished());
-        return srcStart_ + pod.srcLength_;
-    }
-    uint32_t srcEndAfterCurly() const {
-        MOZ_ASSERT(isFinished());
-        return srcStart_ + pod.srcLengthWithRightBrace_;
-    }
-    bool staticallyLink(ExclusiveContext* cx) {
-        return wasm_->staticallyLink(cx, *linkData_);
-    }
-
-    // See WASM_DECLARE_SERIALIZABLE.
-    size_t serializedSize() const;
-    uint8_t* serialize(uint8_t* cursor) const;
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
-    bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
-    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
-                       size_t* asmJSModuleData);
-};
-
-// Store the just-parsed module in the cache using AsmJSCacheOps.
-extern JS::AsmJSCacheResult
-StoreAsmJSModuleInCache(AsmJSParser& parser,
-                        const AsmJSModule& module,
-                        ExclusiveContext* cx);
-
-// Attempt to load the asm.js module that is about to be parsed from the cache
-// using AsmJSCacheOps. The return value indicates whether an error was
-// reported. The loadedFromCache outparam indicates whether the module was
-// successfully loaded and stored in moduleObj.extern bool
-extern bool
-LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
-                         bool* loadedFromCache, UniqueChars* compilationTimeReport);
-
-// This function must be called for every detached ArrayBuffer.
-extern bool
-OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
-
-// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
-// directly to user script) which manages the lifetime of an AsmJSModule. A
-// JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be
-// able to point to their module via their extended slots.
-class AsmJSModuleObject : public NativeObject
-{
-    static const unsigned MODULE_SLOT = 0;
-
-  public:
-    static const unsigned RESERVED_SLOTS = 1;
-
-    static AsmJSModuleObject* create(ExclusiveContext* cx);
-
-    bool hasModule() const;
-    void setModule(AsmJSModule* module);
-    AsmJSModule& module() const;
-
-    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
-                       size_t* asmJSModuleData) {
-        module().addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData);
-    }
-
-    static const Class class_;
-};
-
-} // namespace js
-
-#endif /* asmjs_AsmJSModule_h */
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -13,17 +13,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmGenerator.h"
 
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "asmjs/WasmStubs.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -19,29 +19,33 @@
 #include "asmjs/WasmModule.h"
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/EnumeratedRange.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprf.h"
 
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "asmjs/WasmSerialize.h"
 #include "builtin/AtomicsObject.h"
+#include "builtin/SIMD.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/BaselineJIT.h"
 #include "jit/ExecutableAllocator.h"
+#include "jit/JitCommon.h"
 #include "js/MemoryMetrics.h"
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
+#include "jsobjinlines.h"
+
 #include "jit/MacroAssembler-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 using mozilla::BinarySearch;
@@ -492,16 +496,23 @@ Module::rawHeapPtr() const
 }
 
 uint8_t*&
 Module::rawHeapPtr()
 {
     return *(uint8_t**)(globalData() + HeapGlobalDataOffset);
 }
 
+WasmActivation*&
+Module::activation()
+{
+    MOZ_ASSERT(dynamicallyLinked_);
+    return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
+}
+
 void
 Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
 {
     MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
     MOZ_ASSERT(!maybeHeap_);
     MOZ_ASSERT(!rawHeapPtr());
 
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
@@ -620,16 +631,71 @@ Module::sendCodeRangesToProfiler(JSConte
             method.class_file_name = nullptr;
             method.source_file_name = nullptr;
             iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
         }
     }
 #endif
 }
 
+void
+Module::setProfilingEnabled(bool enabled, JSContext* cx)
+{
+    MOZ_ASSERT(dynamicallyLinked_);
+    MOZ_ASSERT(!activation());
+
+    if (profilingEnabled_ == enabled)
+        return;
+
+    // When enabled, generate profiling labels for every name in funcNames_
+    // that is the name of some Function CodeRange. This involves malloc() so
+    // do it now since, once we start sampling, we'll be in a signal-handing
+    // context where we cannot malloc.
+    if (enabled) {
+        funcLabels_.resize(funcNames_.length());
+        for (const CodeRange& codeRange : codeRanges_) {
+            if (!codeRange.isFunction())
+                continue;
+            unsigned lineno = codeRange.funcLineNumber();
+            const char* name = funcNames_[codeRange.funcNameIndex()].get();
+            funcLabels_[codeRange.funcNameIndex()] =
+                UniqueChars(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
+        }
+    } else {
+        funcLabels_.clear();
+    }
+
+    // Patch callsites and returns to execute profiling prologues/epililogues.
+    {
+        AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
+
+        for (const CallSite& callSite : callSites_)
+            EnableProfilingPrologue(*this, callSite, enabled);
+
+        for (const CodeRange& codeRange : codeRanges_)
+            EnableProfilingEpilogue(*this, codeRange, enabled);
+    }
+
+    // Update the function-pointer tables to point to profiling prologues.
+    for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
+        auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
+        for (size_t i = 0; i < funcPtrTable.numElems; i++) {
+            const CodeRange* codeRange = lookupCodeRange(array[i]);
+            void* from = code() + codeRange->funcNonProfilingEntry();
+            void* to = code() + codeRange->funcProfilingEntry();
+            if (!enabled)
+                Swap(from, to);
+            MOZ_ASSERT(array[i] == from);
+            array[i] = to;
+        }
+    }
+
+    profilingEnabled_ = enabled;
+}
+
 Module::ImportExit&
 Module::importToExit(const Import& import)
 {
     return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
 }
 
 /* static */ Module::CacheablePod
 Module::zeroPod()
@@ -946,23 +1012,16 @@ Module::dynamicallyLink(JSContext* cx, H
 
     // See AllocateCode comment above.
     ExecutableAllocator::makeExecutable(code(), pod.codeBytes_);
 
     sendCodeRangesToProfiler(cx);
     return true;
 }
 
-Module*
-Module::nextLinked() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    return next_;
-}
-
 ArrayBufferObjectMaybeShared*
 Module::maybeBuffer() const
 {
     MOZ_ASSERT(dynamicallyLinked_);
     return maybeHeap_;
 }
 
 SharedMem<uint8_t*>
@@ -988,23 +1047,16 @@ Module::deoptimizeImportExit(uint32_t im
     MOZ_ASSERT(dynamicallyLinked_);
     const Import& import = imports_[importIndex];
     ImportExit& exit = importToExit(import);
     exit.code = code() + import.interpExitCodeOffset();
     exit.baselineScript = nullptr;
 }
 
 bool
-Module::hasDetachedHeap() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    return pod.usesHeap_ && !maybeHeap_;
-}
-
-bool
 Module::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx)
 {
     MOZ_ASSERT(dynamicallyLinked_);
     MOZ_ASSERT(pod.usesHeap_);
 
     // Content JS should not be able to run (and change heap) from within an
     // interrupt callback, but in case it does, fail to change heap. Otherwise,
     // the heap can change at every single instruction which would prevent
@@ -1031,43 +1083,180 @@ Module::detachHeap(JSContext* cx)
     if (interrupted_) {
         JS_ReportError(cx, "attempt to detach from inside interrupt handler");
         return false;
     }
 
     // Even if this->active(), to reach here, the activation must have called
     // out via an import exit stub. FFI stubs check if heapDatum() is null on
     // reentry and throw an exception if so.
-    MOZ_ASSERT_IF(active(), activation()->exitReason() == ExitReason::ImportJit ||
-                            activation()->exitReason() == ExitReason::ImportInterp);
+    MOZ_ASSERT_IF(activation(), activation()->exitReason() == ExitReason::ImportJit ||
+                                activation()->exitReason() == ExitReason::ImportInterp);
 
     AutoMutateCode amc(cx, *this, "Module::detachHeap");
     despecializeFromHeap(maybeHeap_);
     return true;
 }
 
 void
 Module::setInterrupted(bool interrupted)
 {
     MOZ_ASSERT(dynamicallyLinked_);
     interrupted_ = interrupted;
 }
 
-WasmActivation*&
-Module::activation()
+Module*
+Module::nextLinked() const
+{
+    MOZ_ASSERT(dynamicallyLinked_);
+    return next_;
+}
+
+bool
+Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
-}
+
+    const Export& exp = exports_[exportIndex];
+
+    // Enable/disable profiling in the Module to match the current global
+    // profiling state. Don't do this if the Module is already active on the
+    // stack since this would leave the Module in a state where profiling is
+    // enabled but the stack isn't unwindable.
+    if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation())
+        setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
+
+    // The calling convention for an external call into wasm is to pass an
+    // array of 16-byte values where each value contains either a coerced int32
+    // (in the low word), a double value (in the low dword) or a SIMD vector
+    // value, with the coercions specified by the wasm signature. The external
+    // entry point unpacks this array into the system-ABI-specified registers
+    // and stack memory and then calls into the internal entry point. The return
+    // value is stored in the first element of the array (which, therefore, must
+    // have length >= 1).
+    Vector<Module::EntryArg, 8> coercedArgs(cx);
+    if (!coercedArgs.resize(Max<size_t>(1, exp.sig().args().length())))
+        return false;
+
+    RootedValue v(cx);
+    for (unsigned i = 0; i < exp.sig().args().length(); ++i) {
+        v = i < args.length() ? args[i] : UndefinedValue();
+        switch (exp.sig().arg(i)) {
+          case ValType::I32:
+            if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
+                return false;
+            break;
+          case ValType::I64:
+            MOZ_CRASH("int64");
+          case ValType::F32:
+            if (!RoundFloat32(cx, v, (float*)&coercedArgs[i]))
+                return false;
+            break;
+          case ValType::F64:
+            if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
+                return false;
+            break;
+          case ValType::I32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Int32x4>(cx, v, &simd))
+                return false;
+            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::F32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Float32x4>(cx, v, &simd))
+                return false;
+            memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::B32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Bool32x4>(cx, v, &simd))
+                return false;
+            // Bool32x4 uses the same representation as Int32x4.
+            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
+            break;
+          }
+        }
+    }
 
-Module::EntryFuncPtr
-Module::entryTrampoline(const Export& func) const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    return JS_DATA_TO_FUNC_PTR(EntryFuncPtr, code() + func.stubOffset());
+    // The correct way to handle this situation would be to allocate a new range
+    // of PROT_NONE memory and module.changeHeap to this memory. That would
+    // cause every access to take the out-of-bounds signal-handler path which
+    // does the right thing. For now, just throw an out-of-memory exception
+    // since these can technically pop out anywhere and the full fix may
+    // actually OOM when trying to allocate the PROT_NONE memory.
+    if (usesHeap() && !maybeHeap_) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+        return false;
+    }
+
+    {
+        // Push a WasmActivation to describe the wasm frames we're about to push
+        // when running this module. Additionally, push a JitActivation so that
+        // the optimized wasm-to-Ion FFI call path (which we want to be very
+        // fast) can avoid doing so. The JitActivation is marked as inactive so
+        // stack iteration will skip over it.
+        WasmActivation activation(cx, *this);
+        JitActivation jitActivation(cx, /* active */ false);
+
+        // Call the per-exported-function trampoline created by GenerateEntry.
+        auto entry = JS_DATA_TO_FUNC_PTR(EntryFuncPtr, code() + exp.stubOffset());
+        if (!CALL_GENERATED_2(entry, coercedArgs.begin(), globalData()))
+            return false;
+    }
+
+    if (args.isConstructing()) {
+        // By spec, when a function is called as a constructor and this function
+        // returns a primary type, which is the case for all wasm exported
+        // functions, the returned value is discarded and an empty object is
+        // returned instead.
+        PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx);
+        if (!obj)
+            return false;
+        args.rval().set(ObjectValue(*obj));
+        return true;
+    }
+
+    JSObject* simdObj;
+    switch (exp.sig().ret()) {
+      case ExprType::Void:
+        args.rval().set(UndefinedValue());
+        break;
+      case ExprType::I32:
+        args.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
+        break;
+      case ExprType::I64:
+        MOZ_CRASH("int64");
+      case ExprType::F32:
+      case ExprType::F64:
+        args.rval().set(NumberValue(*(double*)&coercedArgs[0]));
+        break;
+      case ExprType::I32x4:
+        simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]);
+        if (!simdObj)
+            return false;
+        args.rval().set(ObjectValue(*simdObj));
+        break;
+      case ExprType::F32x4:
+        simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
+        if (!simdObj)
+            return false;
+        args.rval().set(ObjectValue(*simdObj));
+        break;
+      case ExprType::B32x4:
+        simdObj = CreateSimd<Bool32x4>(cx, (int32_t*)&coercedArgs[0]);
+        if (!simdObj)
+            return false;
+        args.rval().set(ObjectValue(*simdObj));
+        break;
+    }
+
+    return true;
 }
 
 bool
 Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
                    MutableHandleValue rval)
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
@@ -1132,71 +1321,16 @@ Module::callImport(JSContext* cx, uint32
     if (!script->baselineScript()->addDependentWasmModule(cx, *this, importIndex))
         return false;
 
     exit.code = jitExitCode;
     exit.baselineScript = script->baselineScript();
     return true;
 }
 
-void
-Module::setProfilingEnabled(bool enabled, JSContext* cx)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT(!active());
-
-    if (profilingEnabled_ == enabled)
-        return;
-
-    // When enabled, generate profiling labels for every name in funcNames_
-    // that is the name of some Function CodeRange. This involves malloc() so
-    // do it now since, once we start sampling, we'll be in a signal-handing
-    // context where we cannot malloc.
-    if (enabled) {
-        funcLabels_.resize(funcNames_.length());
-        for (const CodeRange& codeRange : codeRanges_) {
-            if (!codeRange.isFunction())
-                continue;
-            unsigned lineno = codeRange.funcLineNumber();
-            const char* name = funcNames_[codeRange.funcNameIndex()].get();
-            funcLabels_[codeRange.funcNameIndex()] =
-                UniqueChars(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
-        }
-    } else {
-        funcLabels_.clear();
-    }
-
-    // Patch callsites and returns to execute profiling prologues/epililogues.
-    {
-        AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
-
-        for (const CallSite& callSite : callSites_)
-            EnableProfilingPrologue(*this, callSite, enabled);
-
-        for (const CodeRange& codeRange : codeRanges_)
-            EnableProfilingEpilogue(*this, codeRange, enabled);
-    }
-
-    // Update the function-pointer tables to point to profiling prologues.
-    for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
-        auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
-        for (size_t i = 0; i < funcPtrTable.numElems; i++) {
-            const CodeRange* codeRange = lookupCodeRange(array[i]);
-            void* from = code() + codeRange->funcNonProfilingEntry();
-            void* to = code() + codeRange->funcProfilingEntry();
-            if (!enabled)
-                Swap(from, to);
-            MOZ_ASSERT(array[i] == from);
-            array[i] = to;
-        }
-    }
-
-    profilingEnabled_ = enabled;
-}
-
 const char*
 Module::profilingLabel(uint32_t funcIndex) const
 {
     MOZ_ASSERT(dynamicallyLinked_);
     MOZ_ASSERT(profilingEnabled_);
     return funcLabels_[funcIndex].get();
 }
 
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -136,21 +136,18 @@ class Export
     }
 
     WASM_DECLARE_SERIALIZABLE(Export)
 };
 
 typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
 
 // An Import describes a wasm module import. Currently, only functions can be
-// imported in wasm and a function import also includes the signature used
-// within the module to call that import. An import is slightly different than
-// an asm.js FFI function: a single asm.js FFI function can be called with many
-// different signatures. When compiled to wasm, each unique FFI function paired
-// with signature generates a wasm import.
+// imported in wasm. A function import includes the signature used within the
+// module to call it.
 
 class Import
 {
     MallocSig sig_;
     struct CacheablePod {
         uint32_t exitGlobalDataOffset_;
         uint32_t interpExitCodeOffset_;
         uint32_t jitExitCodeOffset_;
@@ -340,29 +337,32 @@ AllocateCode(ExclusiveContext* cx, size_
 //    a cache file so that the cached code is stored unlinked and ready to be
 //    statically linked after deserialization.
 //
 //  - Dynamic linking patches code or global data that relies on the address of
 //    the heap and imports of a module. A module may only be dynamically linked
 //    once. However, a dynamically-linked module may be cloned so that the clone
 //    can be independently dynamically linked.
 //
-// Once fully dynamically linked, a module can have its exports invoked (via
-// entryTrampoline). While executing, profiling may be enabled/disabled (when
-// the Module is not active()) via setProfilingEnabled(). When profiling is
-// enabled, a module's frames will be visible to wasm::ProfilingFrameIterator.
+// Once fully dynamically linked, a Module can have its exports invoked via
+// callExport().
 
 class Module
 {
     struct ImportExit {
         void* code;
         jit::BaselineScript* baselineScript;
         HeapPtrFunction fun;
         static_assert(sizeof(HeapPtrFunction) == sizeof(void*), "for JIT access");
     };
+    struct EntryArg {
+        uint64_t lo;
+        uint64_t hi;
+    };
+    typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
     struct FuncPtrTable {
         uint32_t globalDataOffset;
         uint32_t numElems;
         explicit FuncPtrTable(const StaticLinkData::FuncPtrTable& table)
           : globalDataOffset(table.globalDataOffset),
             numElems(table.elemOffsets.length())
         {}
     };
@@ -409,19 +409,21 @@ class Module
     FuncLabelVector              funcLabels_;
     bool                         interrupted_;
 
     class AutoMutateCode;
 
     uint32_t totalBytes() const;
     uint8_t* rawHeapPtr() const;
     uint8_t*& rawHeapPtr();
+    WasmActivation*& activation();
     void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
     void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
     void sendCodeRangesToProfiler(JSContext* cx);
+    void setProfilingEnabled(bool enabled, JSContext* cx);
     ImportExit& importToExit(const Import& import);
 
     enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true };
     enum ProfilingBool { ProfilingDisabled = false, ProfilingEnabled = true };
 
     static CacheablePod zeroPod();
     void init();
     Module(const CacheablePod& pod,
@@ -434,20 +436,22 @@ class Module
            CacheableCharsVector&& funcNames,
            CacheableChars filename,
            CacheableTwoByteChars displayURL,
            CacheBool loadedFromCache,
            ProfilingBool profilingEnabled,
            FuncLabelVector&& funcLabels);
 
     template <class> friend struct js::MallocProvider;
+    friend class js::WasmActivation;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
+    static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
     enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
     enum SharedBool { UnsharedHeap = false, SharedHeap = true };
     enum MutedBool { DontMuteErrors = false, MuteErrors = true };
 
     Module(CompileArgs args,
            uint32_t functionBytes,
            uint32_t codeBytes,
@@ -477,17 +481,16 @@ class Module
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
     const char* functionName(uint32_t i) const { return funcNames_[i].get(); }
     const char* filename() const { return filename_.get(); }
     const char16_t* displayURL() const { return displayURL_.get(); }
     bool loadedFromCache() const { return loadedFromCache_; }
     bool staticallyLinked() const { return staticallyLinked_; }
     bool dynamicallyLinked() const { return dynamicallyLinked_; }
-    bool profilingEnabled() const { return profilingEnabled_; }
 
     // The range [0, functionBytes) is a subrange of [0, codeBytes) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const;
@@ -504,46 +507,39 @@ class Module
 
     // This function transitions the module from a statically-linked state to a
     // dynamically-linked state. If this module usesHeap(), a non-null heap
     // buffer must be given. The given import vector must match the module's
     // ImportVector.
 
     bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
                          const AutoVectorRooter<JSFunction*>& imports);
-    Module* nextLinked() const;
 
     // The wasm heap, established by dynamicallyLink.
 
     ArrayBufferObjectMaybeShared* maybeBuffer() const;
     SharedMem<uint8_t*> maybeHeap() const;
     size_t heapLength() const;
 
     // asm.js may detach and change the heap at any time. As an internal detail,
     // the heap may not be changed while the module has been asynchronously
     // interrupted.
+    //
+    // N.B. These methods and asm.js change-heap support will be removed soon.
 
-    bool hasDetachedHeap() const;
     bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
     bool detachHeap(JSContext* cx);
     void setInterrupted(bool interrupted);
+    Module* nextLinked() const;
 
     // The exports of a wasm module are called by preparing an array of
     // arguments (coerced to the corresponding types of the Export signature)
-    // and calling the export's entry trampoline. All such calls must be
-    // associated with a containing WasmActivation. The innermost
-    // WasmActivation must be maintained in the Module::activation field.
+    // and calling the export's entry trampoline.
 
-    struct EntryArg {
-        uint64_t lo;
-        uint64_t hi;
-    };
-    typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
-    EntryFuncPtr entryTrampoline(const Export& func) const;
-    WasmActivation*& activation();
+    bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
 
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
     // match up, callImport will patch the code to instead call through a thunk
     // directly into the JIT code. If the JIT code is released, the Module must
     // be notified so it can go back to the generic callImport.
 
     bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
@@ -552,22 +548,22 @@ class Module
 
     // At runtime, when $pc is in wasm function code (containsFunctionPC($pc)),
     // $pc may be moved abruptly to interrupt() or outOfBounds() by a signal
     // handler or SetContext() from another thread.
 
     uint8_t* interrupt() const { MOZ_ASSERT(staticallyLinked_); return interrupt_; }
     uint8_t* outOfBounds() const { MOZ_ASSERT(staticallyLinked_); return outOfBounds_; }
 
-    // When a module is inactive (no live activations), the profiling mode
-    // can be toggled. WebAssembly frames only show up in the
-    // ProfilingFrameIterator when profiling is enabled.
+    // Each Module has a profilingEnabled state which is updated to match
+    // SPSProfiler::enabled() on the next Module::callExport when there are no
+    // frames from the Module on the stack. The ProfilingFrameIterator only
+    // shows frames for Module activations that have profilingEnabled.
 
-    bool active() { return !!activation(); }
-    void setProfilingEnabled(bool enabled, JSContext* cx);
+    bool profilingEnabled() const { return profilingEnabled_; }
     const char* profilingLabel(uint32_t funcIndex) const;
 
     // See WASM_DECLARE_SERIALIZABLE.
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
     typedef UniquePtr<Module, JS::DeletePolicy<Module>> UniqueModule;
     static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
                                       UniqueModule* out);
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -16,17 +16,17 @@
  * limitations under the License.
  */
 
 #include "asmjs/WasmSignalHandlers.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/PodOperations.h"
 
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "asmjs/WasmModule.h"
 #include "jit/AtomicOperations.h"
 #include "jit/Disassembler.h"
 #include "vm/Runtime.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -165,30 +165,30 @@ GenerateEntry(ModuleGenerator& mg, unsig
     masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1)));
 
     // Bump the stack for the call.
     masm.reserveStack(AlignBytes(StackArgBytes(sig.args()), AsmJSStackAlignment));
 
     // Copy parameters out of argv and into the registers/stack-slots specified by
     // the system ABI.
     for (ABIArgValTypeIter iter(sig.args()); !iter.done(); iter++) {
-        unsigned argOffset = iter.index() * sizeof(Module::EntryArg);
+        unsigned argOffset = iter.index() * Module::SizeOfEntryArg;
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
         switch (iter->kind()) {
           case ABIArg::GPR:
             masm.load32(src, iter->gpr());
             break;
 #ifdef JS_CODEGEN_REGISTER_PAIR
           case ABIArg::GPR_PAIR:
-            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            MOZ_CRASH("wasm uses hardfp for function calls.");
             break;
 #endif
           case ABIArg::FPU: {
-            static_assert(sizeof(Module::EntryArg) >= jit::Simd128DataSize,
+            static_assert(Module::SizeOfEntryArg >= jit::Simd128DataSize,
                           "EntryArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType_Int32x4:
               case MIRType_Bool32x4:
                 masm.loadUnalignedInt32x4(src, iter->fpu());
                 break;
               case MIRType_Float32x4:
                 masm.loadUnalignedFloat32x4(src, iter->fpu());
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -64,17 +64,16 @@ HandleExecutionInterrupt()
 }
 
 } // namespace wasm
 } // namespace js
 
 static void
 OnDetached()
 {
-    // See hasDetachedHeap comment in LinkAsmJS.
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
 }
 
 static void
 OnOutOfBounds()
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -12,18 +12,17 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
-#include "asmjs/AsmJSLink.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitFrameIterator.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "frontend/BytecodeCompiler.h"
 
 #include "jscntxt.h"
 #include "jsscript.h"
 
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
 #include "builtin/ModuleObject.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/Parser.h"
 #include "vm/GlobalObject.h"
 #include "vm/TraceLogging.h"
 
@@ -658,17 +658,17 @@ BytecodeCompiler::compileFunctionBody(Mu
 
         if (!createEmitter(fn->pn_funbox) ||
             !emitter->emitFunctionScript(fn->pn_body))
         {
             return false;
         }
     } else {
         fun.set(fn->pn_funbox->function());
-        MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
+        MOZ_ASSERT(IsAsmJSModule(fun));
     }
 
     if (!maybeCompleteCompressSource())
         return false;
 
     return true;
 }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -24,17 +24,17 @@
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Stack.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
@@ -6358,17 +6358,17 @@ BytecodeEmitter::emitFunction(ParseNode*
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
                 script->setUsesArgumentsApplyAndThis();
         }
         if (outersc->isFunctionBox())
             outersc->asFunctionBox()->function()->nonLazyScript()->setHasInnerFunctions(true);
     } else {
-        MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
+        MOZ_ASSERT(IsAsmJSModule(fun));
     }
 
     /* Make the function object a literal in the outer script's pool. */
     unsigned index = objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     if (!pn->functionIsHoisted()) {
         /* JSOP_LAMBDA_ARROW is always preceded by a new.target */
@@ -7043,18 +7043,17 @@ BytecodeEmitter::emitStatement(ParseNode
             // "use strict" should warn for any "use strict" statements seen
             // later in the script, because such statements are misleading.
             const char* directive = nullptr;
             if (atom == cx->names().useStrict) {
                 if (!sc->strictScript)
                     directive = js_useStrict_str;
             } else if (atom == cx->names().useAsm) {
                 if (sc->isFunctionBox()) {
-                    JSFunction* fun = sc->asFunctionBox()->function();
-                    if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+                    if (IsAsmJSModule(sc->asFunctionBox()->function()))
                         directive = js_useAsm_str;
                 }
             }
 
             if (directive) {
                 if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
                     return false;
             }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -23,17 +23,17 @@
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jstypes.h"
 
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "builtin/ModuleObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/TokenStream.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
@@ -3310,17 +3310,17 @@ Parser<FullParseHandler>::asmJS(Node lis
     pc->sc->asFunctionBox()->useAsm = true;
 
     // Attempt to validate and compile this asm.js module. On success, the
     // tokenStream has been advanced to the closing }. On failure, the
     // tokenStream is in an indeterminate state and we must reparse the
     // function from the beginning. Reparsing is triggered by marking that a
     // new directive has been encountered and returning 'false'.
     bool validated;
-    if (!ValidateAsmJS(context, *this, list, &validated))
+    if (!CompileAsmJS(context, *this, list, &validated))
         return false;
     if (!validated) {
         pc->newDirectives->setAsmJS();
         return false;
     }
 
     return true;
 }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12760,17 +12760,17 @@ IonBuilder::jsop_object(JSObject* obj)
 }
 
 bool
 IonBuilder::jsop_lambda(JSFunction* fun)
 {
     MOZ_ASSERT(analysis().usesScopeChain());
     MOZ_ASSERT(!fun->isArrow());
 
-    if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+    if (IsAsmJSModule(fun))
         return abort("asm.js module function");
 
     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
     current->add(cst);
     MLambda* ins = MLambda::New(alloc(), constraints(), current->scopeChain(), cst);
     current->add(ins);
     current->push(ins);
 
@@ -12912,17 +12912,17 @@ IonBuilder::jsop_deflexical(uint32_t ind
 
     return resumeAfter(deflex);
 }
 
 bool
 IonBuilder::jsop_deffun(uint32_t index)
 {
     JSFunction* fun = script()->getFunction(index);
-    if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+    if (IsAsmJSModule(fun))
         return abort("asm.js module function");
 
     MOZ_ASSERT(analysis().usesScopeChain());
 
     MDefFun* deffun = MDefFun::New(alloc(), fun, current->scopeChain());
     current->add(deffun);
 
     return resumeAfter(deffun);
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -644,17 +644,17 @@ static inline bool UseHardFpABI()
 #endif
 }
 #endif
 
 // In order to handle SoftFp ABI calls, we need to be able to express that we
 // have ABIArg which are represented by pair of general purpose registers.
 #define JS_CODEGEN_REGISTER_PAIR 1
 
-// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
+// See the comments above AsmJSMappedSize in AsmJS.cpp for more info.
 // TODO: Implement this for ARM. Note that it requires Codegen to respect the
 // offset field of AsmJSHeapAccess.
 static const size_t AsmJSCheckedImmediateRange = 0;
 static const size_t AsmJSImmediateRange = 0;
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/mips-shared/Architecture-mips-shared.h
+++ b/js/src/jit/mips-shared/Architecture-mips-shared.h
@@ -317,17 +317,17 @@ hasUnaliasedDouble() {
 // On MIPS, fn-double aliases both fn-float32 and fn+1-float32, so if you need
 // to convert a float32 to a double as a temporary, you need a temporary
 // double register.
 inline bool
 hasMultiAlias() {
     return true;
 }
 
-// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
+// See the comments above AsmJSMappedSize in AsmJS.cpp for more info.
 // TODO: Implement this for MIPS. Note that it requires Codegen to respect the
 // offset field of AsmJSHeapAccess.
 static const size_t AsmJSCheckedImmediateRange = 0;
 static const size_t AsmJSImmediateRange = 0;
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/x86-shared/Architecture-x86-shared.h
+++ b/js/src/jit/x86-shared/Architecture-x86-shared.h
@@ -442,16 +442,16 @@ hasUnaliasedDouble()
 // to a double as a temporary, you need a temporary double register.
 inline bool
 hasMultiAlias()
 {
     return false;
 }
 
 // Support some constant-offset addressing.
-// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
+// See the comments above AsmJSMappedSize in AsmJS.cpp for more info.
 static const size_t AsmJSCheckedImmediateRange = 4096;
 static const size_t AsmJSImmediateRange = UINT32_C(0x80000000);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_shared_Architecture_x86_h */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -38,17 +38,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
 #include "builtin/AtomicsObject.h"
 #include "builtin/Eval.h"
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
 #include "builtin/SymbolObject.h"
 #ifdef ENABLE_SIMD
 # include "builtin/SIMD.h"
@@ -3512,17 +3512,17 @@ CloneFunctionObject(JSContext* cx, Handl
         return nullptr;
     }
 
     if (fun->isBoundFunction()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
 
-    if (fun->isNative() && IsAsmJSModuleNative(fun->native())) {
+    if (IsAsmJSModule(fun)) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
 
     if (CanReuseScriptForClone(cx->compartment(), fun, dynamicScope)) {
         // If the script is to be reused, either the script can already handle
         // non-syntactic scopes, or there is only the standard global lexical
         // scope.
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -690,16 +690,28 @@ class FunctionExtended : public JSFuncti
   public:
     static const unsigned NUM_EXTENDED_SLOTS = 2;
 
     /* Arrow functions store their lexical new.target in the first extended slot. */
     static const unsigned ARROW_NEWTARGET_SLOT = 0;
 
     static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
 
+    /*
+     * All asm.js/wasm functions store their compiled module (either
+     * WasmModuleObject or AsmJSModuleObject) in the first extended slot.
+     */
+    static const unsigned ASM_MODULE_SLOT = 0;
+
+    /*
+     * wasm/asm.js exported functions store the index of the export in the
+     * module's export vector in the second slot.
+     */
+    static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
+
     static inline size_t offsetOfExtendedSlot(unsigned which) {
         MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
         return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
     }
     static inline size_t offsetOfArrowNewTargetSlot() {
         return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,17 +33,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jswin.h"
 #include "jswrapper.h"
 
-#include "asmjs/AsmJSModule.h"
+#include "asmjs/AsmJS.h"
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/Proxy.h"
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsscriptinlines_h
 #define jsscriptinlines_h
 
 #include "jsscript.h"
 
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonAnalysis.h"
 #include "vm/ScopeObject.h"
 
 #include "jscompartmentinlines.h"
 
 #include "vm/Shape-inl.h"
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -137,19 +137,17 @@ EXPORTS.js += [
     '../public/UbiNodePostOrder.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
-    'asmjs/AsmJSLink.cpp',
-    'asmjs/AsmJSModule.cpp',
-    'asmjs/AsmJSValidate.cpp',
+    'asmjs/AsmJS.cpp',
     'asmjs/WasmFrameIterator.cpp',
     'asmjs/WasmGenerator.cpp',
     'asmjs/WasmIonCompile.cpp',
     'asmjs/WasmModule.cpp',
     'asmjs/WasmSignalHandlers.cpp',
     'asmjs/WasmStubs.cpp',
     'asmjs/WasmTypes.cpp',
     'builtin/AtomicsObject.cpp',
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -29,18 +29,17 @@
 #include "jsobj.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
-#include "asmjs/AsmJSModule.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/SharedArrayObject.h"
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -17,17 +17,17 @@
 #include "jswrapper.h"
 #ifndef XP_WIN
 # include <sys/mman.h>
 #endif
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
 #include "vm/SharedMem.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;