https://github.com/mozilla/gecko-dev
Raw File
Tip revision: 7d573c967f2bdc44ad44e754192674fd3d204a19 authored by ffxbld on 03 March 2016, 21:59:23 UTC
Added FENNEC_45_0_RELEASE FENNEC_45_0_BUILD3 tag(s) for changeset 018c9e0e7da3. DONTBUILD CLOSED TREE a=release
Tip revision: 7d573c9
AsmJSModule.h
/* -*- 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 "jsscript.h"

#include "asmjs/AsmJSFrameIterator.h"
#include "asmjs/AsmJSValidate.h"
#include "asmjs/Wasm.h"
#include "builtin/SIMD.h"
#include "gc/Tracer.h"
#ifdef JS_ION_PERF
# include "jit/PerfSpewer.h"
#endif
#include "vm/TypedArrayObject.h"

namespace js {

namespace frontend { class TokenStream; }
namespace jit { struct BaselineScript; class MacroAssembler; }

// 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
};

// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
enum AsmJSSimdOperation
{
#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
    FORALL_SIMD_OP(ASMJSSIMDOPERATION)
#undef ASMJSSIMDOPERATION
};

// An asm.js module represents the collection of functions nested inside a
// single outer "use asm" function. For example, this asm.js module:
//   function() { "use asm"; function f() {} function g() {} return f }
// contains the functions 'f' and 'g'.
//
// An asm.js module contains both the jit-code produced by compiling all the
// functions in the module as well all the data required to perform the
// link-time validation step in the asm.js spec.
//
// NB: this means that AsmJSModule must be GC-safe.
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 Pod {
            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_;
        }

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, Global* out) const;
    };

    // An Exit holds bookkeeping information about an exit; the ExitDatum
    // struct overlays the actual runtime data stored in the global data
    // section.

    struct ExitDatum
    {
        uint8_t* exit;
        jit::BaselineScript* baselineScript;
        HeapPtrFunction fun;
    };

    class Exit
    {
        wasm::MallocSig sig_;
        struct Pod {
            unsigned ffiIndex_;
            unsigned globalDataOffset_;
            unsigned interpCodeOffset_;
            unsigned jitCodeOffset_;
        } pod;

      public:
        Exit() {}
        Exit(Exit&& rhs) : sig_(Move(rhs.sig_)), pod(rhs.pod) {}
        Exit(wasm::MallocSig&& sig, unsigned ffiIndex, unsigned globalDataOffset)
          : sig_(Move(sig))
        {
            pod.ffiIndex_ = ffiIndex;
            pod.globalDataOffset_ = globalDataOffset;
            pod.interpCodeOffset_ = 0;
            pod.jitCodeOffset_ = 0;
        }
        const wasm::MallocSig& sig() const {
            return sig_;
        }
        unsigned ffiIndex() const {
            return pod.ffiIndex_;
        }
        unsigned globalDataOffset() const {
            return pod.globalDataOffset_;
        }
        void initInterpOffset(unsigned off) {
            MOZ_ASSERT(!pod.interpCodeOffset_);
            pod.interpCodeOffset_ = off;
        }
        void initJitOffset(unsigned off) {
            MOZ_ASSERT(!pod.jitCodeOffset_);
            pod.jitCodeOffset_ = off;
        }
        ExitDatum& datum(const AsmJSModule& module) const {
            return *reinterpret_cast<ExitDatum*>(module.globalData() + pod.globalDataOffset_);
        }
        void initDatum(const AsmJSModule& module) const {
            MOZ_ASSERT(pod.interpCodeOffset_);
            ExitDatum& d = datum(module);
            d.exit = module.codeBase() + pod.interpCodeOffset_;
            d.baselineScript = nullptr;
            d.fun = nullptr;
        }
        bool isOptimized(const AsmJSModule& module) const {
            return datum(module).exit == module.codeBase() + pod.jitCodeOffset_;
        }
        void optimize(const AsmJSModule& module, jit::BaselineScript* baselineScript) const {
            ExitDatum& d = datum(module);
            d.exit = module.codeBase() + pod.jitCodeOffset_;
            d.baselineScript = baselineScript;
        }
        void deoptimize(const AsmJSModule& module) const {
            ExitDatum& d = datum(module);
            d.exit = module.codeBase() + pod.interpCodeOffset_;
            d.baselineScript = nullptr;
        }

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, Exit* out) const;
    };

    struct EntryArg {
        uint64_t lo;
        uint64_t hi;
    };

    typedef int32_t (*CodePtr)(EntryArg* args, uint8_t* global);

    class ExportedFunction
    {
        PropertyName* name_;
        PropertyName* maybeFieldName_;
        wasm::MallocSig sig_;
        struct Pod {
            bool isChangeHeap_;
            uint32_t funcIndex_;
            uint32_t codeOffset_;
            uint32_t startOffsetInModule_;  // Store module-start-relative offsets
            uint32_t endOffsetInModule_;    // so preserved by serialization.
        } pod;

        friend class AsmJSModule;

        ExportedFunction(PropertyName* name, uint32_t funcIndex,
                         uint32_t startOffsetInModule, uint32_t endOffsetInModule,
                         PropertyName* maybeFieldName,
                         wasm::MallocSig&& sig)
         : name_(name),
           maybeFieldName_(maybeFieldName),
           sig_(Move(sig))
        {
            MOZ_ASSERT(name_->isTenured());
            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
            mozilla::PodZero(&pod);  // zero padding for Valgrind
            pod.funcIndex_ = funcIndex;
            pod.isChangeHeap_ = false;
            pod.codeOffset_ = UINT32_MAX;
            pod.startOffsetInModule_ = startOffsetInModule;
            pod.endOffsetInModule_ = endOffsetInModule;
        }

        ExportedFunction(PropertyName* name,
                         uint32_t startOffsetInModule, uint32_t endOffsetInModule,
                         PropertyName* maybeFieldName)
          : name_(name),
            maybeFieldName_(maybeFieldName)
        {
            MOZ_ASSERT(name_->isTenured());
            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
            mozilla::PodZero(&pod);  // zero padding for Valgrind
            pod.isChangeHeap_ = true;
            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");
        }

      public:
        ExportedFunction() {}
        ExportedFunction(ExportedFunction&& rhs)
          : name_(rhs.name_),
            maybeFieldName_(rhs.maybeFieldName_),
            sig_(mozilla::Move(rhs.sig_))
        {
            mozilla::PodZero(&pod);  // zero padding for Valgrind
            pod = rhs.pod;
        }

        PropertyName* name() const {
            return name_;
        }
        PropertyName* maybeFieldName() const {
            return maybeFieldName_;
        }
        uint32_t startOffsetInModule() const {
            return pod.startOffsetInModule_;
        }
        uint32_t endOffsetInModule() const {
            return pod.endOffsetInModule_;
        }
        bool isChangeHeap() const {
            return pod.isChangeHeap_;
        }
        uint32_t funcIndex() const {
            MOZ_ASSERT(!isChangeHeap());
            return pod.funcIndex_;
        }
        void initCodeOffset(unsigned off) {
            MOZ_ASSERT(!isChangeHeap());
            MOZ_ASSERT(pod.codeOffset_ == UINT32_MAX);
            pod.codeOffset_ = off;
        }
        const wasm::MallocSig& sig() const {
            MOZ_ASSERT(!isChangeHeap());
            return sig_;
        }

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, ExportedFunction* out) const;
    };

    class CodeRange
    {
      protected:
        uint32_t nameIndex_;

      private:
        uint32_t lineNumber_;
        uint32_t begin_;
        uint32_t profilingReturn_;
        uint32_t end_;
        union {
            struct {
                uint8_t kind_;
                uint8_t beginToEntry_;
                uint8_t profilingJumpToProfilingReturn_;
                uint8_t profilingEpilogueToProfilingReturn_;
            } func;
            struct {
                uint8_t kind_;
                uint16_t target_;
            } thunk;
            uint8_t kind_;
        } u;

        void assertValid();

      public:
        enum Kind { Function, Entry, JitFFI, SlowFFI, Interrupt, Thunk, Inline };

        CodeRange() {}
        CodeRange(Kind kind, AsmJSOffsets offsets);
        CodeRange(Kind kind, AsmJSProfilingOffsets offsets);
        CodeRange(wasm::Builtin builtin, AsmJSProfilingOffsets offsets);
        CodeRange(uint32_t lineNumber, AsmJSFunctionOffsets offsets);

        Kind kind() const { return Kind(u.kind_); }
        bool isFunction() const { return kind() == Function; }
        bool isEntry() const { return kind() == Entry; }
        bool isFFI() const { return kind() == JitFFI || kind() == SlowFFI; }
        bool isInterrupt() const { return kind() == Interrupt; }
        bool isThunk() const { return kind() == Thunk; }

        uint32_t begin() const {
            return begin_;
        }
        uint32_t profilingEntry() const {
            return begin();
        }
        uint32_t entry() const {
            MOZ_ASSERT(isFunction());
            return begin_ + u.func.beginToEntry_;
        }
        uint32_t end() const {
            return end_;
        }
        uint32_t profilingJump() const {
            MOZ_ASSERT(isFunction());
            return profilingReturn_ - u.func.profilingJumpToProfilingReturn_;
        }
        uint32_t profilingEpilogue() const {
            MOZ_ASSERT(isFunction());
            return profilingReturn_ - u.func.profilingEpilogueToProfilingReturn_;
        }
        uint32_t profilingReturn() const {
            MOZ_ASSERT(isFunction() || isFFI() || isInterrupt() || isThunk());
            return profilingReturn_;
        }
        void initNameIndex(uint32_t nameIndex) {
            MOZ_ASSERT(nameIndex_ == UINT32_MAX);
            nameIndex_ = nameIndex;
        }
        uint32_t functionNameIndex() const {
            MOZ_ASSERT(isFunction());
            MOZ_ASSERT(nameIndex_ != UINT32_MAX);
            return nameIndex_;
        }
        PropertyName* functionName(const AsmJSModule& module) const {
            return module.names_[functionNameIndex()].name();
        }
        const char* functionProfilingLabel(const AsmJSModule& module) const {
            MOZ_ASSERT(isFunction());
            return module.profilingLabels_[nameIndex_].get();
        }
        uint32_t functionLineNumber() const {
            MOZ_ASSERT(isFunction());
            return lineNumber_;
        }
        void functionOffsetBy(uint32_t offset) {
            MOZ_ASSERT(isFunction());
            begin_ += offset;
            profilingReturn_ += offset;
            end_ += offset;
        }
        wasm::Builtin thunkTarget() const {
            MOZ_ASSERT(isThunk());
            return wasm::Builtin(u.thunk.target_);
        }
    };

    class Name
    {
        PropertyName* name_;
      public:
        Name() : name_(nullptr) {}
        MOZ_IMPLICIT Name(PropertyName* name) : name_(name) {}
        PropertyName* name() const { return name_; }
        PropertyName*& name() { return name_; }
        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, Name* out) const;
    };

    typedef mozilla::UniquePtr<char[], JS::FreePolicy> ProfilingLabel;

#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
    // Function information to add to the VTune JIT profiler following linking.
    struct ProfiledFunction
    {
        PropertyName* name;
        struct Pod {
            unsigned startCodeOffset;
            unsigned endCodeOffset;
            unsigned lineno;
            unsigned columnIndex;
        } pod;

        explicit ProfiledFunction()
          : name(nullptr)
        { }

        ProfiledFunction(PropertyName* name, unsigned start, unsigned end,
                         unsigned line = 0, unsigned column = 0)
          : name(name)
        {
            MOZ_ASSERT(name->isTenured());

            pod.startCodeOffset = start;
            pod.endCodeOffset = end;
            pod.lineno = line;
            pod.columnIndex = column;
        }

        void trace(JSTracer* trc) {
            if (name)
                TraceManuallyBarrieredEdge(trc, &name, "asm.js profiled function name");
        }

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
    };
#endif

    struct RelativeLink
    {
        enum Kind
        {
            RawPointer,
            CodeLabel,
            InstructionImmediate
        };

        RelativeLink()
        { }

#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
        // On MIPS, CodeLabels are instruction immediates so RelativeLinks only
        // patch instruction immediates.
        explicit RelativeLink(Kind kind) {
            MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
        }
        bool isRawPointerPatch() {
            return false;
        }
#else
        // On the rest, CodeLabels are raw pointers so RelativeLinks only patch
        // raw pointers.
        explicit RelativeLink(Kind kind) {
            MOZ_ASSERT(kind == CodeLabel || kind == RawPointer);
        }
        bool isRawPointerPatch() {
            return true;
        }
#endif

        uint32_t patchAtOffset;
        uint32_t targetOffset;
    };

    typedef Vector<RelativeLink, 0, SystemAllocPolicy> RelativeLinkVector;

    typedef mozilla::EnumeratedArray<wasm::Builtin,
                                     wasm::Builtin::Limit,
                                     uint32_t> BuiltinThunkOffsetArray;

    typedef Vector<uint32_t, 0, SystemAllocPolicy> OffsetVector;
    typedef mozilla::EnumeratedArray<wasm::SymbolicAddress,
                                     wasm::SymbolicAddress::Limit,
                                     OffsetVector> OffsetVectorArray;

    struct AbsoluteLinkArray : public OffsetVectorArray
    {
        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, AbsoluteLinkArray* out) const;

        size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
    };

    class FuncPtrTable
    {
        struct Pod {
            uint32_t globalDataOffset_;
        } pod;
        OffsetVector elemOffsets_;

      public:
        FuncPtrTable() {}
        FuncPtrTable(FuncPtrTable&& rhs) : pod(rhs.pod), elemOffsets_(Move(rhs.elemOffsets_)) {}
        explicit FuncPtrTable(uint32_t globalDataOffset) { pod.globalDataOffset_ = globalDataOffset; }
        void define(OffsetVector&& elemOffsets) { elemOffsets_ = Move(elemOffsets); }
        uint32_t globalDataOffset() const { return pod.globalDataOffset_; }
        const OffsetVector& elemOffsets() const { return elemOffsets_; }

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, FuncPtrTable* out) const;

        size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
    };

    typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;

    // Static-link data is used to patch a module either after it has been
    // compiled or deserialized with various absolute addresses (of code or
    // data in the process) or relative addresses (of code or data in the same
    // AsmJSModule).
    struct StaticLinkData
    {
        StaticLinkData() { mozilla::PodZero(&pod); }

        struct Pod {
            uint32_t interruptExitOffset;
            uint32_t outOfBoundsExitOffset;
            BuiltinThunkOffsetArray builtinThunkOffsets;
        } pod;

        RelativeLinkVector relativeLinks;
        AbsoluteLinkArray absoluteLinks;
        FuncPtrTableVector funcPtrTables;

        size_t serializedSize() const;
        uint8_t* serialize(uint8_t* cursor) const;
        const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
        bool clone(ExclusiveContext* cx, StaticLinkData* out) const;

        size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
    };

  private:
    struct Pod {
        uint32_t                          functionBytes_;
        uint32_t                          codeBytes_;
        uint32_t                          globalBytes_;
        uint32_t                          totalBytes_;
        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_;
        bool                              canUseSignalHandlers_;
    } pod;

    // These two fields need to be kept out pod as they depend on the position
    // of the module within the ScriptSource and thus aren't invariant with
    // respect to caching.
    const uint32_t                        srcStart_;
    const uint32_t                        srcBodyStart_;

    Vector<Global,                 0, SystemAllocPolicy> globals_;
    Vector<Exit,                   0, SystemAllocPolicy> exits_;
    Vector<ExportedFunction,       0, SystemAllocPolicy> exports_;
    Vector<wasm::CallSite,         0, SystemAllocPolicy> callSites_;
    Vector<CodeRange,              0, SystemAllocPolicy> codeRanges_;
    Vector<Name,                   0, SystemAllocPolicy> names_;
    Vector<ProfilingLabel,         0, SystemAllocPolicy> profilingLabels_;
    Vector<wasm::HeapAccess,       0, SystemAllocPolicy> heapAccesses_;
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
    Vector<ProfiledFunction,       0, SystemAllocPolicy> profiledFunctions_;
#endif

    ScriptSource *                        scriptSource_;
    PropertyName *                        globalArgumentName_;
    PropertyName *                        importArgumentName_;
    PropertyName *                        bufferArgumentName_;
    uint8_t *                             code_;
    uint8_t *                             interruptExit_;
    uint8_t *                             outOfBoundsExit_;
    StaticLinkData                        staticLinkData_;
    RelocatablePtrArrayBufferObjectMaybeShared maybeHeap_;
    AsmJSModule **                        prevLinked_;
    AsmJSModule *                         nextLinked_;
    bool                                  dynamicallyLinked_;
    bool                                  loadedFromCache_;
    bool                                  profilingEnabled_;
    bool                                  interrupted_;

    void restoreHeapToInitialState(ArrayBufferObjectMaybeShared* maybePrevBuffer);
    void restoreToInitialState(ArrayBufferObjectMaybeShared* maybePrevBuffer, uint8_t* prevCode,
                               ExclusiveContext* cx);

  public:
    explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
                         bool strict, bool canUseSignalHandlers);
    void trace(JSTracer* trc);
    ~AsmJSModule();

    // An AsmJSModule transitions from !finished to finished to dynamically linked.
    bool isFinished() const { return !!code_; }
    bool isDynamicallyLinked() const { return dynamicallyLinked_; }

    /*************************************************************************/
    // These functions may be used as soon as the module is constructed:

    ScriptSource* scriptSource() const {
        MOZ_ASSERT(scriptSource_);
        return scriptSource_;
    }
    bool strict() const {
        return pod.strict_;
    }
    bool canUseSignalHandlers() const {
        return pod.canUseSignalHandlers_;
    }
    bool usesSignalHandlersForInterrupt() const {
        return pod.canUseSignalHandlers_;
    }
    bool usesSignalHandlersForOOB() const {
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
        return pod.canUseSignalHandlers_;
#else
        return false;
#endif
    }
    bool loadedFromCache() const {
        return loadedFromCache_;
    }

    // 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_;
    }

    // about:memory reporting
    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
                       size_t* asmJSModuleData);

    /*************************************************************************/
    // These functions build the global scope of the module while parsing the
    // module prologue (before the function bodies):

    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_;
    }

    /*************************************************************************/
    // These functions may only be called before finish():

  private:
    bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset) {
        MOZ_ASSERT(!isFinished());
        uint32_t pad = ComputeByteAlignment(pod.globalBytes_, align);
        if (UINT32_MAX - pod.globalBytes_ < pad + bytes)
            return false;
        pod.globalBytes_ += pad;
        *globalDataOffset = pod.globalBytes_;
        pod.globalBytes_ += bytes;
        return true;
    }
    bool addGlobalVar(wasm::ValType type, uint32_t* globalDataOffset) {
        MOZ_ASSERT(!isFinished());
        unsigned width = 0;
        switch (type) {
          case wasm::ValType::I32:   case wasm::ValType::F32:   width = 4;  break;
          case wasm::ValType::I64:   case wasm::ValType::F64:   width = 8;  break;
          case wasm::ValType::I32x4: case wasm::ValType::F32x4: width = 16; break;
        }
        return allocateGlobalBytes(width, width, globalDataOffset);
    }
  public:
    bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalDataOffset) {
        MOZ_ASSERT(!isFinished());
        if (!addGlobalVar(v.type(), globalDataOffset))
            return false;
        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());
        if (!addGlobalVar(importType, globalDataOffset))
            return false;
        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);
    }
    unsigned numGlobals() const {
        return globals_.length();
    }
    Global& global(unsigned i) {
        return globals_[i];
    }
    void setViewsAreShared() {
        if (pod.hasArrayView_)
            pod.isSharedView_ = true;
    }

    /*************************************************************************/
    // These functions are called while parsing/compiling function bodies:

    bool hasArrayView() const {
        return pod.hasArrayView_;
    }
    bool isSharedView() const {
        MOZ_ASSERT(pod.hasArrayView_);
        return pod.isSharedView_;
    }
    void 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;
    }
    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;
    }
    bool addCodeRange(CodeRange::Kind kind, AsmJSOffsets offsets) {
        return codeRanges_.append(CodeRange(kind, offsets));
    }
    bool addCodeRange(CodeRange::Kind kind, AsmJSProfilingOffsets offsets) {
        return codeRanges_.append(CodeRange(kind, offsets));
    }
    bool addFunctionCodeRange(PropertyName* name, CodeRange codeRange) {
        MOZ_ASSERT(!isFinished());
        MOZ_ASSERT(name->isTenured());
        if (names_.length() >= UINT32_MAX)
            return false;
        codeRange.initNameIndex(names_.length());
        return names_.append(name) && codeRanges_.append(codeRange);
    }
    bool addBuiltinThunkCodeRange(wasm::Builtin builtin, AsmJSProfilingOffsets offsets) {
        MOZ_ASSERT(staticLinkData_.pod.builtinThunkOffsets[builtin] == 0);
        staticLinkData_.pod.builtinThunkOffsets[builtin] = offsets.begin;
        return codeRanges_.append(CodeRange(builtin, offsets));
    }
    bool addExit(wasm::MallocSig&& sig, unsigned ffiIndex, unsigned* exitIndex) {
        MOZ_ASSERT(!isFinished());
        static_assert(sizeof(ExitDatum) % sizeof(void*) == 0, "word aligned");
        uint32_t globalDataOffset;
        if (!allocateGlobalBytes(sizeof(ExitDatum), sizeof(void*), &globalDataOffset))
            return false;
        *exitIndex = unsigned(exits_.length());
        return exits_.append(Exit(Move(sig), ffiIndex, globalDataOffset));
    }
    unsigned numExits() const {
        return exits_.length();
    }
    Exit& exit(unsigned i) {
        return exits_[i];
    }
    const Exit& exit(unsigned i) const {
        return exits_[i];
    }
    bool declareFuncPtrTable(unsigned numElems, uint32_t* funcPtrTableIndex) {
        MOZ_ASSERT(!isFinished());
        MOZ_ASSERT(IsPowerOfTwo(numElems));
        uint32_t globalDataOffset;
        if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), &globalDataOffset))
            return false;
        *funcPtrTableIndex = staticLinkData_.funcPtrTables.length();
        return staticLinkData_.funcPtrTables.append(FuncPtrTable(globalDataOffset));
    }
    FuncPtrTable& funcPtrTable(uint32_t funcPtrTableIndex) {
        return staticLinkData_.funcPtrTables[funcPtrTableIndex];
    }
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
    bool addProfiledFunction(ProfiledFunction func) {
        MOZ_ASSERT(!isFinished());
        return profiledFunctions_.append(func);
    }
    unsigned numProfiledFunctions() const {
        return profiledFunctions_.length();
    }
    ProfiledFunction& profiledFunction(unsigned i) {
        return profiledFunctions_[i];
    }
#endif

    bool addExportedFunction(PropertyName* name,
                             uint32_t funcIndex,
                             uint32_t funcSrcBegin,
                             uint32_t funcSrcEnd,
                             PropertyName* maybeFieldName,
                             wasm::MallocSig&& sig)
    {
        // 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);
        ExportedFunction func(name, funcIndex, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
                              maybeFieldName, mozilla::Move(sig));
        return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
    }
    bool addExportedChangeHeap(PropertyName* name,
                               uint32_t funcSrcBegin,
                               uint32_t funcSrcEnd,
                               PropertyName* maybeFieldName)
    {
        // See addExportedFunction.
        MOZ_ASSERT(!isFinished());
        MOZ_ASSERT(srcStart_ < funcSrcBegin);
        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
        ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
                              maybeFieldName);
        return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
    }
    unsigned numExportedFunctions() const {
        return exports_.length();
    }
    const ExportedFunction& exportedFunction(unsigned i) const {
        return exports_[i];
    }
    ExportedFunction& exportedFunction(unsigned i) {
        return exports_[i];
    }
    void setAsyncInterruptOffset(uint32_t o) {
        staticLinkData_.pod.interruptExitOffset = o;
    }
    void setOnOutOfBoundsExitOffset(uint32_t o) {
        staticLinkData_.pod.outOfBoundsExitOffset = o;
    }

    /*************************************************************************/

    // finish() is called once the entire module has been parsed (via
    // tokenStream) and all function and entry/exit trampolines have been
    // generated (via masm). After this function, the module must still be
    // statically and dynamically linked before code can be run.
    bool finish(ExclusiveContext* cx, frontend::TokenStream& ts, jit::MacroAssembler& masm);

    /*************************************************************************/
    // These accessor functions can be used after finish():

    uint8_t* codeBase() const {
        MOZ_ASSERT(isFinished());
        MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
        return code_;
    }
    uint32_t codeBytes() const {
        MOZ_ASSERT(isFinished());
        return pod.codeBytes_;
    }
    bool containsCodePC(void* pc) const {
        MOZ_ASSERT(isFinished());
        return pc >= code_ && pc < (code_ + codeBytes());
    }

    // 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.
    void setFunctionBytes(uint32_t functionBytes) {
        MOZ_ASSERT(!isFinished());
        MOZ_ASSERT(!pod.functionBytes_);
        pod.functionBytes_ = functionBytes;
    }
    uint32_t functionBytes() const {
        MOZ_ASSERT(isFinished());
        return pod.functionBytes_;
    }
    bool containsFunctionPC(void* pc) const {
        MOZ_ASSERT(isFinished());
        return pc >= code_ && pc < (code_ + functionBytes());
    }

    uint32_t globalBytes() const {
        MOZ_ASSERT(isFinished());
        return pod.globalBytes_;
    }

    unsigned 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_;
    }

    // Lookup a callsite by the return pc (from the callee to the caller).
    // Return null if no callsite was found.
    const wasm::CallSite* lookupCallSite(void* returnAddress) const;

    // Lookup the name the code range containing the given pc. Return null if no
    // code range was found.
    const CodeRange* lookupCodeRange(void* pc) const;

    // Lookup a heap access site by the pc which performs the access. Return
    // null if no heap access was found.
    const wasm::HeapAccess* lookupHeapAccess(void* pc) const;

    // The global data section is placed after the executable code (i.e., at
    // offset codeBytes_) in the module's linear allocation. The global data
    // starts with some fixed allocations followed by interleaved global,
    // function-pointer table and exit allocations.
    uint32_t offsetOfGlobalData() const {
        MOZ_ASSERT(isFinished());
        return pod.codeBytes_;
    }
    uint8_t* globalData() const {
        MOZ_ASSERT(isFinished());
        return codeBase() + offsetOfGlobalData();
    }
    static void assertGlobalDataOffsets() {
        static_assert(wasm::ActivationGlobalDataOffset == 0,
                      "an AsmJSActivation* data goes first");
        static_assert(wasm::HeapGlobalDataOffset == wasm::ActivationGlobalDataOffset + sizeof(void*),
                      "then a pointer to the heap*");
        static_assert(wasm::NaN64GlobalDataOffset == wasm::HeapGlobalDataOffset + sizeof(uint8_t*),
                      "then a 64-bit NaN");
        static_assert(wasm::NaN32GlobalDataOffset == wasm::NaN64GlobalDataOffset + sizeof(double),
                      "then a 32-bit NaN");
        static_assert(sInitialGlobalDataBytes == wasm::NaN32GlobalDataOffset + sizeof(float),
                      "then all the normal global data (globals, exits, func-ptr-tables)");
    }
    static const uint32_t sInitialGlobalDataBytes = wasm::NaN32GlobalDataOffset + sizeof(float);

    AsmJSActivation*& activation() const {
        MOZ_ASSERT(isFinished());
        return *(AsmJSActivation**)(globalData() + wasm::ActivationGlobalDataOffset);
    }
    bool active() const {
        return activation() != nullptr;
    }
  private:
    // The pointer may reference shared memory, use with care.
    // Generally you want to use maybeHeap(), not heapDatum().
    uint8_t*& heapDatum() const {
        MOZ_ASSERT(isFinished());
        return *(uint8_t**)(globalData() + wasm::HeapGlobalDataOffset);
    }
  public:

    /*************************************************************************/
    // These functions are called after finish() but before staticallyLink():

    bool addRelativeLink(RelativeLink link) {
        MOZ_ASSERT(isFinished());
        return staticLinkData_.relativeLinks.append(link);
    }

    // A module is serialized after it is finished but before it is statically
    // linked. (Technically, it could be serialized after static linking, but it
    // would still need to be statically linked on deserialization.)
    size_t serializedSize() const;
    uint8_t* serialize(uint8_t* cursor) const;
    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);

    // Additionally, this function is called to flush the i-cache after
    // deserialization and cloning (but still before static linking, to prevent
    // a bunch of expensive micro-flushes).
    void setAutoFlushICacheRange();

    /*************************************************************************/

    // After a module is finished compiling or deserializing, it is "statically
    // linked" which specializes the code to its current address (this allows
    // code to be relocated between serialization and deserialization).
    void staticallyLink(ExclusiveContext* cx);

    // After a module is statically linked, it is "dynamically linked" which
    // specializes it to a particular set of arguments. In particular, this
    // binds the code to a particular heap (via initHeap) and set of global
    // variables. A given asm.js module cannot be dynamically linked more than
    // once so, if JS tries, the module is cloned. When linked, an asm.js module
    // is kept in a list so that it can be updated if the linked buffer is
    // detached.
    void setIsDynamicallyLinked(JSRuntime* rt) {
        MOZ_ASSERT(isFinished());
        MOZ_ASSERT(!isDynamicallyLinked());
        dynamicallyLinked_ = true;
        nextLinked_ = rt->linkedAsmJSModules;
        prevLinked_ = &rt->linkedAsmJSModules;
        if (nextLinked_)
            nextLinked_->prevLinked_ = &nextLinked_;
        rt->linkedAsmJSModules = this;
        MOZ_ASSERT(isDynamicallyLinked());
    }

    void initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext* cx);
    bool changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx);
    bool detachHeap(JSContext* cx);

    bool clone(JSContext* cx, ScopedJSDeletePtr<AsmJSModule>* moduleOut) const;

    /*************************************************************************/
    // Functions that can be called after dynamic linking succeeds:

    AsmJSModule* nextLinked() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return nextLinked_;
    }
    bool hasDetachedHeap() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return hasArrayView() && !heapDatum();
    }
    CodePtr entryTrampoline(const ExportedFunction& func) const {
        MOZ_ASSERT(isDynamicallyLinked());
        MOZ_ASSERT(!func.isChangeHeap());
        return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_);
    }
    uint8_t* interruptExit() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return interruptExit_;
    }
    uint8_t* outOfBoundsExit() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return outOfBoundsExit_;
    }
    SharedMem<uint8_t*> maybeHeap() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return hasArrayView() && isSharedView() ? SharedMem<uint8_t*>::shared(heapDatum())
            : SharedMem<uint8_t*>::unshared(heapDatum());
    }
    ArrayBufferObjectMaybeShared* maybeHeapBufferObject() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return maybeHeap_;
    }
    size_t heapLength() const;
    bool profilingEnabled() const {
        MOZ_ASSERT(isDynamicallyLinked());
        return profilingEnabled_;
    }
    void setProfilingEnabled(bool enabled, JSContext* cx);
    void setInterrupted(bool interrupted) {
        MOZ_ASSERT(isDynamicallyLinked());
        interrupted_ = interrupted;
    }
};

// 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. On cache hit, *module will be non-null. Note: the
// return value indicates whether or not an error was encountered, not whether
// there was a cache hit.
extern bool
LookupAsmJSModuleInCache(ExclusiveContext* cx,
                         AsmJSParser& parser,
                         ScopedJSDeletePtr<AsmJSModule>* module,
                         ScopedJSFreePtr<char>* 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;

    // On success, return an AsmJSModuleClass JSObject that has taken ownership
    // (and release()ed) the given module.
    static AsmJSModuleObject* create(ExclusiveContext* cx, ScopedJSDeletePtr<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 */
back to top