blob: 3d03ecf88aeb21ff3b322fcafb1ad837ba1b4995 [file] [log] [blame]
/* -*- 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 */