michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_AsmJSModule_h michael@0: #define jit_AsmJSModule_h michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: #include "mozilla/Move.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jsscript.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: #include "jit/AsmJS.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: #ifdef JS_ION_PERF michael@0: # include "jit/PerfSpewer.h" michael@0: #endif michael@0: #include "jit/RegisterSets.h" michael@0: #include "vm/TypedArrayObject.h" michael@0: michael@0: namespace js { michael@0: michael@0: // These EcmaScript-defined coercions form the basis of the asm.js type system. michael@0: enum AsmJSCoercion michael@0: { michael@0: AsmJS_ToInt32, michael@0: AsmJS_ToNumber, michael@0: AsmJS_FRound michael@0: }; michael@0: michael@0: // The asm.js spec recognizes this set of builtin Math functions. michael@0: enum AsmJSMathBuiltinFunction michael@0: { michael@0: AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan, michael@0: AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, michael@0: AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, michael@0: AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, michael@0: AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul, michael@0: AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max michael@0: }; michael@0: michael@0: // An asm.js module represents the collection of functions nested inside a michael@0: // single outer "use asm" function. For example, this asm.js module: michael@0: // function() { "use asm"; function f() {} function g() {} return f } michael@0: // contains the functions 'f' and 'g'. michael@0: // michael@0: // An asm.js module contains both the jit-code produced by compiling all the michael@0: // functions in the module as well all the data required to perform the michael@0: // link-time validation step in the asm.js spec. michael@0: // michael@0: // NB: this means that AsmJSModule must be GC-safe. michael@0: class AsmJSModule michael@0: { michael@0: public: michael@0: class Global michael@0: { michael@0: public: michael@0: enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant }; michael@0: enum VarInitKind { InitConstant, InitImport }; michael@0: enum ConstantKind { GlobalConstant, MathConstant }; michael@0: michael@0: private: michael@0: struct Pod { michael@0: Which which_; michael@0: union { michael@0: struct { michael@0: uint32_t index_; michael@0: VarInitKind initKind_; michael@0: AsmJSCoercion coercion_; michael@0: union { michael@0: Value constant_; // will only contain int32/double michael@0: } init; michael@0: } var; michael@0: uint32_t ffiIndex_; michael@0: ArrayBufferView::ViewType viewType_; michael@0: AsmJSMathBuiltinFunction mathBuiltinFunc_; michael@0: struct { michael@0: ConstantKind kind_; michael@0: double value_; michael@0: } constant; michael@0: } u; michael@0: } pod; michael@0: PropertyName *name_; michael@0: michael@0: friend class AsmJSModule; michael@0: michael@0: Global(Which which, PropertyName *name) { michael@0: pod.which_ = which; michael@0: name_ = name; michael@0: JS_ASSERT_IF(name_, name_->isTenured()); michael@0: } michael@0: michael@0: void trace(JSTracer *trc) { michael@0: if (name_) michael@0: MarkStringUnbarriered(trc, &name_, "asm.js global name"); michael@0: JS_ASSERT_IF(pod.which_ == Variable && pod.u.var.initKind_ == InitConstant, michael@0: !pod.u.var.init.constant_.isMarkable()); michael@0: } michael@0: michael@0: public: michael@0: Global() {} michael@0: Which which() const { michael@0: return pod.which_; michael@0: } michael@0: uint32_t varIndex() const { michael@0: JS_ASSERT(pod.which_ == Variable); michael@0: return pod.u.var.index_; michael@0: } michael@0: VarInitKind varInitKind() const { michael@0: JS_ASSERT(pod.which_ == Variable); michael@0: return pod.u.var.initKind_; michael@0: } michael@0: const Value &varInitConstant() const { michael@0: JS_ASSERT(pod.which_ == Variable); michael@0: JS_ASSERT(pod.u.var.initKind_ == InitConstant); michael@0: return pod.u.var.init.constant_; michael@0: } michael@0: AsmJSCoercion varInitCoercion() const { michael@0: JS_ASSERT(pod.which_ == Variable); michael@0: return pod.u.var.coercion_; michael@0: } michael@0: PropertyName *varImportField() const { michael@0: JS_ASSERT(pod.which_ == Variable); michael@0: JS_ASSERT(pod.u.var.initKind_ == InitImport); michael@0: return name_; michael@0: } michael@0: PropertyName *ffiField() const { michael@0: JS_ASSERT(pod.which_ == FFI); michael@0: return name_; michael@0: } michael@0: uint32_t ffiIndex() const { michael@0: JS_ASSERT(pod.which_ == FFI); michael@0: return pod.u.ffiIndex_; michael@0: } michael@0: PropertyName *viewName() const { michael@0: JS_ASSERT(pod.which_ == ArrayView); michael@0: return name_; michael@0: } michael@0: ArrayBufferView::ViewType viewType() const { michael@0: JS_ASSERT(pod.which_ == ArrayView); michael@0: return pod.u.viewType_; michael@0: } michael@0: PropertyName *mathName() const { michael@0: JS_ASSERT(pod.which_ == MathBuiltinFunction); michael@0: return name_; michael@0: } michael@0: AsmJSMathBuiltinFunction mathBuiltinFunction() const { michael@0: JS_ASSERT(pod.which_ == MathBuiltinFunction); michael@0: return pod.u.mathBuiltinFunc_; michael@0: } michael@0: PropertyName *constantName() const { michael@0: JS_ASSERT(pod.which_ == Constant); michael@0: return name_; michael@0: } michael@0: ConstantKind constantKind() const { michael@0: JS_ASSERT(pod.which_ == Constant); michael@0: return pod.u.constant.kind_; michael@0: } michael@0: double constantValue() const { michael@0: JS_ASSERT(pod.which_ == Constant); michael@0: return pod.u.constant.value_; michael@0: } michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool clone(ExclusiveContext *cx, Global *out) const; michael@0: }; michael@0: michael@0: class Exit michael@0: { michael@0: unsigned ffiIndex_; michael@0: unsigned globalDataOffset_; michael@0: unsigned interpCodeOffset_; michael@0: unsigned ionCodeOffset_; michael@0: michael@0: friend class AsmJSModule; michael@0: michael@0: public: michael@0: Exit() {} michael@0: Exit(unsigned ffiIndex, unsigned globalDataOffset) michael@0: : ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset), michael@0: interpCodeOffset_(0), ionCodeOffset_(0) michael@0: {} michael@0: unsigned ffiIndex() const { michael@0: return ffiIndex_; michael@0: } michael@0: unsigned globalDataOffset() const { michael@0: return globalDataOffset_; michael@0: } michael@0: void initInterpOffset(unsigned off) { michael@0: JS_ASSERT(!interpCodeOffset_); michael@0: interpCodeOffset_ = off; michael@0: } michael@0: void initIonOffset(unsigned off) { michael@0: JS_ASSERT(!ionCodeOffset_); michael@0: ionCodeOffset_ = off; michael@0: } michael@0: void updateOffsets(jit::MacroAssembler &masm) { michael@0: interpCodeOffset_ = masm.actualOffset(interpCodeOffset_); michael@0: ionCodeOffset_ = masm.actualOffset(ionCodeOffset_); michael@0: } michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool clone(ExclusiveContext *cx, Exit *out) const; michael@0: }; michael@0: typedef int32_t (*CodePtr)(uint64_t *args, uint8_t *global); michael@0: michael@0: typedef Vector ArgCoercionVector; michael@0: michael@0: enum ReturnType { Return_Int32, Return_Double, Return_Void }; michael@0: michael@0: class ExportedFunction michael@0: { michael@0: PropertyName *name_; michael@0: PropertyName *maybeFieldName_; michael@0: ArgCoercionVector argCoercions_; michael@0: struct Pod { michael@0: ReturnType returnType_; michael@0: uint32_t codeOffset_; michael@0: // These two fields are offsets to the beginning of the ScriptSource michael@0: // of the module, and thus invariant under serialization (unlike michael@0: // absolute offsets into ScriptSource). michael@0: uint32_t startOffsetInModule_; michael@0: uint32_t endOffsetInModule_; michael@0: } pod; michael@0: michael@0: friend class AsmJSModule; michael@0: michael@0: ExportedFunction(PropertyName *name, michael@0: uint32_t startOffsetInModule, uint32_t endOffsetInModule, michael@0: PropertyName *maybeFieldName, michael@0: ArgCoercionVector &&argCoercions, michael@0: ReturnType returnType) michael@0: { michael@0: name_ = name; michael@0: maybeFieldName_ = maybeFieldName; michael@0: argCoercions_ = mozilla::Move(argCoercions); michael@0: pod.returnType_ = returnType; michael@0: pod.codeOffset_ = UINT32_MAX; michael@0: pod.startOffsetInModule_ = startOffsetInModule; michael@0: pod.endOffsetInModule_ = endOffsetInModule; michael@0: JS_ASSERT_IF(maybeFieldName_, name_->isTenured()); michael@0: } michael@0: michael@0: void trace(JSTracer *trc) { michael@0: MarkStringUnbarriered(trc, &name_, "asm.js export name"); michael@0: if (maybeFieldName_) michael@0: MarkStringUnbarriered(trc, &maybeFieldName_, "asm.js export field"); michael@0: } michael@0: michael@0: public: michael@0: ExportedFunction() {} michael@0: ExportedFunction(ExportedFunction &&rhs) { michael@0: name_ = rhs.name_; michael@0: maybeFieldName_ = rhs.maybeFieldName_; michael@0: argCoercions_ = mozilla::Move(rhs.argCoercions_); michael@0: pod = rhs.pod; michael@0: } michael@0: void updateCodeOffset(jit::MacroAssembler &masm) { michael@0: pod.codeOffset_ = masm.actualOffset(pod.codeOffset_); michael@0: } michael@0: michael@0: void initCodeOffset(unsigned off) { michael@0: JS_ASSERT(pod.codeOffset_ == UINT32_MAX); michael@0: pod.codeOffset_ = off; michael@0: } michael@0: michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: uint32_t startOffsetInModule() const { michael@0: return pod.startOffsetInModule_; michael@0: } michael@0: uint32_t endOffsetInModule() const { michael@0: return pod.endOffsetInModule_; michael@0: } michael@0: PropertyName *maybeFieldName() const { michael@0: return maybeFieldName_; michael@0: } michael@0: unsigned numArgs() const { michael@0: return argCoercions_.length(); michael@0: } michael@0: AsmJSCoercion argCoercion(unsigned i) const { michael@0: return argCoercions_[i]; michael@0: } michael@0: ReturnType returnType() const { michael@0: return pod.returnType_; michael@0: } michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool clone(ExclusiveContext *cx, ExportedFunction *out) const; michael@0: }; michael@0: michael@0: class Name michael@0: { michael@0: PropertyName *name_; michael@0: public: michael@0: Name() : name_(nullptr) {} michael@0: Name(PropertyName *name) : name_(name) {} michael@0: PropertyName *name() const { return name_; } michael@0: PropertyName *&name() { return name_; } michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool clone(ExclusiveContext *cx, Name *out) const; michael@0: }; michael@0: michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: // Function information to add to the VTune JIT profiler following linking. michael@0: struct ProfiledFunction michael@0: { michael@0: PropertyName *name; michael@0: struct Pod { michael@0: unsigned startCodeOffset; michael@0: unsigned endCodeOffset; michael@0: unsigned lineno; michael@0: unsigned columnIndex; michael@0: } pod; michael@0: michael@0: explicit ProfiledFunction() michael@0: : name(nullptr) michael@0: { } michael@0: michael@0: ProfiledFunction(PropertyName *name, unsigned start, unsigned end, michael@0: unsigned line = 0, unsigned column = 0) michael@0: : name(name) michael@0: { michael@0: JS_ASSERT(name->isTenured()); michael@0: michael@0: pod.startCodeOffset = start; michael@0: pod.endCodeOffset = end; michael@0: pod.lineno = line; michael@0: pod.columnIndex = column; michael@0: } michael@0: michael@0: void trace(JSTracer *trc) { michael@0: if (name) michael@0: MarkStringUnbarriered(trc, &name, "asm.js profiled function name"); michael@0: } michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: }; michael@0: #endif michael@0: michael@0: #if defined(JS_ION_PERF) michael@0: struct ProfiledBlocksFunction : public ProfiledFunction michael@0: { michael@0: unsigned endInlineCodeOffset; michael@0: jit::BasicBlocksVector blocks; michael@0: michael@0: ProfiledBlocksFunction(PropertyName *name, unsigned start, unsigned endInline, unsigned end, michael@0: jit::BasicBlocksVector &blocksVector) michael@0: : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline), michael@0: blocks(mozilla::Move(blocksVector)) michael@0: { michael@0: JS_ASSERT(name->isTenured()); michael@0: } michael@0: michael@0: ProfiledBlocksFunction(ProfiledBlocksFunction &©) michael@0: : ProfiledFunction(copy.name, copy.pod.startCodeOffset, copy.pod.endCodeOffset), michael@0: endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::Move(copy.blocks)) michael@0: { } michael@0: }; michael@0: #endif michael@0: michael@0: struct RelativeLink michael@0: { michael@0: uint32_t patchAtOffset; michael@0: uint32_t targetOffset; michael@0: }; michael@0: michael@0: typedef Vector RelativeLinkVector; michael@0: michael@0: struct AbsoluteLink michael@0: { michael@0: jit::CodeOffsetLabel patchAt; michael@0: jit::AsmJSImmKind target; michael@0: }; michael@0: michael@0: typedef Vector AbsoluteLinkVector; michael@0: michael@0: // Static-link data is used to patch a module either after it has been michael@0: // compiled or deserialized with various absolute addresses (of code or michael@0: // data in the process) or relative addresses (of code or data in the same michael@0: // AsmJSModule). michael@0: struct StaticLinkData michael@0: { michael@0: uint32_t interruptExitOffset; michael@0: RelativeLinkVector relativeLinks; michael@0: AbsoluteLinkVector absoluteLinks; michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool clone(ExclusiveContext *cx, StaticLinkData *out) const; michael@0: michael@0: size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: }; michael@0: michael@0: private: michael@0: typedef Vector GlobalVector; michael@0: typedef Vector ExitVector; michael@0: typedef Vector ExportedFunctionVector; michael@0: typedef Vector CallSiteVector; michael@0: typedef Vector FunctionNameVector; michael@0: typedef Vector HeapAccessVector; michael@0: typedef Vector FunctionCountsVector; michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: typedef Vector ProfiledFunctionVector; michael@0: #endif michael@0: #if defined(JS_ION_PERF) michael@0: typedef Vector ProfiledBlocksFunctionVector; michael@0: #endif michael@0: michael@0: private: michael@0: PropertyName * globalArgumentName_; michael@0: PropertyName * importArgumentName_; michael@0: PropertyName * bufferArgumentName_; michael@0: michael@0: GlobalVector globals_; michael@0: ExitVector exits_; michael@0: ExportedFunctionVector exports_; michael@0: CallSiteVector callSites_; michael@0: FunctionNameVector functionNames_; michael@0: HeapAccessVector heapAccesses_; michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: ProfiledFunctionVector profiledFunctions_; michael@0: #endif michael@0: #if defined(JS_ION_PERF) michael@0: ProfiledBlocksFunctionVector perfProfiledBlocksFunctions_; michael@0: #endif michael@0: michael@0: struct Pod { michael@0: uint32_t funcLength_; michael@0: uint32_t funcLengthWithRightBrace_; michael@0: bool strict_; michael@0: uint32_t numGlobalVars_; michael@0: uint32_t numFFIs_; michael@0: size_t funcPtrTableAndExitBytes_; michael@0: bool hasArrayView_; michael@0: size_t functionBytes_; // just the function bodies, no stubs michael@0: size_t codeBytes_; // function bodies and stubs michael@0: size_t totalBytes_; // function bodies, stubs, and global data michael@0: uint32_t minHeapLength_; michael@0: } pod; michael@0: michael@0: uint8_t * code_; michael@0: uint8_t * interruptExit_; michael@0: michael@0: StaticLinkData staticLinkData_; michael@0: bool dynamicallyLinked_; michael@0: bool loadedFromCache_; michael@0: HeapPtr maybeHeap_; michael@0: michael@0: // The next two fields need to be kept out of the Pod as they depend on the michael@0: // position of the module within the ScriptSource and thus aren't invariant michael@0: // with caching. michael@0: uint32_t funcStart_; michael@0: uint32_t offsetToEndOfUseAsm_; michael@0: michael@0: ScriptSource * scriptSource_; michael@0: michael@0: // This field is accessed concurrently when requesting an interrupt. michael@0: // Access must be synchronized via the runtime's interrupt lock. michael@0: mutable bool codeIsProtected_; michael@0: michael@0: public: michael@0: explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart, michael@0: uint32_t offsetToEndOfUseAsm, bool strict); michael@0: ~AsmJSModule(); michael@0: michael@0: void trace(JSTracer *trc) { michael@0: for (unsigned i = 0; i < globals_.length(); i++) michael@0: globals_[i].trace(trc); michael@0: for (unsigned i = 0; i < exports_.length(); i++) michael@0: exports_[i].trace(trc); michael@0: for (unsigned i = 0; i < exits_.length(); i++) { michael@0: if (exitIndexToGlobalDatum(i).fun) michael@0: MarkObject(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function"); michael@0: } michael@0: for (unsigned i = 0; i < functionNames_.length(); i++) michael@0: MarkStringUnbarriered(trc, &functionNames_[i].name(), "asm.js module function name"); michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: for (unsigned i = 0; i < profiledFunctions_.length(); i++) michael@0: profiledFunctions_[i].trace(trc); michael@0: #endif michael@0: #if defined(JS_ION_PERF) michael@0: for (unsigned i = 0; i < perfProfiledBlocksFunctions_.length(); i++) michael@0: perfProfiledBlocksFunctions_[i].trace(trc); michael@0: #endif michael@0: if (maybeHeap_) michael@0: gc::MarkObject(trc, &maybeHeap_, "asm.js heap"); michael@0: michael@0: if (globalArgumentName_) michael@0: MarkStringUnbarriered(trc, &globalArgumentName_, "asm.js global argument name"); michael@0: if (importArgumentName_) michael@0: MarkStringUnbarriered(trc, &importArgumentName_, "asm.js import argument name"); michael@0: if (bufferArgumentName_) michael@0: MarkStringUnbarriered(trc, &bufferArgumentName_, "asm.js buffer argument name"); michael@0: } michael@0: michael@0: ScriptSource *scriptSource() const { michael@0: JS_ASSERT(scriptSource_ != nullptr); michael@0: return scriptSource_; michael@0: } michael@0: michael@0: /* michael@0: * funcStart() refers to the offset in the ScriptSource to the beginning michael@0: * of the function. If the function has been created with the Function michael@0: * constructor, this will be the first character in the function source. michael@0: * Otherwise, it will be the opening parenthesis of the arguments list. michael@0: */ michael@0: uint32_t funcStart() const { michael@0: return funcStart_; michael@0: } michael@0: uint32_t offsetToEndOfUseAsm() const { michael@0: return offsetToEndOfUseAsm_; michael@0: } michael@0: void initFuncEnd(uint32_t endBeforeCurly, uint32_t endAfterCurly) { michael@0: JS_ASSERT(endBeforeCurly >= offsetToEndOfUseAsm_); michael@0: JS_ASSERT(endAfterCurly >= offsetToEndOfUseAsm_); michael@0: pod.funcLength_ = endBeforeCurly - funcStart_; michael@0: pod.funcLengthWithRightBrace_ = endAfterCurly - funcStart_; michael@0: } michael@0: uint32_t funcEndBeforeCurly() const { michael@0: return funcStart_ + pod.funcLength_; michael@0: } michael@0: uint32_t funcEndAfterCurly() const { michael@0: return funcStart_ + pod.funcLengthWithRightBrace_; michael@0: } michael@0: bool strict() const { michael@0: return pod.strict_; michael@0: } michael@0: michael@0: bool addGlobalVarInit(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) { michael@0: JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); michael@0: if (pod.numGlobalVars_ == UINT32_MAX) michael@0: return false; michael@0: Global g(Global::Variable, nullptr); michael@0: g.pod.u.var.initKind_ = Global::InitConstant; michael@0: g.pod.u.var.init.constant_ = v; michael@0: g.pod.u.var.coercion_ = coercion; michael@0: g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; michael@0: return globals_.append(g); michael@0: } michael@0: bool addGlobalVarImport(PropertyName *name, AsmJSCoercion coercion, uint32_t *globalIndex) { michael@0: JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); michael@0: Global g(Global::Variable, name); michael@0: g.pod.u.var.initKind_ = Global::InitImport; michael@0: g.pod.u.var.coercion_ = coercion; michael@0: g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; michael@0: return globals_.append(g); michael@0: } michael@0: bool addFFI(PropertyName *field, uint32_t *ffiIndex) { michael@0: if (pod.numFFIs_ == UINT32_MAX) michael@0: return false; michael@0: Global g(Global::FFI, field); michael@0: g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; michael@0: return globals_.append(g); michael@0: } michael@0: bool addArrayView(ArrayBufferView::ViewType vt, PropertyName *field) { michael@0: pod.hasArrayView_ = true; michael@0: Global g(Global::ArrayView, field); michael@0: g.pod.u.viewType_ = vt; michael@0: return globals_.append(g); michael@0: } michael@0: bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) { michael@0: Global g(Global::MathBuiltinFunction, field); michael@0: g.pod.u.mathBuiltinFunc_ = func; michael@0: return globals_.append(g); michael@0: } michael@0: bool addMathBuiltinConstant(double value, PropertyName *field) { michael@0: Global g(Global::Constant, field); michael@0: g.pod.u.constant.value_ = value; michael@0: g.pod.u.constant.kind_ = Global::MathConstant; michael@0: return globals_.append(g); michael@0: } michael@0: bool addGlobalConstant(double value, PropertyName *name) { michael@0: Global g(Global::Constant, name); michael@0: g.pod.u.constant.value_ = value; michael@0: g.pod.u.constant.kind_ = Global::GlobalConstant; michael@0: return globals_.append(g); michael@0: } michael@0: bool addFuncPtrTable(unsigned numElems, uint32_t *globalDataOffset) { michael@0: JS_ASSERT(IsPowerOfTwo(numElems)); michael@0: if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < numElems * sizeof(void*)) michael@0: return false; michael@0: *globalDataOffset = globalDataBytes(); michael@0: pod.funcPtrTableAndExitBytes_ += numElems * sizeof(void*); michael@0: return true; michael@0: } michael@0: bool addExit(unsigned ffiIndex, unsigned *exitIndex) { michael@0: if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum)) michael@0: return false; michael@0: uint32_t globalDataOffset = globalDataBytes(); michael@0: JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0); michael@0: pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum); michael@0: *exitIndex = unsigned(exits_.length()); michael@0: return exits_.append(Exit(ffiIndex, globalDataOffset)); michael@0: } michael@0: michael@0: bool addExportedFunction(PropertyName *name, uint32_t srcStart, uint32_t srcEnd, michael@0: PropertyName *maybeFieldName, michael@0: ArgCoercionVector &&argCoercions, michael@0: ReturnType returnType) michael@0: { michael@0: ExportedFunction func(name, srcStart, srcEnd, maybeFieldName, michael@0: mozilla::Move(argCoercions), returnType); michael@0: if (exports_.length() >= UINT32_MAX) michael@0: return false; michael@0: return exports_.append(mozilla::Move(func)); michael@0: } michael@0: unsigned numExportedFunctions() const { michael@0: return exports_.length(); michael@0: } michael@0: const ExportedFunction &exportedFunction(unsigned i) const { michael@0: return exports_[i]; michael@0: } michael@0: ExportedFunction &exportedFunction(unsigned i) { michael@0: return exports_[i]; michael@0: } michael@0: CodePtr entryTrampoline(const ExportedFunction &func) const { michael@0: JS_ASSERT(func.pod.codeOffset_ != UINT32_MAX); michael@0: return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_); michael@0: } michael@0: michael@0: bool addFunctionName(PropertyName *name, uint32_t *nameIndex) { michael@0: JS_ASSERT(name->isTenured()); michael@0: if (functionNames_.length() > jit::CallSiteDesc::FUNCTION_NAME_INDEX_MAX) michael@0: return false; michael@0: *nameIndex = functionNames_.length(); michael@0: return functionNames_.append(name); michael@0: } michael@0: PropertyName *functionName(uint32_t i) const { michael@0: return functionNames_[i].name(); michael@0: } michael@0: michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: bool trackProfiledFunction(PropertyName *name, unsigned startCodeOffset, unsigned endCodeOffset, michael@0: unsigned line, unsigned column) michael@0: { michael@0: ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column); michael@0: return profiledFunctions_.append(func); michael@0: } michael@0: unsigned numProfiledFunctions() const { michael@0: return profiledFunctions_.length(); michael@0: } michael@0: ProfiledFunction &profiledFunction(unsigned i) { michael@0: return profiledFunctions_[i]; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef JS_ION_PERF michael@0: bool trackPerfProfiledBlocks(PropertyName *name, unsigned startCodeOffset, unsigned endInlineCodeOffset, michael@0: unsigned endCodeOffset, jit::BasicBlocksVector &basicBlocks) { michael@0: ProfiledBlocksFunction func(name, startCodeOffset, endInlineCodeOffset, endCodeOffset, basicBlocks); michael@0: return perfProfiledBlocksFunctions_.append(mozilla::Move(func)); michael@0: } michael@0: unsigned numPerfBlocksFunctions() const { michael@0: return perfProfiledBlocksFunctions_.length(); michael@0: } michael@0: ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) { michael@0: return perfProfiledBlocksFunctions_[i]; michael@0: } michael@0: #endif michael@0: michael@0: bool hasArrayView() const { michael@0: return pod.hasArrayView_; michael@0: } michael@0: unsigned numFFIs() const { michael@0: return pod.numFFIs_; michael@0: } michael@0: unsigned numGlobalVars() const { michael@0: return pod.numGlobalVars_; michael@0: } michael@0: unsigned numGlobals() const { michael@0: return globals_.length(); michael@0: } michael@0: Global &global(unsigned i) { michael@0: return globals_[i]; michael@0: } michael@0: unsigned numExits() const { michael@0: return exits_.length(); michael@0: } michael@0: Exit &exit(unsigned i) { michael@0: return exits_[i]; michael@0: } michael@0: const Exit &exit(unsigned i) const { michael@0: return exits_[i]; michael@0: } michael@0: uint8_t *interpExitTrampoline(const Exit &exit) const { michael@0: JS_ASSERT(exit.interpCodeOffset_); michael@0: return code_ + exit.interpCodeOffset_; michael@0: } michael@0: uint8_t *ionExitTrampoline(const Exit &exit) const { michael@0: JS_ASSERT(exit.ionCodeOffset_); michael@0: return code_ + exit.ionCodeOffset_; michael@0: } michael@0: michael@0: // An Exit holds bookkeeping information about an exit; the ExitDatum michael@0: // struct overlays the actual runtime data stored in the global data michael@0: // section. michael@0: struct ExitDatum michael@0: { michael@0: uint8_t *exit; michael@0: HeapPtrFunction fun; michael@0: }; michael@0: michael@0: // Global data section michael@0: // michael@0: // The global data section is placed after the executable code (i.e., at michael@0: // offset codeBytes_) in the module's linear allocation. The global data michael@0: // are laid out in this order: michael@0: // 0. a pointer/descriptor for the heap that was linked to the module michael@0: // 1. global variable state (elements are sizeof(uint64_t)) michael@0: // 2. interleaved function-pointer tables and exits. These are allocated michael@0: // while type checking function bodies (as exits and uses of michael@0: // function-pointer tables are encountered). michael@0: size_t offsetOfGlobalData() const { michael@0: JS_ASSERT(code_); michael@0: return pod.codeBytes_; michael@0: } michael@0: uint8_t *globalData() const { michael@0: return code_ + offsetOfGlobalData(); michael@0: } michael@0: size_t globalDataBytes() const { michael@0: return sizeof(void*) + michael@0: pod.numGlobalVars_ * sizeof(uint64_t) + michael@0: pod.funcPtrTableAndExitBytes_; michael@0: } michael@0: unsigned heapOffset() const { michael@0: return 0; michael@0: } michael@0: uint8_t *&heapDatum() const { michael@0: return *(uint8_t**)(globalData() + heapOffset()); michael@0: } michael@0: unsigned globalVarIndexToGlobalDataOffset(unsigned i) const { michael@0: JS_ASSERT(i < pod.numGlobalVars_); michael@0: return sizeof(void*) + michael@0: i * sizeof(uint64_t); michael@0: } michael@0: void *globalVarIndexToGlobalDatum(unsigned i) const { michael@0: return (void *)(globalData() + globalVarIndexToGlobalDataOffset(i)); michael@0: } michael@0: uint8_t **globalDataOffsetToFuncPtrTable(unsigned globalDataOffset) const { michael@0: JS_ASSERT(globalDataOffset < globalDataBytes()); michael@0: return (uint8_t **)(globalData() + globalDataOffset); michael@0: } michael@0: unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const { michael@0: return exits_[exitIndex].globalDataOffset(); michael@0: } michael@0: ExitDatum &exitIndexToGlobalDatum(unsigned exitIndex) const { michael@0: return *(ExitDatum *)(globalData() + exitIndexToGlobalDataOffset(exitIndex)); michael@0: } michael@0: michael@0: void initFunctionBytes(size_t functionBytes) { michael@0: JS_ASSERT(pod.functionBytes_ == 0); michael@0: pod.functionBytes_ = functionBytes; michael@0: } michael@0: void updateFunctionBytes(jit::MacroAssembler &masm) { michael@0: pod.functionBytes_ = masm.actualOffset(pod.functionBytes_); michael@0: JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0); michael@0: } michael@0: size_t functionBytes() const { michael@0: JS_ASSERT(pod.functionBytes_); michael@0: JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0); michael@0: return pod.functionBytes_; michael@0: } michael@0: bool containsPC(void *pc) const { michael@0: return pc >= code_ && pc < (code_ + functionBytes()); michael@0: } michael@0: michael@0: void assignHeapAccesses(jit::AsmJSHeapAccessVector &&accesses) { michael@0: heapAccesses_ = Move(accesses); michael@0: } michael@0: unsigned numHeapAccesses() const { michael@0: return heapAccesses_.length(); michael@0: } michael@0: const jit::AsmJSHeapAccess &heapAccess(unsigned i) const { michael@0: return heapAccesses_[i]; michael@0: } michael@0: jit::AsmJSHeapAccess &heapAccess(unsigned i) { michael@0: return heapAccesses_[i]; michael@0: } michael@0: michael@0: void assignCallSites(jit::CallSiteVector &&callsites) { michael@0: callSites_ = Move(callsites); michael@0: } michael@0: unsigned numCallSites() const { michael@0: return callSites_.length(); michael@0: } michael@0: const jit::CallSite &callSite(unsigned i) const { michael@0: return callSites_[i]; michael@0: } michael@0: jit::CallSite &callSite(unsigned i) { michael@0: return callSites_[i]; michael@0: } michael@0: michael@0: void initHeap(Handle heap, JSContext *cx); michael@0: michael@0: void requireHeapLengthToBeAtLeast(uint32_t len) { michael@0: if (len > pod.minHeapLength_) michael@0: pod.minHeapLength_ = len; michael@0: } michael@0: uint32_t minHeapLength() const { michael@0: return pod.minHeapLength_; michael@0: } michael@0: michael@0: bool allocateAndCopyCode(ExclusiveContext *cx, jit::MacroAssembler &masm); michael@0: michael@0: // StaticLinkData setters (called after finishing compilation, before michael@0: // staticLink). michael@0: bool addRelativeLink(RelativeLink link) { michael@0: return staticLinkData_.relativeLinks.append(link); michael@0: } michael@0: bool addAbsoluteLink(AbsoluteLink link) { michael@0: return staticLinkData_.absoluteLinks.append(link); michael@0: } michael@0: void setInterruptOffset(uint32_t offset) { michael@0: staticLinkData_.interruptExitOffset = offset; michael@0: } michael@0: michael@0: void restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx); michael@0: void setAutoFlushICacheRange(); michael@0: void staticallyLink(ExclusiveContext *cx); michael@0: michael@0: uint8_t *codeBase() const { michael@0: JS_ASSERT(code_); michael@0: JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); michael@0: return code_; michael@0: } michael@0: michael@0: uint8_t *interruptExit() const { michael@0: return interruptExit_; michael@0: } michael@0: michael@0: void setIsDynamicallyLinked() { michael@0: JS_ASSERT(!dynamicallyLinked_); michael@0: dynamicallyLinked_ = true; michael@0: } michael@0: bool isDynamicallyLinked() const { michael@0: return dynamicallyLinked_; michael@0: } michael@0: uint8_t *maybeHeap() const { michael@0: JS_ASSERT(dynamicallyLinked_); michael@0: return heapDatum(); michael@0: } michael@0: ArrayBufferObject *maybeHeapBufferObject() const { michael@0: JS_ASSERT(dynamicallyLinked_); michael@0: return maybeHeap_; michael@0: } michael@0: size_t heapLength() const { michael@0: JS_ASSERT(dynamicallyLinked_); michael@0: return maybeHeap_ ? maybeHeap_->byteLength() : 0; michael@0: } michael@0: michael@0: void initGlobalArgumentName(PropertyName *n) { michael@0: JS_ASSERT_IF(n, n->isTenured()); michael@0: globalArgumentName_ = n; michael@0: } michael@0: void initImportArgumentName(PropertyName *n) { michael@0: JS_ASSERT_IF(n, n->isTenured()); michael@0: importArgumentName_ = n; michael@0: } michael@0: void initBufferArgumentName(PropertyName *n) { michael@0: JS_ASSERT_IF(n, n->isTenured()); michael@0: bufferArgumentName_ = n; michael@0: } michael@0: michael@0: PropertyName *globalArgumentName() const { michael@0: return globalArgumentName_; michael@0: } michael@0: PropertyName *importArgumentName() const { michael@0: return importArgumentName_; michael@0: } michael@0: PropertyName *bufferArgumentName() const { michael@0: return bufferArgumentName_; michael@0: } michael@0: michael@0: void detachIonCompilation(size_t exitIndex) const { michael@0: exitIndexToGlobalDatum(exitIndex).exit = interpExitTrampoline(exit(exitIndex)); michael@0: } michael@0: michael@0: void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode, michael@0: size_t *asmJSModuleData); michael@0: michael@0: size_t serializedSize() const; michael@0: uint8_t *serialize(uint8_t *cursor) const; michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); michael@0: bool loadedFromCache() const { return loadedFromCache_; } michael@0: michael@0: bool clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) const; michael@0: michael@0: // These methods may only be called while holding the Runtime's interrupt michael@0: // lock. michael@0: void protectCode(JSRuntime *rt) const; michael@0: void unprotectCode(JSRuntime *rt) const; michael@0: bool codeIsProtected(JSRuntime *rt) const; michael@0: }; michael@0: michael@0: // Store the just-parsed module in the cache using AsmJSCacheOps. michael@0: extern bool michael@0: StoreAsmJSModuleInCache(AsmJSParser &parser, michael@0: const AsmJSModule &module, michael@0: ExclusiveContext *cx); michael@0: michael@0: // Attempt to load the asm.js module that is about to be parsed from the cache michael@0: // using AsmJSCacheOps. On cache hit, *module will be non-null. Note: the michael@0: // return value indicates whether or not an error was encountered, not whether michael@0: // there was a cache hit. michael@0: extern bool michael@0: LookupAsmJSModuleInCache(ExclusiveContext *cx, michael@0: AsmJSParser &parser, michael@0: ScopedJSDeletePtr *module, michael@0: ScopedJSFreePtr *compilationTimeReport); michael@0: michael@0: // An AsmJSModuleObject is an internal implementation object (i.e., not exposed michael@0: // directly to user script) which manages the lifetime of an AsmJSModule. A michael@0: // JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be michael@0: // able to point to their module via their extended slots. michael@0: class AsmJSModuleObject : public JSObject michael@0: { michael@0: static const unsigned MODULE_SLOT = 0; michael@0: michael@0: public: michael@0: static const unsigned RESERVED_SLOTS = 1; michael@0: michael@0: // On success, return an AsmJSModuleClass JSObject that has taken ownership michael@0: // (and release()ed) the given module. michael@0: static AsmJSModuleObject *create(ExclusiveContext *cx, ScopedJSDeletePtr *module); michael@0: michael@0: AsmJSModule &module() const; michael@0: michael@0: void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode, michael@0: size_t *asmJSModuleData) { michael@0: module().addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData); michael@0: } michael@0: michael@0: static const Class class_; michael@0: }; michael@0: michael@0: } // namespace js michael@0: michael@0: #endif // JS_ION michael@0: michael@0: #endif /* jit_AsmJSModule_h */