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: #include "jit/AsmJS.h" michael@0: michael@0: #include "mozilla/Move.h" michael@0: michael@0: #ifdef MOZ_VTUNE michael@0: # include "vtune/VTuneWrapper.h" michael@0: #endif michael@0: michael@0: #include "jsmath.h" michael@0: #include "jsprf.h" michael@0: #include "jsworkers.h" michael@0: #include "prmjtime.h" michael@0: michael@0: #include "assembler/assembler/MacroAssembler.h" michael@0: #include "frontend/Parser.h" michael@0: #include "jit/AsmJSLink.h" michael@0: #include "jit/AsmJSModule.h" michael@0: #include "jit/AsmJSSignalHandlers.h" michael@0: #include "jit/CodeGenerator.h" michael@0: #include "jit/CompileWrappers.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: #ifdef JS_ION_PERF michael@0: # include "jit/PerfSpewer.h" michael@0: #endif michael@0: #include "vm/Interpreter.h" michael@0: michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "frontend/ParseNode-inl.h" michael@0: #include "frontend/Parser-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::frontend; michael@0: using namespace js::jit; michael@0: michael@0: using mozilla::AddToHash; michael@0: using mozilla::ArrayLength; michael@0: using mozilla::CountLeadingZeroes32; michael@0: using mozilla::DebugOnly; michael@0: using mozilla::HashGeneric; michael@0: using mozilla::IsNaN; michael@0: using mozilla::IsNegativeZero; michael@0: using mozilla::Maybe; michael@0: using mozilla::Move; michael@0: using mozilla::PositiveInfinity; michael@0: using JS::GenericNaN; michael@0: michael@0: static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; michael@0: michael@0: /*****************************************************************************/ michael@0: // ParseNode utilities michael@0: michael@0: static inline ParseNode * michael@0: NextNode(ParseNode *pn) michael@0: { michael@0: return pn->pn_next; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: UnaryKid(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_UNARY)); michael@0: return pn->pn_kid; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: ReturnExpr(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_RETURN)); michael@0: return UnaryKid(pn); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: BinaryRight(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_BINARY)); michael@0: return pn->pn_right; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: BinaryLeft(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_BINARY)); michael@0: return pn->pn_left; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: TernaryKid1(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_TERNARY)); michael@0: return pn->pn_kid1; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: TernaryKid2(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_TERNARY)); michael@0: return pn->pn_kid2; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: TernaryKid3(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_TERNARY)); michael@0: return pn->pn_kid3; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: ListHead(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_LIST)); michael@0: return pn->pn_head; michael@0: } michael@0: michael@0: static inline unsigned michael@0: ListLength(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isArity(PN_LIST)); michael@0: return pn->pn_count; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: CallCallee(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_CALL)); michael@0: return ListHead(pn); michael@0: } michael@0: michael@0: static inline unsigned michael@0: CallArgListLength(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_CALL)); michael@0: JS_ASSERT(ListLength(pn) >= 1); michael@0: return ListLength(pn) - 1; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: CallArgList(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_CALL)); michael@0: return NextNode(ListHead(pn)); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: VarListHead(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)); michael@0: return ListHead(pn); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: CaseExpr(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); michael@0: return BinaryLeft(pn); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: CaseBody(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); michael@0: return BinaryRight(pn); michael@0: } michael@0: michael@0: static inline bool michael@0: IsExpressionStatement(ParseNode *pn) michael@0: { michael@0: return pn->isKind(PNK_SEMI); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: ExpressionStatementExpr(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_SEMI)); michael@0: return UnaryKid(pn); michael@0: } michael@0: michael@0: static inline PropertyName * michael@0: LoopControlMaybeLabel(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE)); michael@0: JS_ASSERT(pn->isArity(PN_NULLARY)); michael@0: return pn->as().label(); michael@0: } michael@0: michael@0: static inline PropertyName * michael@0: LabeledStatementLabel(ParseNode *pn) michael@0: { michael@0: return pn->as().label(); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: LabeledStatementStatement(ParseNode *pn) michael@0: { michael@0: return pn->as().statement(); michael@0: } michael@0: michael@0: static double michael@0: NumberNodeValue(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_NUMBER)); michael@0: return pn->pn_dval; michael@0: } michael@0: michael@0: static bool michael@0: NumberNodeHasFrac(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_NUMBER)); michael@0: return pn->pn_u.number.decimalPoint == HasDecimal; michael@0: } michael@0: michael@0: static ParseNode * michael@0: DotBase(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_DOT)); michael@0: JS_ASSERT(pn->isArity(PN_NAME)); michael@0: return pn->expr(); michael@0: } michael@0: michael@0: static PropertyName * michael@0: DotMember(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_DOT)); michael@0: JS_ASSERT(pn->isArity(PN_NAME)); michael@0: return pn->pn_atom->asPropertyName(); michael@0: } michael@0: michael@0: static ParseNode * michael@0: ElemBase(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_ELEM)); michael@0: return BinaryLeft(pn); michael@0: } michael@0: michael@0: static ParseNode * michael@0: ElemIndex(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_ELEM)); michael@0: return BinaryRight(pn); michael@0: } michael@0: michael@0: static inline JSFunction * michael@0: FunctionObject(ParseNode *fn) michael@0: { michael@0: JS_ASSERT(fn->isKind(PNK_FUNCTION)); michael@0: JS_ASSERT(fn->isArity(PN_CODE)); michael@0: return fn->pn_funbox->function(); michael@0: } michael@0: michael@0: static inline PropertyName * michael@0: FunctionName(ParseNode *fn) michael@0: { michael@0: if (JSAtom *atom = FunctionObject(fn)->atom()) michael@0: return atom->asPropertyName(); michael@0: return nullptr; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: FunctionStatementList(ParseNode *fn) michael@0: { michael@0: JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); michael@0: ParseNode *last = fn->pn_body->last(); michael@0: JS_ASSERT(last->isKind(PNK_STATEMENTLIST)); michael@0: return last; michael@0: } michael@0: michael@0: static inline bool michael@0: IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_COLON)); michael@0: return pn->getOp() == JSOP_INITPROP && michael@0: BinaryLeft(pn)->isKind(PNK_NAME) && michael@0: BinaryLeft(pn)->name() != cx->names().proto; michael@0: } michael@0: michael@0: static inline PropertyName * michael@0: ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn) michael@0: { michael@0: JS_ASSERT(IsNormalObjectField(cx, pn)); michael@0: return BinaryLeft(pn)->name(); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: ObjectFieldInitializer(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(pn->isKind(PNK_COLON)); michael@0: return BinaryRight(pn); michael@0: } michael@0: michael@0: static inline bool michael@0: IsDefinition(ParseNode *pn) michael@0: { michael@0: return pn->isKind(PNK_NAME) && pn->isDefn(); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: MaybeDefinitionInitializer(ParseNode *pn) michael@0: { michael@0: JS_ASSERT(IsDefinition(pn)); michael@0: return pn->expr(); michael@0: } michael@0: michael@0: static inline bool michael@0: IsUseOfName(ParseNode *pn, PropertyName *name) michael@0: { michael@0: return pn->isKind(PNK_NAME) && pn->name() == name; michael@0: } michael@0: michael@0: static inline bool michael@0: IsEmptyStatement(ParseNode *pn) michael@0: { michael@0: return pn->isKind(PNK_SEMI) && !UnaryKid(pn); michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: SkipEmptyStatements(ParseNode *pn) michael@0: { michael@0: while (pn && IsEmptyStatement(pn)) michael@0: pn = pn->pn_next; michael@0: return pn; michael@0: } michael@0: michael@0: static inline ParseNode * michael@0: NextNonEmptyStatement(ParseNode *pn) michael@0: { michael@0: return SkipEmptyStatements(pn->pn_next); michael@0: } michael@0: michael@0: static TokenKind michael@0: PeekToken(AsmJSParser &parser) michael@0: { michael@0: TokenStream &ts = parser.tokenStream; michael@0: while (ts.peekToken(TokenStream::Operand) == TOK_SEMI) michael@0: ts.consumeKnownToken(TOK_SEMI); michael@0: return ts.peekToken(TokenStream::Operand); michael@0: } michael@0: michael@0: static bool michael@0: ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var) michael@0: { michael@0: TokenKind tk = PeekToken(parser); michael@0: if (tk != TOK_VAR && tk != TOK_CONST) { michael@0: *var = nullptr; michael@0: return true; michael@0: } michael@0: michael@0: *var = parser.statement(); michael@0: if (!*var) michael@0: return false; michael@0: michael@0: JS_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST)); michael@0: return true; michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: namespace { michael@0: michael@0: // Respresents the type of a general asm.js expression. michael@0: class Type michael@0: { michael@0: public: michael@0: enum Which { michael@0: Double, michael@0: MaybeDouble, michael@0: Float, michael@0: MaybeFloat, michael@0: Floatish, michael@0: Fixnum, michael@0: Int, michael@0: Signed, michael@0: Unsigned, michael@0: Intish, michael@0: Void michael@0: }; michael@0: michael@0: private: michael@0: Which which_; michael@0: michael@0: public: michael@0: Type() : which_(Which(-1)) {} michael@0: Type(Which w) : which_(w) {} michael@0: michael@0: bool operator==(Type rhs) const { return which_ == rhs.which_; } michael@0: bool operator!=(Type rhs) const { return which_ != rhs.which_; } michael@0: michael@0: bool isSigned() const { michael@0: return which_ == Signed || which_ == Fixnum; michael@0: } michael@0: michael@0: bool isUnsigned() const { michael@0: return which_ == Unsigned || which_ == Fixnum; michael@0: } michael@0: michael@0: bool isInt() const { michael@0: return isSigned() || isUnsigned() || which_ == Int; michael@0: } michael@0: michael@0: bool isIntish() const { michael@0: return isInt() || which_ == Intish; michael@0: } michael@0: michael@0: bool isDouble() const { michael@0: return which_ == Double; michael@0: } michael@0: michael@0: bool isMaybeDouble() const { michael@0: return isDouble() || which_ == MaybeDouble; michael@0: } michael@0: michael@0: bool isFloat() const { michael@0: return which_ == Float; michael@0: } michael@0: michael@0: bool isMaybeFloat() const { michael@0: return isFloat() || which_ == MaybeFloat; michael@0: } michael@0: michael@0: bool isFloatish() const { michael@0: return isMaybeFloat() || which_ == Floatish; michael@0: } michael@0: michael@0: bool isVoid() const { michael@0: return which_ == Void; michael@0: } michael@0: michael@0: bool isExtern() const { michael@0: return isDouble() || isSigned(); michael@0: } michael@0: michael@0: bool isVarType() const { michael@0: return isInt() || isDouble() || isFloat(); michael@0: } michael@0: michael@0: MIRType toMIRType() const { michael@0: switch (which_) { michael@0: case Double: michael@0: case MaybeDouble: michael@0: return MIRType_Double; michael@0: case Float: michael@0: case Floatish: michael@0: case MaybeFloat: michael@0: return MIRType_Float32; michael@0: case Fixnum: michael@0: case Int: michael@0: case Signed: michael@0: case Unsigned: michael@0: case Intish: michael@0: return MIRType_Int32; michael@0: case Void: michael@0: return MIRType_None; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid Type"); michael@0: } michael@0: michael@0: const char *toChars() const { michael@0: switch (which_) { michael@0: case Double: return "double"; michael@0: case MaybeDouble: return "double?"; michael@0: case Float: return "float"; michael@0: case Floatish: return "floatish"; michael@0: case MaybeFloat: return "float?"; michael@0: case Fixnum: return "fixnum"; michael@0: case Int: return "int"; michael@0: case Signed: return "signed"; michael@0: case Unsigned: return "unsigned"; michael@0: case Intish: return "intish"; michael@0: case Void: return "void"; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid Type"); michael@0: } michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: // Represents the subset of Type that can be used as the return type of a michael@0: // function. michael@0: class RetType michael@0: { michael@0: public: michael@0: enum Which { michael@0: Void = Type::Void, michael@0: Signed = Type::Signed, michael@0: Double = Type::Double, michael@0: Float = Type::Float michael@0: }; michael@0: michael@0: private: michael@0: Which which_; michael@0: michael@0: public: michael@0: RetType() : which_(Which(-1)) {} michael@0: RetType(Which w) : which_(w) {} michael@0: RetType(AsmJSCoercion coercion) { michael@0: switch (coercion) { michael@0: case AsmJS_ToInt32: which_ = Signed; break; michael@0: case AsmJS_ToNumber: which_ = Double; break; michael@0: case AsmJS_FRound: which_ = Float; break; michael@0: } michael@0: } michael@0: Which which() const { michael@0: return which_; michael@0: } michael@0: Type toType() const { michael@0: return Type::Which(which_); michael@0: } michael@0: AsmJSModule::ReturnType toModuleReturnType() const { michael@0: switch (which_) { michael@0: case Void: return AsmJSModule::Return_Void; michael@0: case Signed: return AsmJSModule::Return_Int32; michael@0: case Float: // will be converted to a Double michael@0: case Double: return AsmJSModule::Return_Double; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected return type"); michael@0: } michael@0: MIRType toMIRType() const { michael@0: switch (which_) { michael@0: case Void: return MIRType_None; michael@0: case Signed: return MIRType_Int32; michael@0: case Double: return MIRType_Double; michael@0: case Float: return MIRType_Float32; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected return type"); michael@0: } michael@0: bool operator==(RetType rhs) const { return which_ == rhs.which_; } michael@0: bool operator!=(RetType rhs) const { return which_ != rhs.which_; } michael@0: }; michael@0: michael@0: namespace { michael@0: michael@0: // Represents the subset of Type that can be used as a variable or michael@0: // argument's type. Note: AsmJSCoercion and VarType are kept separate to michael@0: // make very clear the signed/int distinction: a coercion may explicitly sign michael@0: // an *expression* but, when stored as a variable, this signedness information michael@0: // is explicitly thrown away by the asm.js type system. E.g., in michael@0: // michael@0: // function f(i) { michael@0: // i = i | 0; (1) michael@0: // if (...) michael@0: // i = foo() >>> 0; michael@0: // else michael@0: // i = bar() | 0; michael@0: // return i | 0; (2) michael@0: // } michael@0: // michael@0: // the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when michael@0: // translated to an VarType, the result is a plain Int since, as shown, it michael@0: // is legal to assign both Signed and Unsigned (or some other Int) values to michael@0: // it. For (2), the AsmJSCoercion is also Signed but, when translated to an michael@0: // RetType, the result is Signed since callers (asm.js and non-asm.js) can michael@0: // rely on the return value being Signed. michael@0: class VarType michael@0: { michael@0: public: michael@0: enum Which { michael@0: Int = Type::Int, michael@0: Double = Type::Double, michael@0: Float = Type::Float michael@0: }; michael@0: michael@0: private: michael@0: Which which_; michael@0: michael@0: public: michael@0: VarType() michael@0: : which_(Which(-1)) {} michael@0: VarType(Which w) michael@0: : which_(w) {} michael@0: VarType(AsmJSCoercion coercion) { michael@0: switch (coercion) { michael@0: case AsmJS_ToInt32: which_ = Int; break; michael@0: case AsmJS_ToNumber: which_ = Double; break; michael@0: case AsmJS_FRound: which_ = Float; break; michael@0: } michael@0: } michael@0: Which which() const { michael@0: return which_; michael@0: } michael@0: Type toType() const { michael@0: return Type::Which(which_); michael@0: } michael@0: MIRType toMIRType() const { michael@0: switch(which_) { michael@0: case Int: return MIRType_Int32; michael@0: case Double: return MIRType_Double; michael@0: case Float: return MIRType_Float32; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); michael@0: } michael@0: AsmJSCoercion toCoercion() const { michael@0: switch(which_) { michael@0: case Int: return AsmJS_ToInt32; michael@0: case Double: return AsmJS_ToNumber; michael@0: case Float: return AsmJS_FRound; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); michael@0: } michael@0: static VarType FromCheckedType(Type type) { michael@0: JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish()); michael@0: if (type.isMaybeDouble()) michael@0: return Double; michael@0: else if (type.isFloatish()) michael@0: return Float; michael@0: else michael@0: return Int; michael@0: } michael@0: bool operator==(VarType rhs) const { return which_ == rhs.which_; } michael@0: bool operator!=(VarType rhs) const { return which_ != rhs.which_; } michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: // Implements <: (subtype) operator when the rhs is an VarType michael@0: static inline bool michael@0: operator<=(Type lhs, VarType rhs) michael@0: { michael@0: switch (rhs.which()) { michael@0: case VarType::Int: return lhs.isInt(); michael@0: case VarType::Double: return lhs.isDouble(); michael@0: case VarType::Float: return lhs.isFloat(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected rhs type"); michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: static inline MIRType ToMIRType(MIRType t) { return t; } michael@0: static inline MIRType ToMIRType(VarType t) { return t.toMIRType(); } michael@0: michael@0: namespace { michael@0: michael@0: template michael@0: class ABIArgIter michael@0: { michael@0: ABIArgGenerator gen_; michael@0: const VecT &types_; michael@0: unsigned i_; michael@0: michael@0: void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); } michael@0: michael@0: public: michael@0: ABIArgIter(const VecT &types) : types_(types), i_(0) { settle(); } michael@0: void operator++(int) { JS_ASSERT(!done()); i_++; settle(); } michael@0: bool done() const { return i_ == types_.length(); } michael@0: michael@0: ABIArg *operator->() { JS_ASSERT(!done()); return &gen_.current(); } michael@0: ABIArg &operator*() { JS_ASSERT(!done()); return gen_.current(); } michael@0: michael@0: unsigned index() const { JS_ASSERT(!done()); return i_; } michael@0: MIRType mirType() const { JS_ASSERT(!done()); return ToMIRType(types_[i_]); } michael@0: uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); } michael@0: }; michael@0: michael@0: typedef js::Vector MIRTypeVector; michael@0: typedef ABIArgIter ABIArgMIRTypeIter; michael@0: michael@0: typedef js::Vector VarTypeVector; michael@0: typedef ABIArgIter ABIArgTypeIter; michael@0: michael@0: class Signature michael@0: { michael@0: VarTypeVector argTypes_; michael@0: RetType retType_; michael@0: michael@0: public: michael@0: Signature(LifoAlloc &alloc) michael@0: : argTypes_(alloc) {} michael@0: Signature(LifoAlloc &alloc, RetType retType) michael@0: : argTypes_(alloc), retType_(retType) {} michael@0: Signature(VarTypeVector &&argTypes, RetType retType) michael@0: : argTypes_(Move(argTypes)), retType_(Move(retType)) {} michael@0: Signature(Signature &&rhs) michael@0: : argTypes_(Move(rhs.argTypes_)), retType_(Move(rhs.retType_)) {} michael@0: michael@0: bool copy(const Signature &rhs) { michael@0: if (!argTypes_.resize(rhs.argTypes_.length())) michael@0: return false; michael@0: for (unsigned i = 0; i < argTypes_.length(); i++) michael@0: argTypes_[i] = rhs.argTypes_[i]; michael@0: retType_ = rhs.retType_; michael@0: return true; michael@0: } michael@0: michael@0: bool appendArg(VarType type) { return argTypes_.append(type); } michael@0: VarType arg(unsigned i) const { return argTypes_[i]; } michael@0: const VarTypeVector &args() const { return argTypes_; } michael@0: VarTypeVector &&extractArgs() { return Move(argTypes_); } michael@0: michael@0: RetType retType() const { return retType_; } michael@0: }; michael@0: michael@0: } /* namespace anonymous */ michael@0: michael@0: static michael@0: bool operator==(const Signature &lhs, const Signature &rhs) michael@0: { michael@0: if (lhs.retType() != rhs.retType()) michael@0: return false; michael@0: if (lhs.args().length() != rhs.args().length()) michael@0: return false; michael@0: for (unsigned i = 0; i < lhs.args().length(); i++) { michael@0: if (lhs.arg(i) != rhs.arg(i)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static inline michael@0: bool operator!=(const Signature &lhs, const Signature &rhs) michael@0: { michael@0: return !(lhs == rhs); michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: // Typed array utilities michael@0: michael@0: static Type michael@0: TypedArrayLoadType(ArrayBufferView::ViewType viewType) michael@0: { michael@0: switch (viewType) { michael@0: case ArrayBufferView::TYPE_INT8: michael@0: case ArrayBufferView::TYPE_INT16: michael@0: case ArrayBufferView::TYPE_INT32: michael@0: case ArrayBufferView::TYPE_UINT8: michael@0: case ArrayBufferView::TYPE_UINT16: michael@0: case ArrayBufferView::TYPE_UINT32: michael@0: return Type::Intish; michael@0: case ArrayBufferView::TYPE_FLOAT32: michael@0: return Type::MaybeFloat; michael@0: case ArrayBufferView::TYPE_FLOAT64: michael@0: return Type::MaybeDouble; michael@0: default:; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected array type"); michael@0: } michael@0: michael@0: enum NeedsBoundsCheck { michael@0: NO_BOUNDS_CHECK, michael@0: NEEDS_BOUNDS_CHECK michael@0: }; michael@0: michael@0: namespace { michael@0: michael@0: typedef js::Vector LabelVector; michael@0: typedef js::Vector BlockVector; michael@0: michael@0: // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over michael@0: // the course of an ModuleCompiler object's lifetime, many FunctionCompiler michael@0: // objects will be created and destroyed in sequence, one for each function in michael@0: // the module. michael@0: // michael@0: // *** asm.js FFI calls *** michael@0: // michael@0: // asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type michael@0: // system does not place any constraints on the FFI call. In particular: michael@0: // - an FFI call's target is not known or speculated at module-compile time; michael@0: // - a single external function can be called with different signatures. michael@0: // michael@0: // If performance didn't matter, all FFI calls could simply box their arguments michael@0: // and call js::Invoke. However, we'd like to be able to specialize FFI calls michael@0: // to be more efficient in several cases: michael@0: // michael@0: // - for calls to JS functions which have been jitted, we'd like to call michael@0: // directly into JIT code without going through C++. michael@0: // michael@0: // - for calls to certain builtins, we'd like to be call directly into the C++ michael@0: // code for the builtin without going through the general call path. michael@0: // michael@0: // All of this requires dynamic specialization techniques which must happen michael@0: // after module compilation. To support this, at module-compilation time, each michael@0: // FFI call generates a call signature according to the system ABI, as if the michael@0: // callee was a C++ function taking/returning the same types as the caller was michael@0: // passing/expecting. The callee is loaded from a fixed offset in the global michael@0: // data array which allows the callee to change at runtime. Initially, the michael@0: // callee is stub which boxes its arguments and calls js::Invoke. michael@0: // michael@0: // To do this, we need to generate a callee stub for each pairing of FFI callee michael@0: // and signature. We call this pairing an "exit". For example, this code has michael@0: // two external functions and three exits: michael@0: // michael@0: // function f(global, imports) { michael@0: // "use asm"; michael@0: // var foo = imports.foo; michael@0: // var bar = imports.bar; michael@0: // function g() { michael@0: // foo(1); // Exit #1: (int) -> void michael@0: // foo(1.5); // Exit #2: (double) -> void michael@0: // bar(1)|0; // Exit #3: (int) -> int michael@0: // bar(2)|0; // Exit #3: (int) -> int michael@0: // } michael@0: // } michael@0: // michael@0: // The ModuleCompiler maintains a hash table (ExitMap) which allows a call site michael@0: // to add a new exit or reuse an existing one. The key is an ExitDescriptor michael@0: // (which holds the exit pairing) and the value is an index into the michael@0: // Vector stored in the AsmJSModule. michael@0: // michael@0: // Rooting note: ModuleCompiler is a stack class that contains unrooted michael@0: // PropertyName (JSAtom) pointers. This is safe because it cannot be michael@0: // constructed without a TokenStream reference. TokenStream is itself a stack michael@0: // class that cannot be constructed without an AutoKeepAtoms being live on the michael@0: // stack, which prevents collection of atoms. michael@0: // michael@0: // ModuleCompiler is marked as rooted in the rooting analysis. Don't add michael@0: // non-JSAtom pointers, or this will break! michael@0: class MOZ_STACK_CLASS ModuleCompiler michael@0: { michael@0: public: michael@0: class Func michael@0: { michael@0: PropertyName *name_; michael@0: bool defined_; michael@0: uint32_t srcOffset_; michael@0: uint32_t endOffset_; michael@0: Signature sig_; michael@0: Label *code_; michael@0: unsigned compileTime_; michael@0: michael@0: public: michael@0: Func(PropertyName *name, Signature &&sig, Label *code) michael@0: : name_(name), defined_(false), srcOffset_(0), endOffset_(0), sig_(Move(sig)), michael@0: code_(code), compileTime_(0) michael@0: {} michael@0: michael@0: PropertyName *name() const { return name_; } michael@0: michael@0: bool defined() const { return defined_; } michael@0: void finish(uint32_t start, uint32_t end) { michael@0: JS_ASSERT(!defined_); michael@0: defined_ = true; michael@0: srcOffset_ = start; michael@0: endOffset_ = end; michael@0: } michael@0: michael@0: uint32_t srcOffset() const { JS_ASSERT(defined_); return srcOffset_; } michael@0: uint32_t endOffset() const { JS_ASSERT(defined_); return endOffset_; } michael@0: Signature &sig() { return sig_; } michael@0: const Signature &sig() const { return sig_; } michael@0: Label *code() const { return code_; } michael@0: unsigned compileTime() const { return compileTime_; } michael@0: void accumulateCompileTime(unsigned ms) { compileTime_ += ms; } michael@0: }; michael@0: michael@0: class Global michael@0: { michael@0: public: michael@0: enum Which { michael@0: Variable, michael@0: ConstantLiteral, michael@0: ConstantImport, michael@0: Function, michael@0: FuncPtrTable, michael@0: FFI, michael@0: ArrayView, michael@0: MathBuiltinFunction michael@0: }; michael@0: michael@0: private: michael@0: Which which_; michael@0: union { michael@0: struct { michael@0: VarType::Which type_; michael@0: uint32_t index_; michael@0: Value literalValue_; michael@0: } varOrConst; michael@0: uint32_t funcIndex_; michael@0: uint32_t funcPtrTableIndex_; michael@0: uint32_t ffiIndex_; michael@0: ArrayBufferView::ViewType viewType_; michael@0: AsmJSMathBuiltinFunction mathBuiltinFunc_; michael@0: } u; michael@0: michael@0: friend class ModuleCompiler; michael@0: friend class js::LifoAlloc; michael@0: michael@0: Global(Which which) : which_(which) {} michael@0: michael@0: public: michael@0: Which which() const { michael@0: return which_; michael@0: } michael@0: VarType varOrConstType() const { michael@0: JS_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport); michael@0: return VarType(u.varOrConst.type_); michael@0: } michael@0: uint32_t varOrConstIndex() const { michael@0: JS_ASSERT(which_ == Variable || which_ == ConstantImport); michael@0: return u.varOrConst.index_; michael@0: } michael@0: bool isConst() const { michael@0: return which_ == ConstantLiteral || which_ == ConstantImport; michael@0: } michael@0: Value constLiteralValue() const { michael@0: JS_ASSERT(which_ == ConstantLiteral); michael@0: return u.varOrConst.literalValue_; michael@0: } michael@0: uint32_t funcIndex() const { michael@0: JS_ASSERT(which_ == Function); michael@0: return u.funcIndex_; michael@0: } michael@0: uint32_t funcPtrTableIndex() const { michael@0: JS_ASSERT(which_ == FuncPtrTable); michael@0: return u.funcPtrTableIndex_; michael@0: } michael@0: unsigned ffiIndex() const { michael@0: JS_ASSERT(which_ == FFI); michael@0: return u.ffiIndex_; michael@0: } michael@0: ArrayBufferView::ViewType viewType() const { michael@0: JS_ASSERT(which_ == ArrayView); michael@0: return u.viewType_; michael@0: } michael@0: AsmJSMathBuiltinFunction mathBuiltinFunction() const { michael@0: JS_ASSERT(which_ == MathBuiltinFunction); michael@0: return u.mathBuiltinFunc_; michael@0: } michael@0: }; michael@0: michael@0: typedef js::Vector FuncPtrVector; michael@0: michael@0: class FuncPtrTable michael@0: { michael@0: Signature sig_; michael@0: uint32_t mask_; michael@0: uint32_t globalDataOffset_; michael@0: FuncPtrVector elems_; michael@0: michael@0: public: michael@0: FuncPtrTable(ExclusiveContext *cx, Signature &&sig, uint32_t mask, uint32_t gdo) michael@0: : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), elems_(cx) michael@0: {} michael@0: michael@0: FuncPtrTable(FuncPtrTable &&rhs) michael@0: : sig_(Move(rhs.sig_)), mask_(rhs.mask_), globalDataOffset_(rhs.globalDataOffset_), michael@0: elems_(Move(rhs.elems_)) michael@0: {} michael@0: michael@0: Signature &sig() { return sig_; } michael@0: const Signature &sig() const { return sig_; } michael@0: unsigned mask() const { return mask_; } michael@0: unsigned globalDataOffset() const { return globalDataOffset_; } michael@0: michael@0: bool initialized() const { return !elems_.empty(); } michael@0: void initElems(FuncPtrVector &&elems) { elems_ = Move(elems); JS_ASSERT(initialized()); } michael@0: unsigned numElems() const { JS_ASSERT(initialized()); return elems_.length(); } michael@0: const Func &elem(unsigned i) const { return *elems_[i]; } michael@0: }; michael@0: michael@0: typedef js::Vector FuncPtrTableVector; michael@0: michael@0: class ExitDescriptor michael@0: { michael@0: PropertyName *name_; michael@0: Signature sig_; michael@0: michael@0: public: michael@0: ExitDescriptor(PropertyName *name, Signature &&sig) michael@0: : name_(name), sig_(Move(sig)) {} michael@0: ExitDescriptor(ExitDescriptor &&rhs) michael@0: : name_(rhs.name_), sig_(Move(rhs.sig_)) michael@0: {} michael@0: const Signature &sig() const { michael@0: return sig_; michael@0: } michael@0: michael@0: // ExitDescriptor is a HashPolicy: michael@0: typedef ExitDescriptor Lookup; michael@0: static HashNumber hash(const ExitDescriptor &d) { michael@0: HashNumber hn = HashGeneric(d.name_, d.sig_.retType().which()); michael@0: const VarTypeVector &args = d.sig_.args(); michael@0: for (unsigned i = 0; i < args.length(); i++) michael@0: hn = AddToHash(hn, args[i].which()); michael@0: return hn; michael@0: } michael@0: static bool match(const ExitDescriptor &lhs, const ExitDescriptor &rhs) { michael@0: return lhs.name_ == rhs.name_ && lhs.sig_ == rhs.sig_; michael@0: } michael@0: }; michael@0: michael@0: typedef HashMap ExitMap; michael@0: michael@0: struct MathBuiltin michael@0: { michael@0: enum Kind { Function, Constant }; michael@0: Kind kind; michael@0: michael@0: union { michael@0: double cst; michael@0: AsmJSMathBuiltinFunction func; michael@0: } u; michael@0: michael@0: MathBuiltin() : kind(Kind(-1)) {} michael@0: MathBuiltin(double cst) : kind(Constant) { michael@0: u.cst = cst; michael@0: } michael@0: MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) { michael@0: u.func = func; michael@0: } michael@0: }; michael@0: michael@0: private: michael@0: struct SlowFunction michael@0: { michael@0: PropertyName *name; michael@0: unsigned ms; michael@0: unsigned line; michael@0: unsigned column; michael@0: }; michael@0: michael@0: typedef HashMap MathNameMap; michael@0: typedef HashMap GlobalMap; michael@0: typedef js::Vector FuncVector; michael@0: typedef js::Vector GlobalAccessVector; michael@0: typedef js::Vector SlowFunctionVector; michael@0: michael@0: ExclusiveContext * cx_; michael@0: AsmJSParser & parser_; michael@0: michael@0: MacroAssembler masm_; michael@0: michael@0: ScopedJSDeletePtr module_; michael@0: LifoAlloc moduleLifo_; michael@0: ParseNode * moduleFunctionNode_; michael@0: PropertyName * moduleFunctionName_; michael@0: michael@0: GlobalMap globals_; michael@0: FuncVector functions_; michael@0: FuncPtrTableVector funcPtrTables_; michael@0: ExitMap exits_; michael@0: MathNameMap standardLibraryMathNames_; michael@0: Label stackOverflowLabel_; michael@0: Label interruptLabel_; michael@0: michael@0: char * errorString_; michael@0: uint32_t errorOffset_; michael@0: bool errorOverRecursed_; michael@0: michael@0: int64_t usecBefore_; michael@0: SlowFunctionVector slowFunctions_; michael@0: michael@0: DebugOnly finishedFunctionBodies_; michael@0: michael@0: bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) { michael@0: JSAtom *atom = Atomize(cx_, name, strlen(name)); michael@0: if (!atom) michael@0: return false; michael@0: MathBuiltin builtin(func); michael@0: return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin); michael@0: } michael@0: bool addStandardLibraryMathName(const char *name, double cst) { michael@0: JSAtom *atom = Atomize(cx_, name, strlen(name)); michael@0: if (!atom) michael@0: return false; michael@0: MathBuiltin builtin(cst); michael@0: return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin); michael@0: } michael@0: michael@0: public: michael@0: ModuleCompiler(ExclusiveContext *cx, AsmJSParser &parser) michael@0: : cx_(cx), michael@0: parser_(parser), michael@0: masm_(MacroAssembler::AsmJSToken()), michael@0: moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE), michael@0: moduleFunctionNode_(parser.pc->maybeFunction), michael@0: moduleFunctionName_(nullptr), michael@0: globals_(cx), michael@0: functions_(cx), michael@0: funcPtrTables_(cx), michael@0: exits_(cx), michael@0: standardLibraryMathNames_(cx), michael@0: errorString_(nullptr), michael@0: errorOffset_(UINT32_MAX), michael@0: errorOverRecursed_(false), michael@0: usecBefore_(PRMJ_Now()), michael@0: slowFunctions_(cx), michael@0: finishedFunctionBodies_(false) michael@0: { michael@0: JS_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox()); michael@0: } michael@0: michael@0: ~ModuleCompiler() { michael@0: if (errorString_) { michael@0: JS_ASSERT(errorOffset_ != UINT32_MAX); michael@0: tokenStream().reportAsmJSError(errorOffset_, michael@0: JSMSG_USE_ASM_TYPE_FAIL, michael@0: errorString_); michael@0: js_free(errorString_); michael@0: } michael@0: if (errorOverRecursed_) michael@0: js_ReportOverRecursed(cx_); michael@0: michael@0: // Avoid spurious Label assertions on compilation failure. michael@0: if (!stackOverflowLabel_.bound()) michael@0: stackOverflowLabel_.bind(0); michael@0: if (!interruptLabel_.bound()) michael@0: interruptLabel_.bind(0); michael@0: } michael@0: michael@0: bool init() { michael@0: if (!globals_.init() || !exits_.init()) michael@0: return false; michael@0: michael@0: if (!standardLibraryMathNames_.init() || michael@0: !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) || michael@0: !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) || michael@0: !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) || michael@0: !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) || michael@0: !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) || michael@0: !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) || michael@0: !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) || michael@0: !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) || michael@0: !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) || michael@0: !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) || michael@0: !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) || michael@0: !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) || michael@0: !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) || michael@0: !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) || michael@0: !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) || michael@0: !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) || michael@0: !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) || michael@0: !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) || michael@0: !addStandardLibraryMathName("E", M_E) || michael@0: !addStandardLibraryMathName("LN10", M_LN10) || michael@0: !addStandardLibraryMathName("LN2", M_LN2) || michael@0: !addStandardLibraryMathName("LOG2E", M_LOG2E) || michael@0: !addStandardLibraryMathName("LOG10E", M_LOG10E) || michael@0: !addStandardLibraryMathName("PI", M_PI) || michael@0: !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) || michael@0: !addStandardLibraryMathName("SQRT2", M_SQRT2)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t funcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin; michael@0: uint32_t offsetToEndOfUseAsm = tokenStream().currentToken().pos.end; michael@0: michael@0: // "use strict" should be added to the source if we are in an implicit michael@0: // strict context, see also comment above addUseStrict in michael@0: // js::FunctionToString. michael@0: bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict(); michael@0: michael@0: module_ = cx_->new_(parser_.ss, funcStart, offsetToEndOfUseAsm, strict); michael@0: if (!module_) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool failOffset(uint32_t offset, const char *str) { michael@0: JS_ASSERT(!errorString_); michael@0: JS_ASSERT(errorOffset_ == UINT32_MAX); michael@0: JS_ASSERT(str); michael@0: errorOffset_ = offset; michael@0: errorString_ = js_strdup(cx_, str); michael@0: return false; michael@0: } michael@0: michael@0: bool fail(ParseNode *pn, const char *str) { michael@0: if (pn) michael@0: return failOffset(pn->pn_pos.begin, str); michael@0: michael@0: // The exact rooting static analysis does not perform dataflow analysis, so it believes michael@0: // that unrooted things on the stack during compilation may still be accessed after this. michael@0: // Since pn is typically only null under OOM, this suppression simply forces any GC to be michael@0: // delayed until the compilation is off the stack and more memory can be freed. michael@0: gc::AutoSuppressGC nogc(cx_); michael@0: return failOffset(tokenStream().peekTokenPos().begin, str); michael@0: } michael@0: michael@0: bool failfVA(ParseNode *pn, const char *fmt, va_list ap) { michael@0: JS_ASSERT(!errorString_); michael@0: JS_ASSERT(errorOffset_ == UINT32_MAX); michael@0: JS_ASSERT(fmt); michael@0: errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end; michael@0: errorString_ = JS_vsmprintf(fmt, ap); michael@0: return false; michael@0: } michael@0: michael@0: bool failf(ParseNode *pn, const char *fmt, ...) { michael@0: va_list ap; michael@0: va_start(ap, fmt); michael@0: failfVA(pn, fmt, ap); michael@0: va_end(ap); michael@0: return false; michael@0: } michael@0: michael@0: bool failName(ParseNode *pn, const char *fmt, PropertyName *name) { michael@0: // This function is invoked without the caller properly rooting its locals. michael@0: gc::AutoSuppressGC suppress(cx_); michael@0: JSAutoByteString bytes; michael@0: if (AtomToPrintableString(cx_, name, &bytes)) michael@0: failf(pn, fmt, bytes.ptr()); michael@0: return false; michael@0: } michael@0: michael@0: bool failOverRecursed() { michael@0: errorOverRecursed_ = true; michael@0: return false; michael@0: } michael@0: michael@0: static const unsigned SLOW_FUNCTION_THRESHOLD_MS = 250; michael@0: michael@0: bool maybeReportCompileTime(const Func &func) { michael@0: if (func.compileTime() < SLOW_FUNCTION_THRESHOLD_MS) michael@0: return true; michael@0: SlowFunction sf; michael@0: sf.name = func.name(); michael@0: sf.ms = func.compileTime(); michael@0: tokenStream().srcCoords.lineNumAndColumnIndex(func.srcOffset(), &sf.line, &sf.column); michael@0: return slowFunctions_.append(sf); michael@0: } michael@0: michael@0: /*************************************************** Read-only interface */ michael@0: michael@0: ExclusiveContext *cx() const { return cx_; } michael@0: AsmJSParser &parser() const { return parser_; } michael@0: TokenStream &tokenStream() const { return parser_.tokenStream; } michael@0: MacroAssembler &masm() { return masm_; } michael@0: Label &stackOverflowLabel() { return stackOverflowLabel_; } michael@0: Label &interruptLabel() { return interruptLabel_; } michael@0: bool hasError() const { return errorString_ != nullptr; } michael@0: const AsmJSModule &module() const { return *module_.get(); } michael@0: uint32_t moduleStart() const { return module_->funcStart(); } michael@0: michael@0: ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; } michael@0: PropertyName *moduleFunctionName() const { return moduleFunctionName_; } michael@0: michael@0: const Global *lookupGlobal(PropertyName *name) const { michael@0: if (GlobalMap::Ptr p = globals_.lookup(name)) michael@0: return p->value(); michael@0: return nullptr; michael@0: } michael@0: Func *lookupFunction(PropertyName *name) { michael@0: if (GlobalMap::Ptr p = globals_.lookup(name)) { michael@0: Global *value = p->value(); michael@0: if (value->which() == Global::Function) michael@0: return functions_[value->funcIndex()]; michael@0: } michael@0: return nullptr; michael@0: } michael@0: unsigned numFunctions() const { michael@0: return functions_.length(); michael@0: } michael@0: Func &function(unsigned i) { michael@0: return *functions_[i]; michael@0: } michael@0: unsigned numFuncPtrTables() const { michael@0: return funcPtrTables_.length(); michael@0: } michael@0: FuncPtrTable &funcPtrTable(unsigned i) { michael@0: return funcPtrTables_[i]; michael@0: } michael@0: bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const { michael@0: if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) { michael@0: *mathBuiltin = p->value(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: ExitMap::Range allExits() const { michael@0: return exits_.all(); michael@0: } michael@0: michael@0: /***************************************************** Mutable interface */ michael@0: michael@0: void initModuleFunctionName(PropertyName *name) { moduleFunctionName_ = name; } michael@0: michael@0: void initGlobalArgumentName(PropertyName *n) { module_->initGlobalArgumentName(n); } michael@0: void initImportArgumentName(PropertyName *n) { module_->initImportArgumentName(n); } michael@0: void initBufferArgumentName(PropertyName *n) { module_->initBufferArgumentName(n); } michael@0: michael@0: bool addGlobalVarInit(PropertyName *varName, VarType type, const Value &v, bool isConst) { michael@0: uint32_t index; michael@0: if (!module_->addGlobalVarInit(v, type.toCoercion(), &index)) michael@0: return false; michael@0: michael@0: Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; michael@0: Global *global = moduleLifo_.new_(which); michael@0: if (!global) michael@0: return false; michael@0: global->u.varOrConst.index_ = index; michael@0: global->u.varOrConst.type_ = type.which(); michael@0: if (isConst) michael@0: global->u.varOrConst.literalValue_ = v; michael@0: michael@0: return globals_.putNew(varName, global); michael@0: } michael@0: bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion, michael@0: bool isConst) { michael@0: uint32_t index; michael@0: if (!module_->addGlobalVarImport(fieldName, coercion, &index)) michael@0: return false; michael@0: michael@0: Global::Which which = isConst ? Global::ConstantImport : Global::Variable; michael@0: Global *global = moduleLifo_.new_(which); michael@0: if (!global) michael@0: return false; michael@0: global->u.varOrConst.index_ = index; michael@0: global->u.varOrConst.type_ = VarType(coercion).which(); michael@0: michael@0: return globals_.putNew(varName, global); michael@0: } michael@0: bool addFunction(PropertyName *name, Signature &&sig, Func **func) { michael@0: JS_ASSERT(!finishedFunctionBodies_); michael@0: Global *global = moduleLifo_.new_(Global::Function); michael@0: if (!global) michael@0: return false; michael@0: global->u.funcIndex_ = functions_.length(); michael@0: if (!globals_.putNew(name, global)) michael@0: return false; michael@0: Label *code = moduleLifo_.new_