js/src/jit/AsmJS.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/AsmJS.h"
     9 #include "mozilla/Move.h"
    11 #ifdef MOZ_VTUNE
    12 # include "vtune/VTuneWrapper.h"
    13 #endif
    15 #include "jsmath.h"
    16 #include "jsprf.h"
    17 #include "jsworkers.h"
    18 #include "prmjtime.h"
    20 #include "assembler/assembler/MacroAssembler.h"
    21 #include "frontend/Parser.h"
    22 #include "jit/AsmJSLink.h"
    23 #include "jit/AsmJSModule.h"
    24 #include "jit/AsmJSSignalHandlers.h"
    25 #include "jit/CodeGenerator.h"
    26 #include "jit/CompileWrappers.h"
    27 #include "jit/MIR.h"
    28 #include "jit/MIRGraph.h"
    29 #ifdef JS_ION_PERF
    30 # include "jit/PerfSpewer.h"
    31 #endif
    32 #include "vm/Interpreter.h"
    34 #include "jsinferinlines.h"
    35 #include "jsobjinlines.h"
    37 #include "frontend/ParseNode-inl.h"
    38 #include "frontend/Parser-inl.h"
    40 using namespace js;
    41 using namespace js::frontend;
    42 using namespace js::jit;
    44 using mozilla::AddToHash;
    45 using mozilla::ArrayLength;
    46 using mozilla::CountLeadingZeroes32;
    47 using mozilla::DebugOnly;
    48 using mozilla::HashGeneric;
    49 using mozilla::IsNaN;
    50 using mozilla::IsNegativeZero;
    51 using mozilla::Maybe;
    52 using mozilla::Move;
    53 using mozilla::PositiveInfinity;
    54 using JS::GenericNaN;
    56 static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
    58 /*****************************************************************************/
    59 // ParseNode utilities
    61 static inline ParseNode *
    62 NextNode(ParseNode *pn)
    63 {
    64     return pn->pn_next;
    65 }
    67 static inline ParseNode *
    68 UnaryKid(ParseNode *pn)
    69 {
    70     JS_ASSERT(pn->isArity(PN_UNARY));
    71     return pn->pn_kid;
    72 }
    74 static inline ParseNode *
    75 ReturnExpr(ParseNode *pn)
    76 {
    77     JS_ASSERT(pn->isKind(PNK_RETURN));
    78     return UnaryKid(pn);
    79 }
    81 static inline ParseNode *
    82 BinaryRight(ParseNode *pn)
    83 {
    84     JS_ASSERT(pn->isArity(PN_BINARY));
    85     return pn->pn_right;
    86 }
    88 static inline ParseNode *
    89 BinaryLeft(ParseNode *pn)
    90 {
    91     JS_ASSERT(pn->isArity(PN_BINARY));
    92     return pn->pn_left;
    93 }
    95 static inline ParseNode *
    96 TernaryKid1(ParseNode *pn)
    97 {
    98     JS_ASSERT(pn->isArity(PN_TERNARY));
    99     return pn->pn_kid1;
   100 }
   102 static inline ParseNode *
   103 TernaryKid2(ParseNode *pn)
   104 {
   105     JS_ASSERT(pn->isArity(PN_TERNARY));
   106     return pn->pn_kid2;
   107 }
   109 static inline ParseNode *
   110 TernaryKid3(ParseNode *pn)
   111 {
   112     JS_ASSERT(pn->isArity(PN_TERNARY));
   113     return pn->pn_kid3;
   114 }
   116 static inline ParseNode *
   117 ListHead(ParseNode *pn)
   118 {
   119     JS_ASSERT(pn->isArity(PN_LIST));
   120     return pn->pn_head;
   121 }
   123 static inline unsigned
   124 ListLength(ParseNode *pn)
   125 {
   126     JS_ASSERT(pn->isArity(PN_LIST));
   127     return pn->pn_count;
   128 }
   130 static inline ParseNode *
   131 CallCallee(ParseNode *pn)
   132 {
   133     JS_ASSERT(pn->isKind(PNK_CALL));
   134     return ListHead(pn);
   135 }
   137 static inline unsigned
   138 CallArgListLength(ParseNode *pn)
   139 {
   140     JS_ASSERT(pn->isKind(PNK_CALL));
   141     JS_ASSERT(ListLength(pn) >= 1);
   142     return ListLength(pn) - 1;
   143 }
   145 static inline ParseNode *
   146 CallArgList(ParseNode *pn)
   147 {
   148     JS_ASSERT(pn->isKind(PNK_CALL));
   149     return NextNode(ListHead(pn));
   150 }
   152 static inline ParseNode *
   153 VarListHead(ParseNode *pn)
   154 {
   155     JS_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST));
   156     return ListHead(pn);
   157 }
   159 static inline ParseNode *
   160 CaseExpr(ParseNode *pn)
   161 {
   162     JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
   163     return BinaryLeft(pn);
   164 }
   166 static inline ParseNode *
   167 CaseBody(ParseNode *pn)
   168 {
   169     JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
   170     return BinaryRight(pn);
   171 }
   173 static inline bool
   174 IsExpressionStatement(ParseNode *pn)
   175 {
   176     return pn->isKind(PNK_SEMI);
   177 }
   179 static inline ParseNode *
   180 ExpressionStatementExpr(ParseNode *pn)
   181 {
   182     JS_ASSERT(pn->isKind(PNK_SEMI));
   183     return UnaryKid(pn);
   184 }
   186 static inline PropertyName *
   187 LoopControlMaybeLabel(ParseNode *pn)
   188 {
   189     JS_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE));
   190     JS_ASSERT(pn->isArity(PN_NULLARY));
   191     return pn->as<LoopControlStatement>().label();
   192 }
   194 static inline PropertyName *
   195 LabeledStatementLabel(ParseNode *pn)
   196 {
   197     return pn->as<LabeledStatement>().label();
   198 }
   200 static inline ParseNode *
   201 LabeledStatementStatement(ParseNode *pn)
   202 {
   203     return pn->as<LabeledStatement>().statement();
   204 }
   206 static double
   207 NumberNodeValue(ParseNode *pn)
   208 {
   209     JS_ASSERT(pn->isKind(PNK_NUMBER));
   210     return pn->pn_dval;
   211 }
   213 static bool
   214 NumberNodeHasFrac(ParseNode *pn)
   215 {
   216     JS_ASSERT(pn->isKind(PNK_NUMBER));
   217     return pn->pn_u.number.decimalPoint == HasDecimal;
   218 }
   220 static ParseNode *
   221 DotBase(ParseNode *pn)
   222 {
   223     JS_ASSERT(pn->isKind(PNK_DOT));
   224     JS_ASSERT(pn->isArity(PN_NAME));
   225     return pn->expr();
   226 }
   228 static PropertyName *
   229 DotMember(ParseNode *pn)
   230 {
   231     JS_ASSERT(pn->isKind(PNK_DOT));
   232     JS_ASSERT(pn->isArity(PN_NAME));
   233     return pn->pn_atom->asPropertyName();
   234 }
   236 static ParseNode *
   237 ElemBase(ParseNode *pn)
   238 {
   239     JS_ASSERT(pn->isKind(PNK_ELEM));
   240     return BinaryLeft(pn);
   241 }
   243 static ParseNode *
   244 ElemIndex(ParseNode *pn)
   245 {
   246     JS_ASSERT(pn->isKind(PNK_ELEM));
   247     return BinaryRight(pn);
   248 }
   250 static inline JSFunction *
   251 FunctionObject(ParseNode *fn)
   252 {
   253     JS_ASSERT(fn->isKind(PNK_FUNCTION));
   254     JS_ASSERT(fn->isArity(PN_CODE));
   255     return fn->pn_funbox->function();
   256 }
   258 static inline PropertyName *
   259 FunctionName(ParseNode *fn)
   260 {
   261     if (JSAtom *atom = FunctionObject(fn)->atom())
   262         return atom->asPropertyName();
   263     return nullptr;
   264 }
   266 static inline ParseNode *
   267 FunctionStatementList(ParseNode *fn)
   268 {
   269     JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
   270     ParseNode *last = fn->pn_body->last();
   271     JS_ASSERT(last->isKind(PNK_STATEMENTLIST));
   272     return last;
   273 }
   275 static inline bool
   276 IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn)
   277 {
   278     JS_ASSERT(pn->isKind(PNK_COLON));
   279     return pn->getOp() == JSOP_INITPROP &&
   280            BinaryLeft(pn)->isKind(PNK_NAME) &&
   281            BinaryLeft(pn)->name() != cx->names().proto;
   282 }
   284 static inline PropertyName *
   285 ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn)
   286 {
   287     JS_ASSERT(IsNormalObjectField(cx, pn));
   288     return BinaryLeft(pn)->name();
   289 }
   291 static inline ParseNode *
   292 ObjectFieldInitializer(ParseNode *pn)
   293 {
   294     JS_ASSERT(pn->isKind(PNK_COLON));
   295     return BinaryRight(pn);
   296 }
   298 static inline bool
   299 IsDefinition(ParseNode *pn)
   300 {
   301     return pn->isKind(PNK_NAME) && pn->isDefn();
   302 }
   304 static inline ParseNode *
   305 MaybeDefinitionInitializer(ParseNode *pn)
   306 {
   307     JS_ASSERT(IsDefinition(pn));
   308     return pn->expr();
   309 }
   311 static inline bool
   312 IsUseOfName(ParseNode *pn, PropertyName *name)
   313 {
   314     return pn->isKind(PNK_NAME) && pn->name() == name;
   315 }
   317 static inline bool
   318 IsEmptyStatement(ParseNode *pn)
   319 {
   320     return pn->isKind(PNK_SEMI) && !UnaryKid(pn);
   321 }
   323 static inline ParseNode *
   324 SkipEmptyStatements(ParseNode *pn)
   325 {
   326     while (pn && IsEmptyStatement(pn))
   327         pn = pn->pn_next;
   328     return pn;
   329 }
   331 static inline ParseNode *
   332 NextNonEmptyStatement(ParseNode *pn)
   333 {
   334     return SkipEmptyStatements(pn->pn_next);
   335 }
   337 static TokenKind
   338 PeekToken(AsmJSParser &parser)
   339 {
   340     TokenStream &ts = parser.tokenStream;
   341     while (ts.peekToken(TokenStream::Operand) == TOK_SEMI)
   342         ts.consumeKnownToken(TOK_SEMI);
   343     return ts.peekToken(TokenStream::Operand);
   344 }
   346 static bool
   347 ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var)
   348 {
   349     TokenKind tk = PeekToken(parser);
   350     if (tk != TOK_VAR && tk != TOK_CONST) {
   351         *var = nullptr;
   352         return true;
   353     }
   355     *var = parser.statement();
   356     if (!*var)
   357         return false;
   359     JS_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST));
   360     return true;
   361 }
   363 /*****************************************************************************/
   365 namespace {
   367 // Respresents the type of a general asm.js expression.
   368 class Type
   369 {
   370   public:
   371     enum Which {
   372         Double,
   373         MaybeDouble,
   374         Float,
   375         MaybeFloat,
   376         Floatish,
   377         Fixnum,
   378         Int,
   379         Signed,
   380         Unsigned,
   381         Intish,
   382         Void
   383     };
   385   private:
   386     Which which_;
   388   public:
   389     Type() : which_(Which(-1)) {}
   390     Type(Which w) : which_(w) {}
   392     bool operator==(Type rhs) const { return which_ == rhs.which_; }
   393     bool operator!=(Type rhs) const { return which_ != rhs.which_; }
   395     bool isSigned() const {
   396         return which_ == Signed || which_ == Fixnum;
   397     }
   399     bool isUnsigned() const {
   400         return which_ == Unsigned || which_ == Fixnum;
   401     }
   403     bool isInt() const {
   404         return isSigned() || isUnsigned() || which_ == Int;
   405     }
   407     bool isIntish() const {
   408         return isInt() || which_ == Intish;
   409     }
   411     bool isDouble() const {
   412         return which_ == Double;
   413     }
   415     bool isMaybeDouble() const {
   416         return isDouble() || which_ == MaybeDouble;
   417     }
   419     bool isFloat() const {
   420         return which_ == Float;
   421     }
   423     bool isMaybeFloat() const {
   424         return isFloat() || which_ == MaybeFloat;
   425     }
   427     bool isFloatish() const {
   428         return isMaybeFloat() || which_ == Floatish;
   429     }
   431     bool isVoid() const {
   432         return which_ == Void;
   433     }
   435     bool isExtern() const {
   436         return isDouble() || isSigned();
   437     }
   439     bool isVarType() const {
   440         return isInt() || isDouble() || isFloat();
   441     }
   443     MIRType toMIRType() const {
   444         switch (which_) {
   445           case Double:
   446           case MaybeDouble:
   447             return MIRType_Double;
   448           case Float:
   449           case Floatish:
   450           case MaybeFloat:
   451             return MIRType_Float32;
   452           case Fixnum:
   453           case Int:
   454           case Signed:
   455           case Unsigned:
   456           case Intish:
   457             return MIRType_Int32;
   458           case Void:
   459             return MIRType_None;
   460         }
   461         MOZ_ASSUME_UNREACHABLE("Invalid Type");
   462     }
   464     const char *toChars() const {
   465         switch (which_) {
   466           case Double:      return "double";
   467           case MaybeDouble: return "double?";
   468           case Float:       return "float";
   469           case Floatish:    return "floatish";
   470           case MaybeFloat:  return "float?";
   471           case Fixnum:      return "fixnum";
   472           case Int:         return "int";
   473           case Signed:      return "signed";
   474           case Unsigned:    return "unsigned";
   475           case Intish:      return "intish";
   476           case Void:        return "void";
   477         }
   478         MOZ_ASSUME_UNREACHABLE("Invalid Type");
   479     }
   480 };
   482 } /* anonymous namespace */
   484 // Represents the subset of Type that can be used as the return type of a
   485 // function.
   486 class RetType
   487 {
   488   public:
   489     enum Which {
   490         Void = Type::Void,
   491         Signed = Type::Signed,
   492         Double = Type::Double,
   493         Float = Type::Float
   494     };
   496   private:
   497     Which which_;
   499   public:
   500     RetType() : which_(Which(-1)) {}
   501     RetType(Which w) : which_(w) {}
   502     RetType(AsmJSCoercion coercion) {
   503         switch (coercion) {
   504           case AsmJS_ToInt32: which_ = Signed; break;
   505           case AsmJS_ToNumber: which_ = Double; break;
   506           case AsmJS_FRound: which_ = Float; break;
   507         }
   508     }
   509     Which which() const {
   510         return which_;
   511     }
   512     Type toType() const {
   513         return Type::Which(which_);
   514     }
   515     AsmJSModule::ReturnType toModuleReturnType() const {
   516         switch (which_) {
   517           case Void: return AsmJSModule::Return_Void;
   518           case Signed: return AsmJSModule::Return_Int32;
   519           case Float: // will be converted to a Double
   520           case Double: return AsmJSModule::Return_Double;
   521         }
   522         MOZ_ASSUME_UNREACHABLE("Unexpected return type");
   523     }
   524     MIRType toMIRType() const {
   525         switch (which_) {
   526           case Void: return MIRType_None;
   527           case Signed: return MIRType_Int32;
   528           case Double: return MIRType_Double;
   529           case Float: return MIRType_Float32;
   530         }
   531         MOZ_ASSUME_UNREACHABLE("Unexpected return type");
   532     }
   533     bool operator==(RetType rhs) const { return which_ == rhs.which_; }
   534     bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
   535 };
   537 namespace {
   539 // Represents the subset of Type that can be used as a variable or
   540 // argument's type. Note: AsmJSCoercion and VarType are kept separate to
   541 // make very clear the signed/int distinction: a coercion may explicitly sign
   542 // an *expression* but, when stored as a variable, this signedness information
   543 // is explicitly thrown away by the asm.js type system. E.g., in
   544 //
   545 //   function f(i) {
   546 //     i = i | 0;             (1)
   547 //     if (...)
   548 //         i = foo() >>> 0;
   549 //     else
   550 //         i = bar() | 0;
   551 //     return i | 0;          (2)
   552 //   }
   553 //
   554 // the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when
   555 // translated to an VarType, the result is a plain Int since, as shown, it
   556 // is legal to assign both Signed and Unsigned (or some other Int) values to
   557 // it. For (2), the AsmJSCoercion is also Signed but, when translated to an
   558 // RetType, the result is Signed since callers (asm.js and non-asm.js) can
   559 // rely on the return value being Signed.
   560 class VarType
   561 {
   562   public:
   563     enum Which {
   564         Int = Type::Int,
   565         Double = Type::Double,
   566         Float = Type::Float
   567     };
   569   private:
   570     Which which_;
   572   public:
   573     VarType()
   574       : which_(Which(-1)) {}
   575     VarType(Which w)
   576       : which_(w) {}
   577     VarType(AsmJSCoercion coercion) {
   578         switch (coercion) {
   579           case AsmJS_ToInt32: which_ = Int; break;
   580           case AsmJS_ToNumber: which_ = Double; break;
   581           case AsmJS_FRound: which_ = Float; break;
   582         }
   583     }
   584     Which which() const {
   585         return which_;
   586     }
   587     Type toType() const {
   588         return Type::Which(which_);
   589     }
   590     MIRType toMIRType() const {
   591         switch(which_) {
   592           case Int:     return MIRType_Int32;
   593           case Double:  return MIRType_Double;
   594           case Float:   return MIRType_Float32;
   595         }
   596         MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
   597     }
   598     AsmJSCoercion toCoercion() const {
   599         switch(which_) {
   600           case Int:     return AsmJS_ToInt32;
   601           case Double:  return AsmJS_ToNumber;
   602           case Float:   return AsmJS_FRound;
   603         }
   604         MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
   605     }
   606     static VarType FromCheckedType(Type type) {
   607         JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
   608         if (type.isMaybeDouble())
   609             return Double;
   610         else if (type.isFloatish())
   611             return Float;
   612         else
   613             return Int;
   614     }
   615     bool operator==(VarType rhs) const { return which_ == rhs.which_; }
   616     bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
   617 };
   619 } /* anonymous namespace */
   621 // Implements <: (subtype) operator when the rhs is an VarType
   622 static inline bool
   623 operator<=(Type lhs, VarType rhs)
   624 {
   625     switch (rhs.which()) {
   626       case VarType::Int:    return lhs.isInt();
   627       case VarType::Double: return lhs.isDouble();
   628       case VarType::Float:  return lhs.isFloat();
   629     }
   630     MOZ_ASSUME_UNREACHABLE("Unexpected rhs type");
   631 }
   633 /*****************************************************************************/
   635 static inline MIRType ToMIRType(MIRType t) { return t; }
   636 static inline MIRType ToMIRType(VarType t) { return t.toMIRType(); }
   638 namespace {
   640 template <class VecT>
   641 class ABIArgIter
   642 {
   643     ABIArgGenerator gen_;
   644     const VecT &types_;
   645     unsigned i_;
   647     void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); }
   649   public:
   650     ABIArgIter(const VecT &types) : types_(types), i_(0) { settle(); }
   651     void operator++(int) { JS_ASSERT(!done()); i_++; settle(); }
   652     bool done() const { return i_ == types_.length(); }
   654     ABIArg *operator->() { JS_ASSERT(!done()); return &gen_.current(); }
   655     ABIArg &operator*() { JS_ASSERT(!done()); return gen_.current(); }
   657     unsigned index() const { JS_ASSERT(!done()); return i_; }
   658     MIRType mirType() const { JS_ASSERT(!done()); return ToMIRType(types_[i_]); }
   659     uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); }
   660 };
   662 typedef js::Vector<MIRType, 8> MIRTypeVector;
   663 typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
   665 typedef js::Vector<VarType, 8, LifoAllocPolicy> VarTypeVector;
   666 typedef ABIArgIter<VarTypeVector> ABIArgTypeIter;
   668 class Signature
   669 {
   670     VarTypeVector argTypes_;
   671     RetType retType_;
   673   public:
   674     Signature(LifoAlloc &alloc)
   675       : argTypes_(alloc) {}
   676     Signature(LifoAlloc &alloc, RetType retType)
   677       : argTypes_(alloc), retType_(retType) {}
   678     Signature(VarTypeVector &&argTypes, RetType retType)
   679       : argTypes_(Move(argTypes)), retType_(Move(retType)) {}
   680     Signature(Signature &&rhs)
   681       : argTypes_(Move(rhs.argTypes_)), retType_(Move(rhs.retType_)) {}
   683     bool copy(const Signature &rhs) {
   684         if (!argTypes_.resize(rhs.argTypes_.length()))
   685             return false;
   686         for (unsigned i = 0; i < argTypes_.length(); i++)
   687             argTypes_[i] = rhs.argTypes_[i];
   688         retType_ = rhs.retType_;
   689         return true;
   690     }
   692     bool appendArg(VarType type) { return argTypes_.append(type); }
   693     VarType arg(unsigned i) const { return argTypes_[i]; }
   694     const VarTypeVector &args() const { return argTypes_; }
   695     VarTypeVector &&extractArgs() { return Move(argTypes_); }
   697     RetType retType() const { return retType_; }
   698 };
   700 } /* namespace anonymous */
   702 static
   703 bool operator==(const Signature &lhs, const Signature &rhs)
   704 {
   705     if (lhs.retType() != rhs.retType())
   706         return false;
   707     if (lhs.args().length() != rhs.args().length())
   708         return false;
   709     for (unsigned i = 0; i < lhs.args().length(); i++) {
   710         if (lhs.arg(i) != rhs.arg(i))
   711             return false;
   712     }
   713     return true;
   714 }
   716 static inline
   717 bool operator!=(const Signature &lhs, const Signature &rhs)
   718 {
   719     return !(lhs == rhs);
   720 }
   722 /*****************************************************************************/
   723 // Typed array utilities
   725 static Type
   726 TypedArrayLoadType(ArrayBufferView::ViewType viewType)
   727 {
   728     switch (viewType) {
   729       case ArrayBufferView::TYPE_INT8:
   730       case ArrayBufferView::TYPE_INT16:
   731       case ArrayBufferView::TYPE_INT32:
   732       case ArrayBufferView::TYPE_UINT8:
   733       case ArrayBufferView::TYPE_UINT16:
   734       case ArrayBufferView::TYPE_UINT32:
   735         return Type::Intish;
   736       case ArrayBufferView::TYPE_FLOAT32:
   737         return Type::MaybeFloat;
   738       case ArrayBufferView::TYPE_FLOAT64:
   739         return Type::MaybeDouble;
   740       default:;
   741     }
   742     MOZ_ASSUME_UNREACHABLE("Unexpected array type");
   743 }
   745 enum NeedsBoundsCheck {
   746     NO_BOUNDS_CHECK,
   747     NEEDS_BOUNDS_CHECK
   748 };
   750 namespace {
   752 typedef js::Vector<PropertyName*,1> LabelVector;
   753 typedef js::Vector<MBasicBlock*,8> BlockVector;
   755 // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
   756 // the course of an ModuleCompiler object's lifetime, many FunctionCompiler
   757 // objects will be created and destroyed in sequence, one for each function in
   758 // the module.
   759 //
   760 // *** asm.js FFI calls ***
   761 //
   762 // asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type
   763 // system does not place any constraints on the FFI call. In particular:
   764 //  - an FFI call's target is not known or speculated at module-compile time;
   765 //  - a single external function can be called with different signatures.
   766 //
   767 // If performance didn't matter, all FFI calls could simply box their arguments
   768 // and call js::Invoke. However, we'd like to be able to specialize FFI calls
   769 // to be more efficient in several cases:
   770 //
   771 //  - for calls to JS functions which have been jitted, we'd like to call
   772 //    directly into JIT code without going through C++.
   773 //
   774 //  - for calls to certain builtins, we'd like to be call directly into the C++
   775 //    code for the builtin without going through the general call path.
   776 //
   777 // All of this requires dynamic specialization techniques which must happen
   778 // after module compilation. To support this, at module-compilation time, each
   779 // FFI call generates a call signature according to the system ABI, as if the
   780 // callee was a C++ function taking/returning the same types as the caller was
   781 // passing/expecting. The callee is loaded from a fixed offset in the global
   782 // data array which allows the callee to change at runtime. Initially, the
   783 // callee is stub which boxes its arguments and calls js::Invoke.
   784 //
   785 // To do this, we need to generate a callee stub for each pairing of FFI callee
   786 // and signature. We call this pairing an "exit". For example, this code has
   787 // two external functions and three exits:
   788 //
   789 //  function f(global, imports) {
   790 //    "use asm";
   791 //    var foo = imports.foo;
   792 //    var bar = imports.bar;
   793 //    function g() {
   794 //      foo(1);      // Exit #1: (int) -> void
   795 //      foo(1.5);    // Exit #2: (double) -> void
   796 //      bar(1)|0;    // Exit #3: (int) -> int
   797 //      bar(2)|0;    // Exit #3: (int) -> int
   798 //    }
   799 //  }
   800 //
   801 // The ModuleCompiler maintains a hash table (ExitMap) which allows a call site
   802 // to add a new exit or reuse an existing one. The key is an ExitDescriptor
   803 // (which holds the exit pairing) and the value is an index into the
   804 // Vector<Exit> stored in the AsmJSModule.
   805 //
   806 // Rooting note: ModuleCompiler is a stack class that contains unrooted
   807 // PropertyName (JSAtom) pointers.  This is safe because it cannot be
   808 // constructed without a TokenStream reference.  TokenStream is itself a stack
   809 // class that cannot be constructed without an AutoKeepAtoms being live on the
   810 // stack, which prevents collection of atoms.
   811 //
   812 // ModuleCompiler is marked as rooted in the rooting analysis.  Don't add
   813 // non-JSAtom pointers, or this will break!
   814 class MOZ_STACK_CLASS ModuleCompiler
   815 {
   816   public:
   817     class Func
   818     {
   819         PropertyName *name_;
   820         bool defined_;
   821         uint32_t srcOffset_;
   822         uint32_t endOffset_;
   823         Signature sig_;
   824         Label *code_;
   825         unsigned compileTime_;
   827       public:
   828         Func(PropertyName *name, Signature &&sig, Label *code)
   829           : name_(name), defined_(false), srcOffset_(0), endOffset_(0), sig_(Move(sig)),
   830             code_(code), compileTime_(0)
   831         {}
   833         PropertyName *name() const { return name_; }
   835         bool defined() const { return defined_; }
   836         void finish(uint32_t start, uint32_t end) {
   837             JS_ASSERT(!defined_);
   838             defined_ = true;
   839             srcOffset_ = start;
   840             endOffset_ = end;
   841         }
   843         uint32_t srcOffset() const { JS_ASSERT(defined_); return srcOffset_; }
   844         uint32_t endOffset() const { JS_ASSERT(defined_); return endOffset_; }
   845         Signature &sig() { return sig_; }
   846         const Signature &sig() const { return sig_; }
   847         Label *code() const { return code_; }
   848         unsigned compileTime() const { return compileTime_; }
   849         void accumulateCompileTime(unsigned ms) { compileTime_ += ms; }
   850     };
   852     class Global
   853     {
   854       public:
   855         enum Which {
   856             Variable,
   857             ConstantLiteral,
   858             ConstantImport,
   859             Function,
   860             FuncPtrTable,
   861             FFI,
   862             ArrayView,
   863             MathBuiltinFunction
   864         };
   866       private:
   867         Which which_;
   868         union {
   869             struct {
   870                 VarType::Which type_;
   871                 uint32_t index_;
   872                 Value literalValue_;
   873             } varOrConst;
   874             uint32_t funcIndex_;
   875             uint32_t funcPtrTableIndex_;
   876             uint32_t ffiIndex_;
   877             ArrayBufferView::ViewType viewType_;
   878             AsmJSMathBuiltinFunction mathBuiltinFunc_;
   879         } u;
   881         friend class ModuleCompiler;
   882         friend class js::LifoAlloc;
   884         Global(Which which) : which_(which) {}
   886       public:
   887         Which which() const {
   888             return which_;
   889         }
   890         VarType varOrConstType() const {
   891             JS_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
   892             return VarType(u.varOrConst.type_);
   893         }
   894         uint32_t varOrConstIndex() const {
   895             JS_ASSERT(which_ == Variable || which_ == ConstantImport);
   896             return u.varOrConst.index_;
   897         }
   898         bool isConst() const {
   899             return which_ == ConstantLiteral || which_ == ConstantImport;
   900         }
   901         Value constLiteralValue() const {
   902             JS_ASSERT(which_ == ConstantLiteral);
   903             return u.varOrConst.literalValue_;
   904         }
   905         uint32_t funcIndex() const {
   906             JS_ASSERT(which_ == Function);
   907             return u.funcIndex_;
   908         }
   909         uint32_t funcPtrTableIndex() const {
   910             JS_ASSERT(which_ == FuncPtrTable);
   911             return u.funcPtrTableIndex_;
   912         }
   913         unsigned ffiIndex() const {
   914             JS_ASSERT(which_ == FFI);
   915             return u.ffiIndex_;
   916         }
   917         ArrayBufferView::ViewType viewType() const {
   918             JS_ASSERT(which_ == ArrayView);
   919             return u.viewType_;
   920         }
   921         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
   922             JS_ASSERT(which_ == MathBuiltinFunction);
   923             return u.mathBuiltinFunc_;
   924         }
   925     };
   927     typedef js::Vector<const Func*> FuncPtrVector;
   929     class FuncPtrTable
   930     {
   931         Signature sig_;
   932         uint32_t mask_;
   933         uint32_t globalDataOffset_;
   934         FuncPtrVector elems_;
   936       public:
   937         FuncPtrTable(ExclusiveContext *cx, Signature &&sig, uint32_t mask, uint32_t gdo)
   938           : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), elems_(cx)
   939         {}
   941         FuncPtrTable(FuncPtrTable &&rhs)
   942           : sig_(Move(rhs.sig_)), mask_(rhs.mask_), globalDataOffset_(rhs.globalDataOffset_),
   943             elems_(Move(rhs.elems_))
   944         {}
   946         Signature &sig() { return sig_; }
   947         const Signature &sig() const { return sig_; }
   948         unsigned mask() const { return mask_; }
   949         unsigned globalDataOffset() const { return globalDataOffset_; }
   951         bool initialized() const { return !elems_.empty(); }
   952         void initElems(FuncPtrVector &&elems) { elems_ = Move(elems); JS_ASSERT(initialized()); }
   953         unsigned numElems() const { JS_ASSERT(initialized()); return elems_.length(); }
   954         const Func &elem(unsigned i) const { return *elems_[i]; }
   955     };
   957     typedef js::Vector<FuncPtrTable> FuncPtrTableVector;
   959     class ExitDescriptor
   960     {
   961         PropertyName *name_;
   962         Signature sig_;
   964       public:
   965         ExitDescriptor(PropertyName *name, Signature &&sig)
   966           : name_(name), sig_(Move(sig)) {}
   967         ExitDescriptor(ExitDescriptor &&rhs)
   968           : name_(rhs.name_), sig_(Move(rhs.sig_))
   969         {}
   970         const Signature &sig() const {
   971             return sig_;
   972         }
   974         // ExitDescriptor is a HashPolicy:
   975         typedef ExitDescriptor Lookup;
   976         static HashNumber hash(const ExitDescriptor &d) {
   977             HashNumber hn = HashGeneric(d.name_, d.sig_.retType().which());
   978             const VarTypeVector &args = d.sig_.args();
   979             for (unsigned i = 0; i < args.length(); i++)
   980                 hn = AddToHash(hn, args[i].which());
   981             return hn;
   982         }
   983         static bool match(const ExitDescriptor &lhs, const ExitDescriptor &rhs) {
   984             return lhs.name_ == rhs.name_ && lhs.sig_ == rhs.sig_;
   985         }
   986     };
   988     typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
   990     struct MathBuiltin
   991     {
   992         enum Kind { Function, Constant };
   993         Kind kind;
   995         union {
   996             double cst;
   997             AsmJSMathBuiltinFunction func;
   998         } u;
  1000         MathBuiltin() : kind(Kind(-1)) {}
  1001         MathBuiltin(double cst) : kind(Constant) {
  1002             u.cst = cst;
  1004         MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
  1005             u.func = func;
  1007     };
  1009   private:
  1010     struct SlowFunction
  1012         PropertyName *name;
  1013         unsigned ms;
  1014         unsigned line;
  1015         unsigned column;
  1016     };
  1018     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
  1019     typedef HashMap<PropertyName*, Global*> GlobalMap;
  1020     typedef js::Vector<Func*> FuncVector;
  1021     typedef js::Vector<AsmJSGlobalAccess> GlobalAccessVector;
  1022     typedef js::Vector<SlowFunction> SlowFunctionVector;
  1024     ExclusiveContext *             cx_;
  1025     AsmJSParser &                  parser_;
  1027     MacroAssembler                 masm_;
  1029     ScopedJSDeletePtr<AsmJSModule> module_;
  1030     LifoAlloc                      moduleLifo_;
  1031     ParseNode *                    moduleFunctionNode_;
  1032     PropertyName *                 moduleFunctionName_;
  1034     GlobalMap                      globals_;
  1035     FuncVector                     functions_;
  1036     FuncPtrTableVector             funcPtrTables_;
  1037     ExitMap                        exits_;
  1038     MathNameMap                    standardLibraryMathNames_;
  1039     Label                          stackOverflowLabel_;
  1040     Label                          interruptLabel_;
  1042     char *                         errorString_;
  1043     uint32_t                       errorOffset_;
  1044     bool                           errorOverRecursed_;
  1046     int64_t                        usecBefore_;
  1047     SlowFunctionVector             slowFunctions_;
  1049     DebugOnly<bool>                finishedFunctionBodies_;
  1051     bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) {
  1052         JSAtom *atom = Atomize(cx_, name, strlen(name));
  1053         if (!atom)
  1054             return false;
  1055         MathBuiltin builtin(func);
  1056         return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
  1058     bool addStandardLibraryMathName(const char *name, double cst) {
  1059         JSAtom *atom = Atomize(cx_, name, strlen(name));
  1060         if (!atom)
  1061             return false;
  1062         MathBuiltin builtin(cst);
  1063         return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
  1066   public:
  1067     ModuleCompiler(ExclusiveContext *cx, AsmJSParser &parser)
  1068       : cx_(cx),
  1069         parser_(parser),
  1070         masm_(MacroAssembler::AsmJSToken()),
  1071         moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
  1072         moduleFunctionNode_(parser.pc->maybeFunction),
  1073         moduleFunctionName_(nullptr),
  1074         globals_(cx),
  1075         functions_(cx),
  1076         funcPtrTables_(cx),
  1077         exits_(cx),
  1078         standardLibraryMathNames_(cx),
  1079         errorString_(nullptr),
  1080         errorOffset_(UINT32_MAX),
  1081         errorOverRecursed_(false),
  1082         usecBefore_(PRMJ_Now()),
  1083         slowFunctions_(cx),
  1084         finishedFunctionBodies_(false)
  1086         JS_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
  1089     ~ModuleCompiler() {
  1090         if (errorString_) {
  1091             JS_ASSERT(errorOffset_ != UINT32_MAX);
  1092             tokenStream().reportAsmJSError(errorOffset_,
  1093                                            JSMSG_USE_ASM_TYPE_FAIL,
  1094                                            errorString_);
  1095             js_free(errorString_);
  1097         if (errorOverRecursed_)
  1098             js_ReportOverRecursed(cx_);
  1100         // Avoid spurious Label assertions on compilation failure.
  1101         if (!stackOverflowLabel_.bound())
  1102             stackOverflowLabel_.bind(0);
  1103         if (!interruptLabel_.bound())
  1104             interruptLabel_.bind(0);
  1107     bool init() {
  1108         if (!globals_.init() || !exits_.init())
  1109             return false;
  1111         if (!standardLibraryMathNames_.init() ||
  1112             !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
  1113             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
  1114             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
  1115             !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
  1116             !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
  1117             !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) ||
  1118             !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
  1119             !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
  1120             !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
  1121             !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
  1122             !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
  1123             !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
  1124             !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
  1125             !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
  1126             !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
  1127             !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
  1128             !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) ||
  1129             !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) ||
  1130             !addStandardLibraryMathName("E", M_E) ||
  1131             !addStandardLibraryMathName("LN10", M_LN10) ||
  1132             !addStandardLibraryMathName("LN2", M_LN2) ||
  1133             !addStandardLibraryMathName("LOG2E", M_LOG2E) ||
  1134             !addStandardLibraryMathName("LOG10E", M_LOG10E) ||
  1135             !addStandardLibraryMathName("PI", M_PI) ||
  1136             !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
  1137             !addStandardLibraryMathName("SQRT2", M_SQRT2))
  1139             return false;
  1142         uint32_t funcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
  1143         uint32_t offsetToEndOfUseAsm = tokenStream().currentToken().pos.end;
  1145         // "use strict" should be added to the source if we are in an implicit
  1146         // strict context, see also comment above addUseStrict in
  1147         // js::FunctionToString.
  1148         bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
  1150         module_ = cx_->new_<AsmJSModule>(parser_.ss, funcStart, offsetToEndOfUseAsm, strict);
  1151         if (!module_)
  1152             return false;
  1154         return true;
  1157     bool failOffset(uint32_t offset, const char *str) {
  1158         JS_ASSERT(!errorString_);
  1159         JS_ASSERT(errorOffset_ == UINT32_MAX);
  1160         JS_ASSERT(str);
  1161         errorOffset_ = offset;
  1162         errorString_ = js_strdup(cx_, str);
  1163         return false;
  1166     bool fail(ParseNode *pn, const char *str) {
  1167         if (pn)
  1168             return failOffset(pn->pn_pos.begin, str);
  1170         // The exact rooting static analysis does not perform dataflow analysis, so it believes
  1171         // that unrooted things on the stack during compilation may still be accessed after this.
  1172         // Since pn is typically only null under OOM, this suppression simply forces any GC to be
  1173         // delayed until the compilation is off the stack and more memory can be freed.
  1174         gc::AutoSuppressGC nogc(cx_);
  1175         return failOffset(tokenStream().peekTokenPos().begin, str);
  1178     bool failfVA(ParseNode *pn, const char *fmt, va_list ap) {
  1179         JS_ASSERT(!errorString_);
  1180         JS_ASSERT(errorOffset_ == UINT32_MAX);
  1181         JS_ASSERT(fmt);
  1182         errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end;
  1183         errorString_ = JS_vsmprintf(fmt, ap);
  1184         return false;
  1187     bool failf(ParseNode *pn, const char *fmt, ...) {
  1188         va_list ap;
  1189         va_start(ap, fmt);
  1190         failfVA(pn, fmt, ap);
  1191         va_end(ap);
  1192         return false;
  1195     bool failName(ParseNode *pn, const char *fmt, PropertyName *name) {
  1196         // This function is invoked without the caller properly rooting its locals.
  1197         gc::AutoSuppressGC suppress(cx_);
  1198         JSAutoByteString bytes;
  1199         if (AtomToPrintableString(cx_, name, &bytes))
  1200             failf(pn, fmt, bytes.ptr());
  1201         return false;
  1204     bool failOverRecursed() {
  1205         errorOverRecursed_ = true;
  1206         return false;
  1209     static const unsigned SLOW_FUNCTION_THRESHOLD_MS = 250;
  1211     bool maybeReportCompileTime(const Func &func) {
  1212         if (func.compileTime() < SLOW_FUNCTION_THRESHOLD_MS)
  1213             return true;
  1214         SlowFunction sf;
  1215         sf.name = func.name();
  1216         sf.ms = func.compileTime();
  1217         tokenStream().srcCoords.lineNumAndColumnIndex(func.srcOffset(), &sf.line, &sf.column);
  1218         return slowFunctions_.append(sf);
  1221     /*************************************************** Read-only interface */
  1223     ExclusiveContext *cx() const { return cx_; }
  1224     AsmJSParser &parser() const { return parser_; }
  1225     TokenStream &tokenStream() const { return parser_.tokenStream; }
  1226     MacroAssembler &masm() { return masm_; }
  1227     Label &stackOverflowLabel() { return stackOverflowLabel_; }
  1228     Label &interruptLabel() { return interruptLabel_; }
  1229     bool hasError() const { return errorString_ != nullptr; }
  1230     const AsmJSModule &module() const { return *module_.get(); }
  1231     uint32_t moduleStart() const { return module_->funcStart(); }
  1233     ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
  1234     PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
  1236     const Global *lookupGlobal(PropertyName *name) const {
  1237         if (GlobalMap::Ptr p = globals_.lookup(name))
  1238             return p->value();
  1239         return nullptr;
  1241     Func *lookupFunction(PropertyName *name) {
  1242         if (GlobalMap::Ptr p = globals_.lookup(name)) {
  1243             Global *value = p->value();
  1244             if (value->which() == Global::Function)
  1245                 return functions_[value->funcIndex()];
  1247         return nullptr;
  1249     unsigned numFunctions() const {
  1250         return functions_.length();
  1252     Func &function(unsigned i) {
  1253         return *functions_[i];
  1255     unsigned numFuncPtrTables() const {
  1256         return funcPtrTables_.length();
  1258     FuncPtrTable &funcPtrTable(unsigned i) {
  1259         return funcPtrTables_[i];
  1261     bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const {
  1262         if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
  1263             *mathBuiltin = p->value();
  1264             return true;
  1266         return false;
  1268     ExitMap::Range allExits() const {
  1269         return exits_.all();
  1272     /***************************************************** Mutable interface */
  1274     void initModuleFunctionName(PropertyName *name) { moduleFunctionName_ = name; }
  1276     void initGlobalArgumentName(PropertyName *n) { module_->initGlobalArgumentName(n); }
  1277     void initImportArgumentName(PropertyName *n) { module_->initImportArgumentName(n); }
  1278     void initBufferArgumentName(PropertyName *n) { module_->initBufferArgumentName(n); }
  1280     bool addGlobalVarInit(PropertyName *varName, VarType type, const Value &v, bool isConst) {
  1281         uint32_t index;
  1282         if (!module_->addGlobalVarInit(v, type.toCoercion(), &index))
  1283             return false;
  1285         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
  1286         Global *global = moduleLifo_.new_<Global>(which);
  1287         if (!global)
  1288             return false;
  1289         global->u.varOrConst.index_ = index;
  1290         global->u.varOrConst.type_ = type.which();
  1291         if (isConst)
  1292             global->u.varOrConst.literalValue_ = v;
  1294         return globals_.putNew(varName, global);
  1296     bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion,
  1297                             bool isConst) {
  1298         uint32_t index;
  1299         if (!module_->addGlobalVarImport(fieldName, coercion, &index))
  1300             return false;
  1302         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
  1303         Global *global = moduleLifo_.new_<Global>(which);
  1304         if (!global)
  1305             return false;
  1306         global->u.varOrConst.index_ = index;
  1307         global->u.varOrConst.type_ = VarType(coercion).which();
  1309         return globals_.putNew(varName, global);
  1311     bool addFunction(PropertyName *name, Signature &&sig, Func **func) {
  1312         JS_ASSERT(!finishedFunctionBodies_);
  1313         Global *global = moduleLifo_.new_<Global>(Global::Function);
  1314         if (!global)
  1315             return false;
  1316         global->u.funcIndex_ = functions_.length();
  1317         if (!globals_.putNew(name, global))
  1318             return false;
  1319         Label *code = moduleLifo_.new_<Label>();
  1320         if (!code)
  1321             return false;
  1322         *func = moduleLifo_.new_<Func>(name, Move(sig), code);
  1323         if (!*func)
  1324             return false;
  1325         return functions_.append(*func);
  1327     bool addFuncPtrTable(PropertyName *name, Signature &&sig, uint32_t mask, FuncPtrTable **table) {
  1328         Global *global = moduleLifo_.new_<Global>(Global::FuncPtrTable);
  1329         if (!global)
  1330             return false;
  1331         global->u.funcPtrTableIndex_ = funcPtrTables_.length();
  1332         if (!globals_.putNew(name, global))
  1333             return false;
  1334         uint32_t globalDataOffset;
  1335         if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset))
  1336             return false;
  1337         FuncPtrTable tmpTable(cx_, Move(sig), mask, globalDataOffset);
  1338         if (!funcPtrTables_.append(Move(tmpTable)))
  1339             return false;
  1340         *table = &funcPtrTables_.back();
  1341         return true;
  1343     bool addFFI(PropertyName *varName, PropertyName *field) {
  1344         Global *global = moduleLifo_.new_<Global>(Global::FFI);
  1345         if (!global)
  1346             return false;
  1347         uint32_t index;
  1348         if (!module_->addFFI(field, &index))
  1349             return false;
  1350         global->u.ffiIndex_ = index;
  1351         return globals_.putNew(varName, global);
  1353     bool addArrayView(PropertyName *varName, ArrayBufferView::ViewType vt, PropertyName *fieldName) {
  1354         Global *global = moduleLifo_.new_<Global>(Global::ArrayView);
  1355         if (!global)
  1356             return false;
  1357         if (!module_->addArrayView(vt, fieldName))
  1358             return false;
  1359         global->u.viewType_ = vt;
  1360         return globals_.putNew(varName, global);
  1362     bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) {
  1363         if (!module_->addMathBuiltinFunction(func, fieldName))
  1364             return false;
  1365         Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
  1366         if (!global)
  1367             return false;
  1368         global->u.mathBuiltinFunc_ = func;
  1369         return globals_.putNew(varName, global);
  1371   private:
  1372     bool addGlobalDoubleConstant(PropertyName *varName, double constant) {
  1373         Global *global = moduleLifo_.new_<Global>(Global::ConstantLiteral);
  1374         if (!global)
  1375             return false;
  1376         global->u.varOrConst.literalValue_ = DoubleValue(constant);
  1377         global->u.varOrConst.type_ = VarType::Double;
  1378         return globals_.putNew(varName, global);
  1380   public:
  1381     bool addMathBuiltinConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
  1382         if (!module_->addMathBuiltinConstant(constant, fieldName))
  1383             return false;
  1384         return addGlobalDoubleConstant(varName, constant);
  1386     bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
  1387         if (!module_->addGlobalConstant(constant, fieldName))
  1388             return false;
  1389         return addGlobalDoubleConstant(varName, constant);
  1391     bool addExportedFunction(const Func *func, PropertyName *maybeFieldName) {
  1392         AsmJSModule::ArgCoercionVector argCoercions;
  1393         const VarTypeVector &args = func->sig().args();
  1394         if (!argCoercions.resize(args.length()))
  1395             return false;
  1396         for (unsigned i = 0; i < args.length(); i++)
  1397             argCoercions[i] = args[i].toCoercion();
  1398         AsmJSModule::ReturnType retType = func->sig().retType().toModuleReturnType();
  1399         return module_->addExportedFunction(func->name(), func->srcOffset(), func->endOffset(),
  1400                                             maybeFieldName, Move(argCoercions), retType);
  1402     bool addExit(unsigned ffiIndex, PropertyName *name, Signature &&sig, unsigned *exitIndex) {
  1403         ExitDescriptor exitDescriptor(name, Move(sig));
  1404         ExitMap::AddPtr p = exits_.lookupForAdd(exitDescriptor);
  1405         if (p) {
  1406             *exitIndex = p->value();
  1407             return true;
  1409         if (!module_->addExit(ffiIndex, exitIndex))
  1410             return false;
  1411         return exits_.add(p, Move(exitDescriptor), *exitIndex);
  1413     bool addFunctionName(PropertyName *name, uint32_t *index) {
  1414         return module_->addFunctionName(name, index);
  1417     // Note a constraint on the minimum size of the heap.  The heap size is
  1418     // constrained when linking to be at least the maximum of all such constraints.
  1419     void requireHeapLengthToBeAtLeast(uint32_t len) {
  1420         module_->requireHeapLengthToBeAtLeast(len);
  1422     uint32_t minHeapLength() const {
  1423         return module_->minHeapLength();
  1425     LifoAlloc &lifo() {
  1426         return moduleLifo_;
  1429 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
  1430     bool trackProfiledFunction(const Func &func, unsigned endCodeOffset) {
  1431         unsigned lineno = 0U, columnIndex = 0U;
  1432         tokenStream().srcCoords.lineNumAndColumnIndex(func.srcOffset(), &lineno, &columnIndex);
  1433         unsigned startCodeOffset = func.code()->offset();
  1434         return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset,
  1435                                               lineno, columnIndex);
  1437 #endif
  1439 #ifdef JS_ION_PERF
  1440     bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) {
  1441         unsigned startCodeOffset = func.code()->offset();
  1442         perfSpewer.noteBlocksOffsets();
  1443         unsigned endInlineCodeOffset = perfSpewer.endInlineCode.offset();
  1444         return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endInlineCodeOffset,
  1445                                                 endCodeOffset, perfSpewer.basicBlocks());
  1447 #endif
  1449     void finishFunctionBodies() {
  1450         JS_ASSERT(!finishedFunctionBodies_);
  1451         masm_.align(AsmJSPageSize);
  1452         finishedFunctionBodies_ = true;
  1453         module_->initFunctionBytes(masm_.currentOffset());
  1456     void setInterpExitOffset(unsigned exitIndex) {
  1457         module_->exit(exitIndex).initInterpOffset(masm_.currentOffset());
  1459     void setIonExitOffset(unsigned exitIndex) {
  1460         module_->exit(exitIndex).initIonOffset(masm_.currentOffset());
  1462     void setEntryOffset(unsigned exportIndex) {
  1463         module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset());
  1466     void buildCompilationTimeReport(bool storedInCache, ScopedJSFreePtr<char> *out) {
  1467         ScopedJSFreePtr<char> slowFuns;
  1468 #ifndef JS_MORE_DETERMINISTIC
  1469         int64_t usecAfter = PRMJ_Now();
  1470         int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
  1471         if (!slowFunctions_.empty()) {
  1472             slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
  1473             if (!slowFuns)
  1474                 return;
  1475             for (unsigned i = 0; i < slowFunctions_.length(); i++) {
  1476                 SlowFunction &func = slowFunctions_[i];
  1477                 JSAutoByteString name;
  1478                 if (!AtomToPrintableString(cx_, func.name, &name))
  1479                     return;
  1480                 slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
  1481                                            name.ptr(), func.line, func.column, func.ms,
  1482                                            i+1 < slowFunctions_.length() ? ", " : ""));
  1483                 if (!slowFuns)
  1484                     return;
  1487         out->reset(JS_smprintf("total compilation time %dms; %s%s",
  1488                                msTotal,
  1489                                storedInCache ? "stored in cache" : "not stored in cache",
  1490                                slowFuns ? slowFuns.get() : ""));
  1491 #endif
  1494     bool finish(ScopedJSDeletePtr<AsmJSModule> *module)
  1496         module_->initFuncEnd(tokenStream().currentToken().pos.end,
  1497                              tokenStream().peekTokenPos().end);
  1498         masm_.finish();
  1499         if (masm_.oom())
  1500             return false;
  1502         module_->assignCallSites(masm_.extractCallSites());
  1503         module_->assignHeapAccesses(masm_.extractAsmJSHeapAccesses());
  1505 #if defined(JS_CODEGEN_ARM)
  1506         // Now that compilation has finished, we need to update offsets to
  1507         // reflect actual offsets (an ARM distinction).
  1508         for (unsigned i = 0; i < module_->numHeapAccesses(); i++) {
  1509             AsmJSHeapAccess &a = module_->heapAccess(i);
  1510             a.setOffset(masm_.actualOffset(a.offset()));
  1512         for (unsigned i = 0; i < module_->numExportedFunctions(); i++)
  1513             module_->exportedFunction(i).updateCodeOffset(masm_);
  1514         for (unsigned i = 0; i < module_->numExits(); i++)
  1515             module_->exit(i).updateOffsets(masm_);
  1516         for (unsigned i = 0; i < module_->numCallSites(); i++) {
  1517             CallSite &c = module_->callSite(i);
  1518             c.setReturnAddressOffset(masm_.actualOffset(c.returnAddressOffset()));
  1520 #endif
  1522         // The returned memory is owned by module_.
  1523         if (!module_->allocateAndCopyCode(cx_, masm_))
  1524             return false;
  1526         module_->updateFunctionBytes(masm_);
  1527         // c.f. JitCode::copyFrom
  1528         JS_ASSERT(masm_.jumpRelocationTableBytes() == 0);
  1529         JS_ASSERT(masm_.dataRelocationTableBytes() == 0);
  1530         JS_ASSERT(masm_.preBarrierTableBytes() == 0);
  1531         JS_ASSERT(!masm_.hasEnteredExitFrame());
  1533 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
  1534         // Fix up the code offsets.
  1535         for (unsigned i = 0; i < module_->numProfiledFunctions(); i++) {
  1536             AsmJSModule::ProfiledFunction &func = module_->profiledFunction(i);
  1537             func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset);
  1538             func.pod.endCodeOffset = masm_.actualOffset(func.pod.endCodeOffset);
  1540 #endif
  1542 #ifdef JS_ION_PERF
  1543         for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) {
  1544             AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i);
  1545             func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset);
  1546             func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset);
  1547             func.pod.endCodeOffset = masm_.actualOffset(func.pod.endCodeOffset);
  1548             BasicBlocksVector &basicBlocks = func.blocks;
  1549             for (uint32_t i = 0; i < basicBlocks.length(); i++) {
  1550                 Record &r = basicBlocks[i];
  1551                 r.startOffset = masm_.actualOffset(r.startOffset);
  1552                 r.endOffset = masm_.actualOffset(r.endOffset);
  1555 #endif
  1557         module_->setInterruptOffset(masm_.actualOffset(interruptLabel_.offset()));
  1559         // CodeLabels produced during codegen
  1560         for (size_t i = 0; i < masm_.numCodeLabels(); i++) {
  1561             CodeLabel src = masm_.codeLabel(i);
  1562             int32_t labelOffset = src.dest()->offset();
  1563             int32_t targetOffset = masm_.actualOffset(src.src()->offset());
  1564             // The patched uses of a label embed a linked list where the
  1565             // to-be-patched immediate is the offset of the next to-be-patched
  1566             // instruction.
  1567             while (labelOffset != LabelBase::INVALID_OFFSET) {
  1568                 size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset);
  1569                 AsmJSModule::RelativeLink link;
  1570                 link.patchAtOffset = patchAtOffset;
  1571                 link.targetOffset = targetOffset;
  1572                 if (!module_->addRelativeLink(link))
  1573                     return false;
  1574                 labelOffset = *(uintptr_t *)(module_->codeBase() + patchAtOffset);
  1578         // Function-pointer-table entries
  1579         for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) {
  1580             FuncPtrTable &table = funcPtrTables_[tableIndex];
  1581             unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
  1582             for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
  1583                 AsmJSModule::RelativeLink link;
  1584                 link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
  1585                 link.targetOffset = masm_.actualOffset(table.elem(elemIndex).code()->offset());
  1586                 if (!module_->addRelativeLink(link))
  1587                     return false;
  1591 #if defined(JS_CODEGEN_X86)
  1592         // Global data accesses in x86 need to be patched with the absolute
  1593         // address of the global. Globals are allocated sequentially after the
  1594         // code section so we can just use an RelativeLink.
  1595         for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
  1596             AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
  1597             AsmJSModule::RelativeLink link;
  1598             link.patchAtOffset = masm_.labelOffsetToPatchOffset(a.patchAt.offset());
  1599             link.targetOffset = module_->offsetOfGlobalData() + a.globalDataOffset;
  1600             if (!module_->addRelativeLink(link))
  1601                 return false;
  1603 #endif
  1605 #if defined(JS_CODEGEN_X64)
  1606         // Global data accesses on x64 use rip-relative addressing and thus do
  1607         // not need patching after deserialization.
  1608         uint8_t *code = module_->codeBase();
  1609         for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
  1610             AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
  1611             masm_.patchAsmJSGlobalAccess(a.patchAt, code, module_->globalData(), a.globalDataOffset);
  1613 #endif
  1615         // Absolute links
  1616         for (size_t i = 0; i < masm_.numAsmJSAbsoluteLinks(); i++) {
  1617             AsmJSAbsoluteLink src = masm_.asmJSAbsoluteLink(i);
  1618             AsmJSModule::AbsoluteLink link;
  1619             link.patchAt = masm_.actualOffset(src.patchAt.offset());
  1620             link.target = src.target;
  1621             if (!module_->addAbsoluteLink(link))
  1622                 return false;
  1625         *module = module_.forget();
  1626         return true;
  1628 };
  1630 } /* anonymous namespace */
  1632 /*****************************************************************************/
  1633 // Numeric literal utilities
  1635 namespace {
  1637 // Represents the type and value of an asm.js numeric literal.
  1638 //
  1639 // A literal is a double iff the literal contains an exponent or decimal point
  1640 // (even if the fractional part is 0). Otherwise, integers may be classified:
  1641 //  fixnum: [0, 2^31)
  1642 //  negative int: [-2^31, 0)
  1643 //  big unsigned: [2^31, 2^32)
  1644 //  out of range: otherwise
  1645 // Lastly, a literal may be a float literal which is any double or integer
  1646 // literal coerced with Math.fround.
  1647 class NumLit
  1649   public:
  1650     enum Which {
  1651         Fixnum = Type::Fixnum,
  1652         NegativeInt = Type::Signed,
  1653         BigUnsigned = Type::Unsigned,
  1654         Double = Type::Double,
  1655         Float = Type::Float,
  1656         OutOfRangeInt = -1
  1657     };
  1659   private:
  1660     Which which_;
  1661     Value v_;
  1663   public:
  1664     NumLit() {}
  1666     NumLit(Which w, Value v)
  1667       : which_(w), v_(v)
  1668     {}
  1670     Which which() const {
  1671         return which_;
  1674     int32_t toInt32() const {
  1675         JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
  1676         return v_.toInt32();
  1679     double toDouble() const {
  1680         JS_ASSERT(which_ == Double);
  1681         return v_.toDouble();
  1684     float toFloat() const {
  1685         JS_ASSERT(which_ == Float);
  1686         return float(v_.toDouble());
  1689     Value value() const {
  1690         JS_ASSERT(which_ != OutOfRangeInt);
  1691         return v_;
  1694     bool hasType() const {
  1695         return which_ != OutOfRangeInt;
  1698     Type type() const {
  1699         JS_ASSERT(hasType());
  1700         return Type::Which(which_);
  1703     VarType varType() const {
  1704         JS_ASSERT(hasType());
  1705         switch (which_) {
  1706           case NumLit::Fixnum:
  1707           case NumLit::NegativeInt:
  1708           case NumLit::BigUnsigned:
  1709             return VarType::Int;
  1710           case NumLit::Double:
  1711             return VarType::Double;
  1712           case NumLit::Float:
  1713             return VarType::Float;
  1714           case NumLit::OutOfRangeInt:;
  1716         MOZ_ASSUME_UNREACHABLE("Unexpected NumLit type");
  1718 };
  1720 } /* anonymous namespace */
  1722 static bool
  1723 IsNumericNonFloatLiteral(ParseNode *pn)
  1725     // Note: '-' is never rolled into the number; numbers are always positive
  1726     // and negations must be applied manually.
  1727     return pn->isKind(PNK_NUMBER) ||
  1728            (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
  1731 static bool
  1732 IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr)
  1734     if (!pn->isKind(PNK_CALL))
  1735         return false;
  1737     ParseNode *callee = CallCallee(pn);
  1738     if (!callee->isKind(PNK_NAME))
  1739         return false;
  1741     const ModuleCompiler::Global *global = m.lookupGlobal(callee->name());
  1742     if (!global ||
  1743         global->which() != ModuleCompiler::Global::MathBuiltinFunction ||
  1744         global->mathBuiltinFunction() != AsmJSMathBuiltin_fround)
  1746         return false;
  1749     if (CallArgListLength(pn) != 1)
  1750         return false;
  1752     if (coercedExpr)
  1753         *coercedExpr = CallArgList(pn);
  1755     return true;
  1758 static bool
  1759 IsNumericFloatLiteral(ModuleCompiler &m, ParseNode *pn)
  1761     ParseNode *coercedExpr;
  1762     if (!IsFloatCoercion(m, pn, &coercedExpr))
  1763         return false;
  1765     return IsNumericNonFloatLiteral(coercedExpr);
  1768 static bool
  1769 IsNumericLiteral(ModuleCompiler &m, ParseNode *pn)
  1771     return IsNumericNonFloatLiteral(pn) ||
  1772            IsNumericFloatLiteral(m, pn);
  1775 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
  1776 // productions) for the unary - and literal 42). However, the asm.js spec
  1777 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
  1778 // so fold the two potential parse nodes into a single double value.
  1779 static double
  1780 ExtractNumericNonFloatValue(ParseNode **pn)
  1782     JS_ASSERT(IsNumericNonFloatLiteral(*pn));
  1784     if ((*pn)->isKind(PNK_NEG)) {
  1785         *pn = UnaryKid(*pn);
  1786         return -NumberNodeValue(*pn);
  1789     return NumberNodeValue(*pn);
  1792 static NumLit
  1793 ExtractNumericLiteral(ModuleCompiler &m, ParseNode *pn)
  1795     JS_ASSERT(IsNumericLiteral(m, pn));
  1797     // Float literals are explicitly coerced and thus the coerced literal may be
  1798     // any valid (non-float) numeric literal.
  1799     if (pn->isKind(PNK_CALL)) {
  1800         pn = CallArgList(pn);
  1801         double d = ExtractNumericNonFloatValue(&pn);
  1802         return NumLit(NumLit::Float, DoubleValue(d));
  1805     double d = ExtractNumericNonFloatValue(&pn);
  1807     // The asm.js spec syntactically distinguishes any literal containing a
  1808     // decimal point or the literal -0 as having double type.
  1809     if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
  1810         return NumLit(NumLit::Double, DoubleValue(d));
  1812     // The syntactic checks above rule out these double values.
  1813     JS_ASSERT(!IsNegativeZero(d));
  1814     JS_ASSERT(!IsNaN(d));
  1816     // Although doubles can only *precisely* represent 53-bit integers, they
  1817     // can *imprecisely* represent integers much bigger than an int64_t.
  1818     // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
  1819     // is undefined, so test against the integer bounds using doubles.
  1820     if (d < double(INT32_MIN) || d > double(UINT32_MAX))
  1821         return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
  1823     // With the above syntactic and range limitations, d is definitely an
  1824     // integer in the range [INT32_MIN, UINT32_MAX] range.
  1825     int64_t i64 = int64_t(d);
  1826     if (i64 >= 0) {
  1827         if (i64 <= INT32_MAX)
  1828             return NumLit(NumLit::Fixnum, Int32Value(i64));
  1829         JS_ASSERT(i64 <= UINT32_MAX);
  1830         return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
  1832     JS_ASSERT(i64 >= INT32_MIN);
  1833     return NumLit(NumLit::NegativeInt, Int32Value(i64));
  1836 static inline bool
  1837 IsLiteralInt(ModuleCompiler &m, ParseNode *pn, uint32_t *u32)
  1839     if (!IsNumericLiteral(m, pn))
  1840         return false;
  1842     NumLit literal = ExtractNumericLiteral(m, pn);
  1843     switch (literal.which()) {
  1844       case NumLit::Fixnum:
  1845       case NumLit::BigUnsigned:
  1846       case NumLit::NegativeInt:
  1847         *u32 = uint32_t(literal.toInt32());
  1848         return true;
  1849       case NumLit::Double:
  1850       case NumLit::Float:
  1851       case NumLit::OutOfRangeInt:
  1852         return false;
  1855     MOZ_ASSUME_UNREACHABLE("Bad literal type");
  1858 /*****************************************************************************/
  1860 namespace {
  1862 // Encapsulates the compilation of a single function in an asm.js module. The
  1863 // function compiler handles the creation and final backend compilation of the
  1864 // MIR graph. Also see ModuleCompiler comment.
  1865 class FunctionCompiler
  1867   public:
  1868     struct Local
  1870         VarType type;
  1871         unsigned slot;
  1872         Local(VarType t, unsigned slot) : type(t), slot(slot) {}
  1873     };
  1875     struct TypedValue
  1877         VarType type;
  1878         Value value;
  1879         TypedValue(VarType t, const Value &v) : type(t), value(v) {}
  1880     };
  1882   private:
  1883     typedef HashMap<PropertyName*, Local> LocalMap;
  1884     typedef js::Vector<TypedValue> VarInitializerVector;
  1885     typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap;
  1886     typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap;
  1887     typedef js::Vector<ParseNode*, 4> NodeStack;
  1889     ModuleCompiler &       m_;
  1890     LifoAlloc &            lifo_;
  1891     ParseNode *            fn_;
  1892     uint32_t               functionNameIndex_;
  1894     LocalMap               locals_;
  1895     VarInitializerVector   varInitializers_;
  1896     Maybe<RetType>         alreadyReturned_;
  1898     TempAllocator *        alloc_;
  1899     MIRGraph *             graph_;
  1900     CompileInfo *          info_;
  1901     MIRGenerator *         mirGen_;
  1902     Maybe<IonContext>      ionContext_;
  1904     MBasicBlock *          curBlock_;
  1906     NodeStack              loopStack_;
  1907     NodeStack              breakableStack_;
  1908     UnlabeledBlockMap      unlabeledBreaks_;
  1909     UnlabeledBlockMap      unlabeledContinues_;
  1910     LabeledBlockMap        labeledBreaks_;
  1911     LabeledBlockMap        labeledContinues_;
  1913     static const uint32_t NO_FUNCTION_NAME_INDEX = UINT32_MAX;
  1914     JS_STATIC_ASSERT(NO_FUNCTION_NAME_INDEX > CallSiteDesc::FUNCTION_NAME_INDEX_MAX);
  1916   public:
  1917     FunctionCompiler(ModuleCompiler &m, ParseNode *fn, LifoAlloc &lifo)
  1918       : m_(m),
  1919         lifo_(lifo),
  1920         fn_(fn),
  1921         functionNameIndex_(NO_FUNCTION_NAME_INDEX),
  1922         locals_(m.cx()),
  1923         varInitializers_(m.cx()),
  1924         alloc_(nullptr),
  1925         graph_(nullptr),
  1926         info_(nullptr),
  1927         mirGen_(nullptr),
  1928         curBlock_(nullptr),
  1929         loopStack_(m.cx()),
  1930         breakableStack_(m.cx()),
  1931         unlabeledBreaks_(m.cx()),
  1932         unlabeledContinues_(m.cx()),
  1933         labeledBreaks_(m.cx()),
  1934         labeledContinues_(m.cx())
  1935     {}
  1937     ModuleCompiler &    m() const      { return m_; }
  1938     TempAllocator &     alloc() const  { return *alloc_; }
  1939     LifoAlloc &         lifo() const   { return lifo_; }
  1940     ParseNode *         fn() const     { return fn_; }
  1941     ExclusiveContext *  cx() const     { return m_.cx(); }
  1942     const AsmJSModule & module() const { return m_.module(); }
  1944     bool init()
  1946         return locals_.init() &&
  1947                unlabeledBreaks_.init() &&
  1948                unlabeledContinues_.init() &&
  1949                labeledBreaks_.init() &&
  1950                labeledContinues_.init();
  1953     bool fail(ParseNode *pn, const char *str)
  1955         return m_.fail(pn, str);
  1958     bool failf(ParseNode *pn, const char *fmt, ...)
  1960         va_list ap;
  1961         va_start(ap, fmt);
  1962         m_.failfVA(pn, fmt, ap);
  1963         va_end(ap);
  1964         return false;
  1967     bool failName(ParseNode *pn, const char *fmt, PropertyName *name)
  1969         return m_.failName(pn, fmt, name);
  1972     ~FunctionCompiler()
  1974 #ifdef DEBUG
  1975         if (!m().hasError() && cx()->isJSContext() && !cx()->asJSContext()->isExceptionPending()) {
  1976             JS_ASSERT(loopStack_.empty());
  1977             JS_ASSERT(unlabeledBreaks_.empty());
  1978             JS_ASSERT(unlabeledContinues_.empty());
  1979             JS_ASSERT(labeledBreaks_.empty());
  1980             JS_ASSERT(labeledContinues_.empty());
  1981             JS_ASSERT(inDeadCode());
  1983 #endif
  1986     /***************************************************** Local scope setup */
  1988     bool addFormal(ParseNode *pn, PropertyName *name, VarType type)
  1990         LocalMap::AddPtr p = locals_.lookupForAdd(name);
  1991         if (p)
  1992             return failName(pn, "duplicate local name '%s' not allowed", name);
  1993         return locals_.add(p, name, Local(type, locals_.count()));
  1996     bool addVariable(ParseNode *pn, PropertyName *name, VarType type, const Value &init)
  1998         LocalMap::AddPtr p = locals_.lookupForAdd(name);
  1999         if (p)
  2000             return failName(pn, "duplicate local name '%s' not allowed", name);
  2001         if (!locals_.add(p, name, Local(type, locals_.count())))
  2002             return false;
  2003         return varInitializers_.append(TypedValue(type, init));
  2006     bool prepareToEmitMIR(const VarTypeVector &argTypes)
  2008         JS_ASSERT(locals_.count() == argTypes.length() + varInitializers_.length());
  2010         alloc_  = lifo_.new_<TempAllocator>(&lifo_);
  2011         ionContext_.construct(m_.cx(), alloc_);
  2013         graph_  = lifo_.new_<MIRGraph>(alloc_);
  2014         info_   = lifo_.new_<CompileInfo>(locals_.count(), SequentialExecution);
  2015         const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
  2016         const JitCompileOptions options;
  2017         mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()),
  2018                                            options, alloc_,
  2019                                            graph_, info_, optimizationInfo);
  2021         if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_))
  2022             return false;
  2024         for (ABIArgTypeIter i = argTypes; !i.done(); i++) {
  2025             MAsmJSParameter *ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
  2026             curBlock_->add(ins);
  2027             curBlock_->initSlot(info().localSlot(i.index()), ins);
  2028             if (!mirGen_->ensureBallast())
  2029                 return false;
  2031         unsigned firstLocalSlot = argTypes.length();
  2032         for (unsigned i = 0; i < varInitializers_.length(); i++) {
  2033             MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value,
  2034                                                  varInitializers_[i].type.toMIRType());
  2035             curBlock_->add(ins);
  2036             curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
  2037             if (!mirGen_->ensureBallast())
  2038                 return false;
  2040         return true;
  2043     /******************************* For consistency of returns in a function */
  2045     bool hasAlreadyReturned() const {
  2046         return !alreadyReturned_.empty();
  2049     RetType returnedType() const {
  2050         return alreadyReturned_.ref();
  2053     void setReturnedType(RetType retType) {
  2054         alreadyReturned_.construct(retType);
  2057     /************************* Read-only interface (after local scope setup) */
  2059     MIRGenerator & mirGen() const     { JS_ASSERT(mirGen_); return *mirGen_; }
  2060     MIRGraph &     mirGraph() const   { JS_ASSERT(graph_); return *graph_; }
  2061     CompileInfo &  info() const       { JS_ASSERT(info_); return *info_; }
  2063     const Local *lookupLocal(PropertyName *name) const
  2065         if (LocalMap::Ptr p = locals_.lookup(name))
  2066             return &p->value();
  2067         return nullptr;
  2070     MDefinition *getLocalDef(const Local &local)
  2072         if (inDeadCode())
  2073             return nullptr;
  2074         return curBlock_->getSlot(info().localSlot(local.slot));
  2077     const ModuleCompiler::Global *lookupGlobal(PropertyName *name) const
  2079         if (locals_.has(name))
  2080             return nullptr;
  2081         return m_.lookupGlobal(name);
  2084     /***************************** Code generation (after local scope setup) */
  2086     MDefinition *constant(Value v, Type t)
  2088         if (inDeadCode())
  2089             return nullptr;
  2090         MConstant *constant = MConstant::NewAsmJS(alloc(), v, t.toMIRType());
  2091         curBlock_->add(constant);
  2092         return constant;
  2095     template <class T>
  2096     MDefinition *unary(MDefinition *op)
  2098         if (inDeadCode())
  2099             return nullptr;
  2100         T *ins = T::NewAsmJS(alloc(), op);
  2101         curBlock_->add(ins);
  2102         return ins;
  2105     template <class T>
  2106     MDefinition *unary(MDefinition *op, MIRType type)
  2108         if (inDeadCode())
  2109             return nullptr;
  2110         T *ins = T::NewAsmJS(alloc(), op, type);
  2111         curBlock_->add(ins);
  2112         return ins;
  2115     template <class T>
  2116     MDefinition *binary(MDefinition *lhs, MDefinition *rhs)
  2118         if (inDeadCode())
  2119             return nullptr;
  2120         T *ins = T::New(alloc(), lhs, rhs);
  2121         curBlock_->add(ins);
  2122         return ins;
  2125     template <class T>
  2126     MDefinition *binary(MDefinition *lhs, MDefinition *rhs, MIRType type)
  2128         if (inDeadCode())
  2129             return nullptr;
  2130         T *ins = T::NewAsmJS(alloc(), lhs, rhs, type);
  2131         curBlock_->add(ins);
  2132         return ins;
  2135     MDefinition *minMax(MDefinition *lhs, MDefinition *rhs, MIRType type, bool isMax) {
  2136         if (inDeadCode())
  2137             return nullptr;
  2138         MMinMax *ins = MMinMax::New(alloc(), lhs, rhs, type, isMax);
  2139         curBlock_->add(ins);
  2140         return ins;
  2143     MDefinition *mul(MDefinition *lhs, MDefinition *rhs, MIRType type, MMul::Mode mode)
  2145         if (inDeadCode())
  2146             return nullptr;
  2147         MMul *ins = MMul::New(alloc(), lhs, rhs, type, mode);
  2148         curBlock_->add(ins);
  2149         return ins;
  2152     MDefinition *div(MDefinition *lhs, MDefinition *rhs, MIRType type, bool unsignd)
  2154         if (inDeadCode())
  2155             return nullptr;
  2156         MDiv *ins = MDiv::NewAsmJS(alloc(), lhs, rhs, type, unsignd);
  2157         curBlock_->add(ins);
  2158         return ins;
  2161     MDefinition *mod(MDefinition *lhs, MDefinition *rhs, MIRType type, bool unsignd)
  2163         if (inDeadCode())
  2164             return nullptr;
  2165         MMod *ins = MMod::NewAsmJS(alloc(), lhs, rhs, type, unsignd);
  2166         curBlock_->add(ins);
  2167         return ins;
  2170     template <class T>
  2171     MDefinition *bitwise(MDefinition *lhs, MDefinition *rhs)
  2173         if (inDeadCode())
  2174             return nullptr;
  2175         T *ins = T::NewAsmJS(alloc(), lhs, rhs);
  2176         curBlock_->add(ins);
  2177         return ins;
  2180     template <class T>
  2181     MDefinition *bitwise(MDefinition *op)
  2183         if (inDeadCode())
  2184             return nullptr;
  2185         T *ins = T::NewAsmJS(alloc(), op);
  2186         curBlock_->add(ins);
  2187         return ins;
  2190     MDefinition *compare(MDefinition *lhs, MDefinition *rhs, JSOp op, MCompare::CompareType type)
  2192         if (inDeadCode())
  2193             return nullptr;
  2194         MCompare *ins = MCompare::NewAsmJS(alloc(), lhs, rhs, op, type);
  2195         curBlock_->add(ins);
  2196         return ins;
  2199     void assign(const Local &local, MDefinition *def)
  2201         if (inDeadCode())
  2202             return;
  2203         curBlock_->setSlot(info().localSlot(local.slot), def);
  2206     MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
  2208         if (inDeadCode())
  2209             return nullptr;
  2210         MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr);
  2211         curBlock_->add(load);
  2212         if (chk == NO_BOUNDS_CHECK)
  2213             load->setSkipBoundsCheck(true);
  2214         return load;
  2217     void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
  2219         if (inDeadCode())
  2220             return;
  2221         MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v);
  2222         curBlock_->add(store);
  2223         if (chk == NO_BOUNDS_CHECK)
  2224             store->setSkipBoundsCheck(true);
  2227     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
  2229         if (inDeadCode())
  2230             return nullptr;
  2232         uint32_t index = global.varOrConstIndex();
  2233         unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(index);
  2234         MIRType type = global.varOrConstType().toMIRType();
  2235         MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(alloc(), type, globalDataOffset,
  2236                                                              global.isConst());
  2237         curBlock_->add(load);
  2238         return load;
  2241     void storeGlobalVar(const ModuleCompiler::Global &global, MDefinition *v)
  2243         if (inDeadCode())
  2244             return;
  2245         JS_ASSERT(!global.isConst());
  2246         unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varOrConstIndex());
  2247         curBlock_->add(MAsmJSStoreGlobalVar::New(alloc(), globalDataOffset, v));
  2250     /***************************************************************** Calls */
  2252     // The IonMonkey backend maintains a single stack offset (from the stack
  2253     // pointer to the base of the frame) by adding the total amount of spill
  2254     // space required plus the maximum stack required for argument passing.
  2255     // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
  2256     // manually accumulate, for the entire function, the maximum required stack
  2257     // space for argument passing. (This is passed to the CodeGenerator via
  2258     // MIRGenerator::maxAsmJSStackArgBytes.) Naively, this would just be the
  2259     // maximum of the stack space required for each individual call (as
  2260     // determined by the call ABI). However, as an optimization, arguments are
  2261     // stored to the stack immediately after evaluation (to decrease live
  2262     // ranges and reduce spilling). This introduces the complexity that,
  2263     // between evaluating an argument and making the call, another argument
  2264     // evaluation could perform a call that also needs to store to the stack.
  2265     // When this occurs childClobbers_ = true and the parent expression's
  2266     // arguments are stored above the maximum depth clobbered by a child
  2267     // expression.
  2269     class Call
  2271         ParseNode *node_;
  2272         ABIArgGenerator abi_;
  2273         uint32_t prevMaxStackBytes_;
  2274         uint32_t maxChildStackBytes_;
  2275         uint32_t spIncrement_;
  2276         Signature sig_;
  2277         MAsmJSCall::Args regArgs_;
  2278         js::Vector<MAsmJSPassStackArg*> stackArgs_;
  2279         bool childClobbers_;
  2281         friend class FunctionCompiler;
  2283       public:
  2284         Call(FunctionCompiler &f, ParseNode *callNode, RetType retType)
  2285           : node_(callNode),
  2286             prevMaxStackBytes_(0),
  2287             maxChildStackBytes_(0),
  2288             spIncrement_(0),
  2289             sig_(f.m().lifo(), retType),
  2290             regArgs_(f.cx()),
  2291             stackArgs_(f.cx()),
  2292             childClobbers_(false)
  2293         { }
  2294         Signature &sig() { return sig_; }
  2295         const Signature &sig() const { return sig_; }
  2296     };
  2298     void startCallArgs(Call *call)
  2300         if (inDeadCode())
  2301             return;
  2302         call->prevMaxStackBytes_ = mirGen().resetAsmJSMaxStackArgBytes();
  2305     bool passArg(MDefinition *argDef, VarType type, Call *call)
  2307         if (!call->sig().appendArg(type))
  2308             return false;
  2310         if (inDeadCode())
  2311             return true;
  2313         uint32_t childStackBytes = mirGen().resetAsmJSMaxStackArgBytes();
  2314         call->maxChildStackBytes_ = Max(call->maxChildStackBytes_, childStackBytes);
  2315         if (childStackBytes > 0 && !call->stackArgs_.empty())
  2316             call->childClobbers_ = true;
  2318         ABIArg arg = call->abi_.next(type.toMIRType());
  2319         if (arg.kind() == ABIArg::Stack) {
  2320             MAsmJSPassStackArg *mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(),
  2321                                                               argDef);
  2322             curBlock_->add(mir);
  2323             if (!call->stackArgs_.append(mir))
  2324                 return false;
  2325         } else {
  2326             if (!call->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef)))
  2327                 return false;
  2329         return true;
  2332     void finishCallArgs(Call *call)
  2334         if (inDeadCode())
  2335             return;
  2336         uint32_t parentStackBytes = call->abi_.stackBytesConsumedSoFar();
  2337         uint32_t newStackBytes;
  2338         if (call->childClobbers_) {
  2339             call->spIncrement_ = AlignBytes(call->maxChildStackBytes_, StackAlignment);
  2340             for (unsigned i = 0; i < call->stackArgs_.length(); i++)
  2341                 call->stackArgs_[i]->incrementOffset(call->spIncrement_);
  2342             newStackBytes = Max(call->prevMaxStackBytes_,
  2343                                 call->spIncrement_ + parentStackBytes);
  2344         } else {
  2345             call->spIncrement_ = 0;
  2346             newStackBytes = Max(call->prevMaxStackBytes_,
  2347                                 Max(call->maxChildStackBytes_, parentStackBytes));
  2349         mirGen_->setAsmJSMaxStackArgBytes(newStackBytes);
  2352   private:
  2353     bool callPrivate(MAsmJSCall::Callee callee, const Call &call, MIRType returnType, MDefinition **def)
  2355         if (inDeadCode()) {
  2356             *def = nullptr;
  2357             return true;
  2360         uint32_t line, column;
  2361         m_.tokenStream().srcCoords.lineNumAndColumnIndex(call.node_->pn_pos.begin, &line, &column);
  2363         if (functionNameIndex_ == NO_FUNCTION_NAME_INDEX) {
  2364             if (!m_.addFunctionName(FunctionName(fn_), &functionNameIndex_))
  2365                 return false;
  2368         CallSiteDesc desc(line, column, functionNameIndex_);
  2369         MAsmJSCall *ins = MAsmJSCall::New(alloc(), desc, callee, call.regArgs_, returnType,
  2370                                           call.spIncrement_);
  2371         if (!ins)
  2372             return false;
  2374         curBlock_->add(ins);
  2375         *def = ins;
  2376         return true;
  2379   public:
  2380     bool internalCall(const ModuleCompiler::Func &func, const Call &call, MDefinition **def)
  2382         MIRType returnType = func.sig().retType().toMIRType();
  2383         return callPrivate(MAsmJSCall::Callee(func.code()), call, returnType, def);
  2386     bool funcPtrCall(const ModuleCompiler::FuncPtrTable &table, MDefinition *index,
  2387                      const Call &call, MDefinition **def)
  2389         if (inDeadCode()) {
  2390             *def = nullptr;
  2391             return true;
  2394         MConstant *mask = MConstant::New(alloc(), Int32Value(table.mask()));
  2395         curBlock_->add(mask);
  2396         MBitAnd *maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
  2397         curBlock_->add(maskedIndex);
  2398         MAsmJSLoadFuncPtr *ptrFun = MAsmJSLoadFuncPtr::New(alloc(), table.globalDataOffset(), maskedIndex);
  2399         curBlock_->add(ptrFun);
  2401         MIRType returnType = table.sig().retType().toMIRType();
  2402         return callPrivate(MAsmJSCall::Callee(ptrFun), call, returnType, def);
  2405     bool ffiCall(unsigned exitIndex, const Call &call, MIRType returnType, MDefinition **def)
  2407         if (inDeadCode()) {
  2408             *def = nullptr;
  2409             return true;
  2412         JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0);
  2413         unsigned globalDataOffset = module().exitIndexToGlobalDataOffset(exitIndex);
  2415         MAsmJSLoadFFIFunc *ptrFun = MAsmJSLoadFFIFunc::New(alloc(), globalDataOffset);
  2416         curBlock_->add(ptrFun);
  2418         return callPrivate(MAsmJSCall::Callee(ptrFun), call, returnType, def);
  2421     bool builtinCall(AsmJSImmKind builtin, const Call &call, MIRType returnType, MDefinition **def)
  2423         return callPrivate(MAsmJSCall::Callee(builtin), call, returnType, def);
  2426     /*********************************************** Control flow generation */
  2428     inline bool inDeadCode() const {
  2429         return curBlock_ == nullptr;
  2432     void returnExpr(MDefinition *expr)
  2434         if (inDeadCode())
  2435             return;
  2436         MAsmJSReturn *ins = MAsmJSReturn::New(alloc(), expr);
  2437         curBlock_->end(ins);
  2438         curBlock_ = nullptr;
  2441     void returnVoid()
  2443         if (inDeadCode())
  2444             return;
  2445         MAsmJSVoidReturn *ins = MAsmJSVoidReturn::New(alloc());
  2446         curBlock_->end(ins);
  2447         curBlock_ = nullptr;
  2450     bool branchAndStartThen(MDefinition *cond, MBasicBlock **thenBlock, MBasicBlock **elseBlock,
  2451                             ParseNode *thenPn, ParseNode* elsePn)
  2453         if (inDeadCode())
  2454             return true;
  2456         bool hasThenBlock = *thenBlock != nullptr;
  2457         bool hasElseBlock = *elseBlock != nullptr;
  2459         if (!hasThenBlock && !newBlock(curBlock_, thenBlock, thenPn))
  2460             return false;
  2461         if (!hasElseBlock && !newBlock(curBlock_, elseBlock, thenPn))
  2462             return false;
  2464         curBlock_->end(MTest::New(alloc(), cond, *thenBlock, *elseBlock));
  2466         // Only add as a predecessor if newBlock hasn't been called (as it does it for us)
  2467         if (hasThenBlock && !(*thenBlock)->addPredecessor(alloc(), curBlock_))
  2468             return false;
  2469         if (hasElseBlock && !(*elseBlock)->addPredecessor(alloc(), curBlock_))
  2470             return false;
  2472         curBlock_ = *thenBlock;
  2473         mirGraph().moveBlockToEnd(curBlock_);
  2474         return true;
  2477     void assertCurrentBlockIs(MBasicBlock *block) {
  2478         if (inDeadCode())
  2479             return;
  2480         JS_ASSERT(curBlock_ == block);
  2483     bool appendThenBlock(BlockVector *thenBlocks)
  2485         if (inDeadCode())
  2486             return true;
  2487         return thenBlocks->append(curBlock_);
  2490     bool joinIf(const BlockVector &thenBlocks, MBasicBlock *joinBlock)
  2492         if (!joinBlock)
  2493             return true;
  2494         JS_ASSERT_IF(curBlock_, thenBlocks.back() == curBlock_);
  2495         for (size_t i = 0; i < thenBlocks.length(); i++) {
  2496             thenBlocks[i]->end(MGoto::New(alloc(), joinBlock));
  2497             if (!joinBlock->addPredecessor(alloc(), thenBlocks[i]))
  2498                 return false;
  2500         curBlock_ = joinBlock;
  2501         mirGraph().moveBlockToEnd(curBlock_);
  2502         return true;
  2505     void switchToElse(MBasicBlock *elseBlock)
  2507         if (!elseBlock)
  2508             return;
  2509         curBlock_ = elseBlock;
  2510         mirGraph().moveBlockToEnd(curBlock_);
  2513     bool joinIfElse(const BlockVector &thenBlocks, ParseNode *pn)
  2515         if (inDeadCode() && thenBlocks.empty())
  2516             return true;
  2517         MBasicBlock *pred = curBlock_ ? curBlock_ : thenBlocks[0];
  2518         MBasicBlock *join;
  2519         if (!newBlock(pred, &join, pn))
  2520             return false;
  2521         if (curBlock_)
  2522             curBlock_->end(MGoto::New(alloc(), join));
  2523         for (size_t i = 0; i < thenBlocks.length(); i++) {
  2524             thenBlocks[i]->end(MGoto::New(alloc(), join));
  2525             if (pred == curBlock_ || i > 0) {
  2526                 if (!join->addPredecessor(alloc(), thenBlocks[i]))
  2527                     return false;
  2530         curBlock_ = join;
  2531         return true;
  2534     void pushPhiInput(MDefinition *def)
  2536         if (inDeadCode())
  2537             return;
  2538         JS_ASSERT(curBlock_->stackDepth() == info().firstStackSlot());
  2539         curBlock_->push(def);
  2542     MDefinition *popPhiOutput()
  2544         if (inDeadCode())
  2545             return nullptr;
  2546         JS_ASSERT(curBlock_->stackDepth() == info().firstStackSlot() + 1);
  2547         return curBlock_->pop();
  2550     bool startPendingLoop(ParseNode *pn, MBasicBlock **loopEntry, ParseNode *bodyStmt)
  2552         if (!loopStack_.append(pn) || !breakableStack_.append(pn))
  2553             return false;
  2554         JS_ASSERT_IF(curBlock_, curBlock_->loopDepth() == loopStack_.length() - 1);
  2555         if (inDeadCode()) {
  2556             *loopEntry = nullptr;
  2557             return true;
  2559         *loopEntry = MBasicBlock::NewAsmJS(mirGraph(), info(), curBlock_,
  2560                                            MBasicBlock::PENDING_LOOP_HEADER);
  2561         if (!*loopEntry)
  2562             return false;
  2563         mirGraph().addBlock(*loopEntry);
  2564         noteBasicBlockPosition(*loopEntry, bodyStmt);
  2565         (*loopEntry)->setLoopDepth(loopStack_.length());
  2566         curBlock_->end(MGoto::New(alloc(), *loopEntry));
  2567         curBlock_ = *loopEntry;
  2568         return true;
  2571     bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop, ParseNode *bodyPn, ParseNode *afterPn)
  2573         if (inDeadCode()) {
  2574             *afterLoop = nullptr;
  2575             return true;
  2577         JS_ASSERT(curBlock_->loopDepth() > 0);
  2578         MBasicBlock *body;
  2579         if (!newBlock(curBlock_, &body, bodyPn))
  2580             return false;
  2581         if (cond->isConstant() && cond->toConstant()->valueToBoolean()) {
  2582             *afterLoop = nullptr;
  2583             curBlock_->end(MGoto::New(alloc(), body));
  2584         } else {
  2585             if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop, afterPn))
  2586                 return false;
  2587             curBlock_->end(MTest::New(alloc(), cond, body, *afterLoop));
  2589         curBlock_ = body;
  2590         return true;
  2593   private:
  2594     ParseNode *popLoop()
  2596         ParseNode *pn = loopStack_.popCopy();
  2597         JS_ASSERT(!unlabeledContinues_.has(pn));
  2598         breakableStack_.popBack();
  2599         return pn;
  2602   public:
  2603     bool closeLoop(MBasicBlock *loopEntry, MBasicBlock *afterLoop)
  2605         ParseNode *pn = popLoop();
  2606         if (!loopEntry) {
  2607             JS_ASSERT(!afterLoop);
  2608             JS_ASSERT(inDeadCode());
  2609             JS_ASSERT(!unlabeledBreaks_.has(pn));
  2610             return true;
  2612         JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1);
  2613         JS_ASSERT_IF(afterLoop, afterLoop->loopDepth() == loopStack_.length());
  2614         if (curBlock_) {
  2615             JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1);
  2616             curBlock_->end(MGoto::New(alloc(), loopEntry));
  2617             if (!loopEntry->setBackedgeAsmJS(curBlock_))
  2618                 return false;
  2620         curBlock_ = afterLoop;
  2621         if (curBlock_)
  2622             mirGraph().moveBlockToEnd(curBlock_);
  2623         return bindUnlabeledBreaks(pn);
  2626     bool branchAndCloseDoWhileLoop(MDefinition *cond, MBasicBlock *loopEntry, ParseNode *afterLoopStmt)
  2628         ParseNode *pn = popLoop();
  2629         if (!loopEntry) {
  2630             JS_ASSERT(inDeadCode());
  2631             JS_ASSERT(!unlabeledBreaks_.has(pn));
  2632             return true;
  2634         JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1);
  2635         if (curBlock_) {
  2636             JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1);
  2637             if (cond->isConstant()) {
  2638                 if (cond->toConstant()->valueToBoolean()) {
  2639                     curBlock_->end(MGoto::New(alloc(), loopEntry));
  2640                     if (!loopEntry->setBackedgeAsmJS(curBlock_))
  2641                         return false;
  2642                     curBlock_ = nullptr;
  2643                 } else {
  2644                     MBasicBlock *afterLoop;
  2645                     if (!newBlock(curBlock_, &afterLoop, afterLoopStmt))
  2646                         return false;
  2647                     curBlock_->end(MGoto::New(alloc(), afterLoop));
  2648                     curBlock_ = afterLoop;
  2650             } else {
  2651                 MBasicBlock *afterLoop;
  2652                 if (!newBlock(curBlock_, &afterLoop, afterLoopStmt))
  2653                     return false;
  2654                 curBlock_->end(MTest::New(alloc(), cond, loopEntry, afterLoop));
  2655                 if (!loopEntry->setBackedgeAsmJS(curBlock_))
  2656                     return false;
  2657                 curBlock_ = afterLoop;
  2660         return bindUnlabeledBreaks(pn);
  2663     bool bindContinues(ParseNode *pn, const LabelVector *maybeLabels)
  2665         bool createdJoinBlock = false;
  2666         if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(pn)) {
  2667             if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock, pn))
  2668                 return false;
  2669             unlabeledContinues_.remove(p);
  2671         return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_, &createdJoinBlock, pn);
  2674     bool bindLabeledBreaks(const LabelVector *maybeLabels, ParseNode *pn)
  2676         bool createdJoinBlock = false;
  2677         return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_, &createdJoinBlock, pn);
  2680     bool addBreak(PropertyName *maybeLabel) {
  2681         if (maybeLabel)
  2682             return addBreakOrContinue(maybeLabel, &labeledBreaks_);
  2683         return addBreakOrContinue(breakableStack_.back(), &unlabeledBreaks_);
  2686     bool addContinue(PropertyName *maybeLabel) {
  2687         if (maybeLabel)
  2688             return addBreakOrContinue(maybeLabel, &labeledContinues_);
  2689         return addBreakOrContinue(loopStack_.back(), &unlabeledContinues_);
  2692     bool startSwitch(ParseNode *pn, MDefinition *expr, int32_t low, int32_t high,
  2693                      MBasicBlock **switchBlock)
  2695         if (!breakableStack_.append(pn))
  2696             return false;
  2697         if (inDeadCode()) {
  2698             *switchBlock = nullptr;
  2699             return true;
  2701         curBlock_->end(MTableSwitch::New(alloc(), expr, low, high));
  2702         *switchBlock = curBlock_;
  2703         curBlock_ = nullptr;
  2704         return true;
  2707     bool startSwitchCase(MBasicBlock *switchBlock, MBasicBlock **next, ParseNode *pn)
  2709         if (!switchBlock) {
  2710             *next = nullptr;
  2711             return true;
  2713         if (!newBlock(switchBlock, next, pn))
  2714             return false;
  2715         if (curBlock_) {
  2716             curBlock_->end(MGoto::New(alloc(), *next));
  2717             if (!(*next)->addPredecessor(alloc(), curBlock_))
  2718                 return false;
  2720         curBlock_ = *next;
  2721         return true;
  2724     bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock, ParseNode *pn)
  2726         if (!startSwitchCase(switchBlock, defaultBlock, pn))
  2727             return false;
  2728         if (!*defaultBlock)
  2729             return true;
  2730         mirGraph().moveBlockToEnd(*defaultBlock);
  2731         return true;
  2734     bool joinSwitch(MBasicBlock *switchBlock, const BlockVector &cases, MBasicBlock *defaultBlock)
  2736         ParseNode *pn = breakableStack_.popCopy();
  2737         if (!switchBlock)
  2738             return true;
  2739         MTableSwitch *mir = switchBlock->lastIns()->toTableSwitch();
  2740         size_t defaultIndex = mir->addDefault(defaultBlock);
  2741         for (unsigned i = 0; i < cases.length(); i++) {
  2742             if (!cases[i])
  2743                 mir->addCase(defaultIndex);
  2744             else
  2745                 mir->addCase(mir->addSuccessor(cases[i]));
  2747         if (curBlock_) {
  2748             MBasicBlock *next;
  2749             if (!newBlock(curBlock_, &next, pn))
  2750                 return false;
  2751             curBlock_->end(MGoto::New(alloc(), next));
  2752             curBlock_ = next;
  2754         return bindUnlabeledBreaks(pn);
  2757     /*************************************************************************/
  2759     MIRGenerator *extractMIR()
  2761         JS_ASSERT(mirGen_ != nullptr);
  2762         MIRGenerator *mirGen = mirGen_;
  2763         mirGen_ = nullptr;
  2764         return mirGen;
  2767     /*************************************************************************/
  2768   private:
  2769     void noteBasicBlockPosition(MBasicBlock *blk, ParseNode *pn)
  2771 #if defined(JS_ION_PERF)
  2772         if (pn) {
  2773             unsigned line = 0U, column = 0U;
  2774             m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
  2775             blk->setLineno(line);
  2776             blk->setColumnIndex(column);
  2778 #endif
  2781     bool newBlockWithDepth(MBasicBlock *pred, unsigned loopDepth, MBasicBlock **block, ParseNode *pn)
  2783         *block = MBasicBlock::NewAsmJS(mirGraph(), info(), pred, MBasicBlock::NORMAL);
  2784         if (!*block)
  2785             return false;
  2786         noteBasicBlockPosition(*block, pn);
  2787         mirGraph().addBlock(*block);
  2788         (*block)->setLoopDepth(loopDepth);
  2789         return true;
  2792     bool newBlock(MBasicBlock *pred, MBasicBlock **block, ParseNode *pn)
  2794         return newBlockWithDepth(pred, loopStack_.length(), block, pn);
  2797     bool bindBreaksOrContinues(BlockVector *preds, bool *createdJoinBlock, ParseNode *pn)
  2799         for (unsigned i = 0; i < preds->length(); i++) {
  2800             MBasicBlock *pred = (*preds)[i];
  2801             if (*createdJoinBlock) {
  2802                 pred->end(MGoto::New(alloc(), curBlock_));
  2803                 if (!curBlock_->addPredecessor(alloc(), pred))
  2804                     return false;
  2805             } else {
  2806                 MBasicBlock *next;
  2807                 if (!newBlock(pred, &next, pn))
  2808                     return false;
  2809                 pred->end(MGoto::New(alloc(), next));
  2810                 if (curBlock_) {
  2811                     curBlock_->end(MGoto::New(alloc(), next));
  2812                     if (!next->addPredecessor(alloc(), curBlock_))
  2813                         return false;
  2815                 curBlock_ = next;
  2816                 *createdJoinBlock = true;
  2818             JS_ASSERT(curBlock_->begin() == curBlock_->end());
  2819             if (!mirGen_->ensureBallast())
  2820                 return false;
  2822         preds->clear();
  2823         return true;
  2826     bool bindLabeledBreaksOrContinues(const LabelVector *maybeLabels, LabeledBlockMap *map,
  2827                                       bool *createdJoinBlock, ParseNode *pn)
  2829         if (!maybeLabels)
  2830             return true;
  2831         const LabelVector &labels = *maybeLabels;
  2832         for (unsigned i = 0; i < labels.length(); i++) {
  2833             if (LabeledBlockMap::Ptr p = map->lookup(labels[i])) {
  2834                 if (!bindBreaksOrContinues(&p->value(), createdJoinBlock, pn))
  2835                     return false;
  2836                 map->remove(p);
  2838             if (!mirGen_->ensureBallast())
  2839                 return false;
  2841         return true;
  2844     template <class Key, class Map>
  2845     bool addBreakOrContinue(Key key, Map *map)
  2847         if (inDeadCode())
  2848             return true;
  2849         typename Map::AddPtr p = map->lookupForAdd(key);
  2850         if (!p) {
  2851             BlockVector empty(m().cx());
  2852             if (!map->add(p, key, Move(empty)))
  2853                 return false;
  2855         if (!p->value().append(curBlock_))
  2856             return false;
  2857         curBlock_ = nullptr;
  2858         return true;
  2861     bool bindUnlabeledBreaks(ParseNode *pn)
  2863         bool createdJoinBlock = false;
  2864         if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(pn)) {
  2865             if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock, pn))
  2866                 return false;
  2867             unlabeledBreaks_.remove(p);
  2869         return true;
  2871 };
  2873 } /* anonymous namespace */
  2875 /*****************************************************************************/
  2876 // asm.js type-checking and code-generation algorithm
  2878 static bool
  2879 CheckIdentifier(ModuleCompiler &m, ParseNode *usepn, PropertyName *name)
  2881     if (name == m.cx()->names().arguments || name == m.cx()->names().eval)
  2882         return m.failName(usepn, "'%s' is not an allowed identifier", name);
  2883     return true;
  2886 static bool
  2887 CheckModuleLevelName(ModuleCompiler &m, ParseNode *usepn, PropertyName *name)
  2889     if (!CheckIdentifier(m, usepn, name))
  2890         return false;
  2892     if (name == m.moduleFunctionName() ||
  2893         name == m.module().globalArgumentName() ||
  2894         name == m.module().importArgumentName() ||
  2895         name == m.module().bufferArgumentName() ||
  2896         m.lookupGlobal(name))
  2898         return m.failName(usepn, "duplicate name '%s' not allowed", name);
  2901     return true;
  2904 static bool
  2905 CheckFunctionHead(ModuleCompiler &m, ParseNode *fn)
  2907     JSFunction *fun = FunctionObject(fn);
  2908     if (fun->hasRest())
  2909         return m.fail(fn, "rest args not allowed");
  2910     if (fun->isExprClosure())
  2911         return m.fail(fn, "expression closures not allowed");
  2912     if (fn->pn_funbox->hasDestructuringArgs)
  2913         return m.fail(fn, "destructuring args not allowed");
  2914     return true;
  2917 static bool
  2918 CheckArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name)
  2920     if (!IsDefinition(arg))
  2921         return m.fail(arg, "duplicate argument name not allowed");
  2923     if (arg->pn_dflags & PND_DEFAULT)
  2924         return m.fail(arg, "default arguments not allowed");
  2926     if (!CheckIdentifier(m, arg, arg->name()))
  2927         return false;
  2929     *name = arg->name();
  2930     return true;
  2933 static bool
  2934 CheckModuleArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name)
  2936     if (!CheckArgument(m, arg, name))
  2937         return false;
  2939     if (!CheckModuleLevelName(m, arg, *name))
  2940         return false;
  2942     return true;
  2945 static bool
  2946 CheckModuleArguments(ModuleCompiler &m, ParseNode *fn)
  2948     unsigned numFormals;
  2949     ParseNode *arg1 = FunctionArgsList(fn, &numFormals);
  2950     ParseNode *arg2 = arg1 ? NextNode(arg1) : nullptr;
  2951     ParseNode *arg3 = arg2 ? NextNode(arg2) : nullptr;
  2953     if (numFormals > 3)
  2954         return m.fail(fn, "asm.js modules takes at most 3 argument");
  2956     PropertyName *arg1Name = nullptr;
  2957     if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name))
  2958         return false;
  2959     m.initGlobalArgumentName(arg1Name);
  2961     PropertyName *arg2Name = nullptr;
  2962     if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name))
  2963         return false;
  2964     m.initImportArgumentName(arg2Name);
  2966     PropertyName *arg3Name = nullptr;
  2967     if (numFormals >= 3 && !CheckModuleArgument(m, arg3, &arg3Name))
  2968         return false;
  2969     m.initBufferArgumentName(arg3Name);
  2971     return true;
  2974 static bool
  2975 CheckPrecedingStatements(ModuleCompiler &m, ParseNode *stmtList)
  2977     JS_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
  2979     if (ListLength(stmtList) != 0)
  2980         return m.fail(ListHead(stmtList), "invalid asm.js statement");
  2982     return true;
  2985 static bool
  2986 CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
  2987                                 bool isConst)
  2989     NumLit literal = ExtractNumericLiteral(m, initNode);
  2990     if (!literal.hasType())
  2991         return m.fail(initNode, "global initializer is out of representable integer range");
  2993     return m.addGlobalVarInit(varName, literal.varType(), literal.value(), isConst);
  2996 static bool
  2997 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
  2998                     ParseNode **coercedExpr = nullptr)
  3000     switch (coercionNode->getKind()) {
  3001       case PNK_BITOR: {
  3002         ParseNode *rhs = BinaryRight(coercionNode);
  3003         uint32_t i;
  3004         if (!IsLiteralInt(m, rhs, &i) || i != 0)
  3005             return m.fail(rhs, "must use |0 for argument/return coercion");
  3006         *coercion = AsmJS_ToInt32;
  3007         if (coercedExpr)
  3008             *coercedExpr = BinaryLeft(coercionNode);
  3009         return true;
  3011       case PNK_POS: {
  3012         *coercion = AsmJS_ToNumber;
  3013         if (coercedExpr)
  3014             *coercedExpr = UnaryKid(coercionNode);
  3015         return true;
  3017       case PNK_CALL: {
  3018         *coercion = AsmJS_FRound;
  3019         if (!IsFloatCoercion(m, coercionNode, coercedExpr))
  3020             return m.fail(coercionNode, "call must be to fround coercion");
  3021         return true;
  3023       default:;
  3026     return m.fail(coercionNode, "must be of the form +x, fround(x) or x|0");
  3029 static bool
  3030 CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion,
  3031                               ParseNode *coercedExpr, bool isConst)
  3033     if (!coercedExpr->isKind(PNK_DOT))
  3034         return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
  3036     ParseNode *base = DotBase(coercedExpr);
  3037     PropertyName *field = DotMember(coercedExpr);
  3039     PropertyName *importName = m.module().importArgumentName();
  3040     if (!importName)
  3041         return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
  3042     if (!IsUseOfName(base, importName))
  3043         return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
  3045     return m.addGlobalVarImport(varName, field, coercion, isConst);
  3048 static bool
  3049 CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
  3050                               bool isConst)
  3052     AsmJSCoercion coercion;
  3053     ParseNode *coercedExpr;
  3054     if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
  3055         return false;
  3056     return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst);
  3059 static bool
  3060 CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
  3062     ParseNode *ctorExpr = ListHead(newExpr);
  3063     if (!ctorExpr->isKind(PNK_DOT))
  3064         return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
  3066     ParseNode *base = DotBase(ctorExpr);
  3067     PropertyName *field = DotMember(ctorExpr);
  3069     PropertyName *globalName = m.module().globalArgumentName();
  3070     if (!globalName)
  3071         return m.fail(base, "cannot create array view without an asm.js global parameter");
  3072     if (!IsUseOfName(base, globalName))
  3073         return m.failName(base, "expecting '%s.*Array", globalName);
  3075     ParseNode *bufArg = NextNode(ctorExpr);
  3076     if (!bufArg || NextNode(bufArg) != nullptr)
  3077         return m.fail(ctorExpr, "array view constructor takes exactly one argument");
  3079     PropertyName *bufferName = m.module().bufferArgumentName();
  3080     if (!bufferName)
  3081         return m.fail(bufArg, "cannot create array view without an asm.js heap parameter");
  3082     if (!IsUseOfName(bufArg, bufferName))
  3083         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
  3085     JSAtomState &names = m.cx()->names();
  3086     ArrayBufferView::ViewType type;
  3087     if (field == names.Int8Array)
  3088         type = ArrayBufferView::TYPE_INT8;
  3089     else if (field == names.Uint8Array)
  3090         type = ArrayBufferView::TYPE_UINT8;
  3091     else if (field == names.Int16Array)
  3092         type = ArrayBufferView::TYPE_INT16;
  3093     else if (field == names.Uint16Array)
  3094         type = ArrayBufferView::TYPE_UINT16;
  3095     else if (field == names.Int32Array)
  3096         type = ArrayBufferView::TYPE_INT32;
  3097     else if (field == names.Uint32Array)
  3098         type = ArrayBufferView::TYPE_UINT32;
  3099     else if (field == names.Float32Array)
  3100         type = ArrayBufferView::TYPE_FLOAT32;
  3101     else if (field == names.Float64Array)
  3102         type = ArrayBufferView::TYPE_FLOAT64;
  3103     else
  3104         return m.fail(ctorExpr, "could not match typed array name");
  3106     return m.addArrayView(varName, type, field);
  3109 static bool
  3110 CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
  3112     ParseNode *base = DotBase(initNode);
  3113     PropertyName *field = DotMember(initNode);
  3115     if (base->isKind(PNK_DOT)) {
  3116         ParseNode *global = DotBase(base);
  3117         PropertyName *math = DotMember(base);
  3118         if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math)
  3119             return m.fail(base, "expecting global.Math");
  3121         ModuleCompiler::MathBuiltin mathBuiltin;
  3122         if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
  3123             return m.failName(initNode, "'%s' is not a standard Math builtin", field);
  3125         switch (mathBuiltin.kind) {
  3126           case ModuleCompiler::MathBuiltin::Function:
  3127             return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
  3128           case ModuleCompiler::MathBuiltin::Constant:
  3129             return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
  3130           default:
  3131             break;
  3133         MOZ_ASSUME_UNREACHABLE("unexpected or uninitialized math builtin type");
  3136     if (IsUseOfName(base, m.module().globalArgumentName())) {
  3137         if (field == m.cx()->names().NaN)
  3138             return m.addGlobalConstant(varName, GenericNaN(), field);
  3139         if (field == m.cx()->names().Infinity)
  3140             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
  3141         return m.failName(initNode, "'%s' is not a standard global constant", field);
  3144     if (IsUseOfName(base, m.module().importArgumentName()))
  3145         return m.addFFI(varName, field);
  3147     return m.fail(initNode, "expecting c.y where c is either the global or foreign parameter");
  3150 static bool
  3151 CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst)
  3153     if (!IsDefinition(var))
  3154         return m.fail(var, "import variable names must be unique");
  3156     if (!CheckModuleLevelName(m, var, var->name()))
  3157         return false;
  3159     ParseNode *initNode = MaybeDefinitionInitializer(var);
  3160     if (!initNode)
  3161         return m.fail(var, "module import needs initializer");
  3163     if (IsNumericLiteral(m, initNode))
  3164         return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
  3166     if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS) || initNode->isKind(PNK_CALL))
  3167         return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
  3169     if (initNode->isKind(PNK_NEW))
  3170         return CheckNewArrayView(m, var->name(), initNode);
  3172     if (initNode->isKind(PNK_DOT))
  3173         return CheckGlobalDotImport(m, var->name(), initNode);
  3175     return m.fail(initNode, "unsupported import expression");
  3178 static bool
  3179 CheckModuleGlobals(ModuleCompiler &m)
  3181     while (true) {
  3182         ParseNode *varStmt;
  3183         if (!ParseVarOrConstStatement(m.parser(), &varStmt))
  3184             return false;
  3185         if (!varStmt)
  3186             break;
  3187         for (ParseNode *var = VarListHead(varStmt); var; var = NextNode(var)) {
  3188             if (!CheckModuleGlobal(m, var, varStmt->isKind(PNK_CONST)))
  3189                 return false;
  3193     return true;
  3196 static bool
  3197 ArgFail(FunctionCompiler &f, PropertyName *argName, ParseNode *stmt)
  3199     return f.failName(stmt, "expecting argument type declaration for '%s' of the "
  3200                       "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName);
  3203 static bool
  3204 CheckArgumentType(FunctionCompiler &f, ParseNode *stmt, PropertyName *name, VarType *type)
  3206     if (!stmt || !IsExpressionStatement(stmt))
  3207         return ArgFail(f, name, stmt ? stmt : f.fn());
  3209     ParseNode *initNode = ExpressionStatementExpr(stmt);
  3210     if (!initNode || !initNode->isKind(PNK_ASSIGN))
  3211         return ArgFail(f, name, stmt);
  3213     ParseNode *argNode = BinaryLeft(initNode);
  3214     ParseNode *coercionNode = BinaryRight(initNode);
  3216     if (!IsUseOfName(argNode, name))
  3217         return ArgFail(f, name, stmt);
  3219     ParseNode *coercedExpr;
  3220     AsmJSCoercion coercion;
  3221     if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion, &coercedExpr))
  3222         return false;
  3224     if (!IsUseOfName(coercedExpr, name))
  3225         return ArgFail(f, name, stmt);
  3227     *type = VarType(coercion);
  3228     return true;
  3231 static bool
  3232 CheckArguments(FunctionCompiler &f, ParseNode **stmtIter, VarTypeVector *argTypes)
  3234     ParseNode *stmt = *stmtIter;
  3236     unsigned numFormals;
  3237     ParseNode *argpn = FunctionArgsList(f.fn(), &numFormals);
  3239     for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
  3240         PropertyName *name;
  3241         if (!CheckArgument(f.m(), argpn, &name))
  3242             return false;
  3244         VarType type;
  3245         if (!CheckArgumentType(f, stmt, name, &type))
  3246             return false;
  3248         if (!argTypes->append(type))
  3249             return false;
  3251         if (!f.addFormal(argpn, name, type))
  3252             return false;
  3255     *stmtIter = stmt;
  3256     return true;
  3259 static bool
  3260 CheckFinalReturn(FunctionCompiler &f, ParseNode *stmt, RetType *retType)
  3262     if (stmt && stmt->isKind(PNK_RETURN)) {
  3263         if (ParseNode *coercionNode = UnaryKid(stmt)) {
  3264             if (IsNumericLiteral(f.m(), coercionNode)) {
  3265                 switch (ExtractNumericLiteral(f.m(), coercionNode).which()) {
  3266                   case NumLit::BigUnsigned:
  3267                   case NumLit::OutOfRangeInt:
  3268                     return f.fail(coercionNode, "returned literal is out of integer range");
  3269                   case NumLit::Fixnum:
  3270                   case NumLit::NegativeInt:
  3271                     *retType = RetType::Signed;
  3272                     break;
  3273                   case NumLit::Double:
  3274                     *retType = RetType::Double;
  3275                     break;
  3276                   case NumLit::Float:
  3277                     *retType = RetType::Float;
  3278                     break;
  3280                 return true;
  3283             AsmJSCoercion coercion;
  3284             if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion))
  3285                 return false;
  3287             *retType = RetType(coercion);
  3288             return true;
  3291         *retType = RetType::Void;
  3292         return true;
  3295     *retType = RetType::Void;
  3296     f.returnVoid();
  3297     return true;
  3300 static bool
  3301 CheckVariable(FunctionCompiler &f, ParseNode *var)
  3303     if (!IsDefinition(var))
  3304         return f.fail(var, "local variable names must not restate argument names");
  3306     PropertyName *name = var->name();
  3308     if (!CheckIdentifier(f.m(), var, name))
  3309         return false;
  3311     ParseNode *initNode = MaybeDefinitionInitializer(var);
  3312     if (!initNode)
  3313         return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
  3315     if (initNode->isKind(PNK_NAME)) {
  3316         PropertyName *initName = initNode->name();
  3317         if (const ModuleCompiler::Global *global = f.lookupGlobal(initName)) {
  3318             if (global->which() != ModuleCompiler::Global::ConstantLiteral)
  3319                 return f.failName(initNode, "'%s' isn't a possible global variable initializer, "
  3320                                             "needs to be a const numeric literal", initName);
  3321             return f.addVariable(var, name, global->varOrConstType(), global->constLiteralValue());
  3323         return f.failName(initNode, "'%s' needs to be a global name", initName);
  3326     if (!IsNumericLiteral(f.m(), initNode))
  3327         return f.failName(initNode, "initializer for '%s' needs to be a numeric literal or a global const literal", name);
  3329     NumLit literal = ExtractNumericLiteral(f.m(), initNode);
  3330     if (!literal.hasType())
  3331         return f.failName(initNode, "initializer for '%s' is out of range", name);
  3333     return f.addVariable(var, name, literal.varType(), literal.value());
  3336 static bool
  3337 CheckVariables(FunctionCompiler &f, ParseNode **stmtIter)
  3339     ParseNode *stmt = *stmtIter;
  3341     for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) {
  3342         for (ParseNode *var = VarListHead(stmt); var; var = NextNode(var)) {
  3343             if (!CheckVariable(f, var))
  3344                 return false;
  3348     *stmtIter = stmt;
  3349     return true;
  3352 static bool
  3353 CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type);
  3355 static bool
  3356 CheckNumericLiteral(FunctionCompiler &f, ParseNode *num, MDefinition **def, Type *type)
  3358     NumLit literal = ExtractNumericLiteral(f.m(), num);
  3359     if (!literal.hasType())
  3360         return f.fail(num, "numeric literal out of representable integer range");
  3362     *type = literal.type();
  3363     *def = f.constant(literal.value(), literal.type());
  3364     return true;
  3367 static bool
  3368 CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *type)
  3370     PropertyName *name = varRef->name();
  3372     if (const FunctionCompiler::Local *local = f.lookupLocal(name)) {
  3373         *def = f.getLocalDef(*local);
  3374         *type = local->type.toType();
  3375         return true;
  3378     if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) {
  3379         switch (global->which()) {
  3380           case ModuleCompiler::Global::ConstantLiteral:
  3381             *def = f.constant(global->constLiteralValue(), global->varOrConstType().toType());
  3382             *type = global->varOrConstType().toType();
  3383             break;
  3384           case ModuleCompiler::Global::ConstantImport:
  3385           case ModuleCompiler::Global::Variable:
  3386             *def = f.loadGlobalVar(*global);
  3387             *type = global->varOrConstType().toType();
  3388             break;
  3389           case ModuleCompiler::Global::Function:
  3390           case ModuleCompiler::Global::FFI:
  3391           case ModuleCompiler::Global::MathBuiltinFunction:
  3392           case ModuleCompiler::Global::FuncPtrTable:
  3393           case ModuleCompiler::Global::ArrayView:
  3394             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
  3396         return true;
  3399     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
  3402 static inline bool
  3403 IsLiteralOrConstInt(FunctionCompiler &f, ParseNode *pn, uint32_t *u32)
  3405     if (IsLiteralInt(f.m(), pn, u32))
  3406         return true;
  3408     if (pn->getKind() != PNK_NAME)
  3409         return false;
  3411     PropertyName *name = pn->name();
  3412     const ModuleCompiler::Global *global = f.lookupGlobal(name);
  3413     if (!global || global->which() != ModuleCompiler::Global::ConstantLiteral)
  3414         return false;
  3416     const Value &v = global->constLiteralValue();
  3417     if (!v.isInt32())
  3418         return false;
  3420     *u32 = (uint32_t) v.toInt32();
  3421     return true;
  3424 static bool
  3425 FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
  3426                      NeedsBoundsCheck *needsBoundsCheck)
  3428     ParseNode *indexNode = BinaryLeft(*indexExpr);
  3429     ParseNode *maskNode = BinaryRight(*indexExpr);
  3431     uint32_t mask2;
  3432     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
  3433         // Flag the access to skip the bounds check if the mask ensures that an 'out of
  3434         // bounds' access can not occur based on the current heap length constraint.
  3435         if (mask2 == 0 ||
  3436             CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
  3437             *needsBoundsCheck = NO_BOUNDS_CHECK;
  3439         *mask &= mask2;
  3440         *indexExpr = indexNode;
  3441         return true;
  3444     return false;
  3447 static bool
  3448 CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType,
  3449                  MDefinition **def, NeedsBoundsCheck *needsBoundsCheck)
  3451     ParseNode *viewName = ElemBase(elem);
  3452     ParseNode *indexExpr = ElemIndex(elem);
  3453     *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
  3455     if (!viewName->isKind(PNK_NAME))
  3456         return f.fail(viewName, "base of array access must be a typed array view name");
  3458     const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name());
  3459     if (!global || global->which() != ModuleCompiler::Global::ArrayView)
  3460         return f.fail(viewName, "base of array access must be a typed array view name");
  3462     *viewType = global->viewType();
  3464     uint32_t pointer;
  3465     if (IsLiteralOrConstInt(f, indexExpr, &pointer)) {
  3466         if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
  3467             return f.fail(indexExpr, "constant index out of range");
  3468         pointer <<= TypedArrayShift(*viewType);
  3469         // It is adequate to note pointer+1 rather than rounding up to the next
  3470         // access-size boundary because access is always aligned and the constraint
  3471         // will be rounded up to a larger alignment later.
  3472         f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1);
  3473         *needsBoundsCheck = NO_BOUNDS_CHECK;
  3474         *def = f.constant(Int32Value(pointer), Type::Int);
  3475         return true;
  3478     // Mask off the low bits to account for the clearing effect of a right shift
  3479     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
  3480     // loses the low two bits.
  3481     int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
  3483     MDefinition *pointerDef;
  3484     if (indexExpr->isKind(PNK_RSH)) {
  3485         ParseNode *shiftNode = BinaryRight(indexExpr);
  3486         ParseNode *pointerNode = BinaryLeft(indexExpr);
  3488         uint32_t shift;
  3489         if (!IsLiteralInt(f.m(), shiftNode, &shift))
  3490             return f.failf(shiftNode, "shift amount must be constant");
  3492         unsigned requiredShift = TypedArrayShift(*viewType);
  3493         if (shift != requiredShift)
  3494             return f.failf(shiftNode, "shift amount must be %u", requiredShift);
  3496         if (pointerNode->isKind(PNK_BITAND))
  3497             FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
  3499         // Fold a 'literal constant right shifted' now, and skip the bounds check if
  3500         // currently possible. This handles the optimization of many of these uses without
  3501         // the need for range analysis, and saves the generation of a MBitAnd op.
  3502         if (IsLiteralOrConstInt(f, pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
  3503             // Cases: b[c>>n], and b[(c&m)>>n]
  3504             pointer &= mask;
  3505             if (pointer < f.m().minHeapLength())
  3506                 *needsBoundsCheck = NO_BOUNDS_CHECK;
  3507             *def = f.constant(Int32Value(pointer), Type::Int);
  3508             return true;
  3511         Type pointerType;
  3512         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
  3513             return false;
  3515         if (!pointerType.isIntish())
  3516             return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
  3517     } else {
  3518         if (TypedArrayShift(*viewType) != 0)
  3519             return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
  3521         JS_ASSERT(mask == -1);
  3522         bool folded = false;
  3524         if (indexExpr->isKind(PNK_BITAND))
  3525             folded = FoldMaskedArrayIndex(f, &indexExpr, &mask, needsBoundsCheck);
  3527         Type pointerType;
  3528         if (!CheckExpr(f, indexExpr, &pointerDef, &pointerType))
  3529             return false;
  3531         if (folded) {
  3532             if (!pointerType.isIntish())
  3533                 return f.failf(indexExpr, "%s is not a subtype of intish", pointerType.toChars());
  3534         } else {
  3535             if (!pointerType.isInt())
  3536                 return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
  3540     // Don't generate the mask op if there is no need for it which could happen for
  3541     // a shift of zero.
  3542     if (mask == -1)
  3543         *def = pointerDef;
  3544     else
  3545         *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask), Type::Int));
  3547     return true;
  3550 static bool
  3551 CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
  3553     ArrayBufferView::ViewType viewType;
  3554     MDefinition *pointerDef;
  3555     NeedsBoundsCheck needsBoundsCheck;
  3556     if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck))
  3557         return false;
  3559     *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
  3560     *type = TypedArrayLoadType(viewType);
  3561     return true;
  3564 static bool
  3565 CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
  3567     ArrayBufferView::ViewType viewType;
  3568     MDefinition *pointerDef;
  3569     NeedsBoundsCheck needsBoundsCheck;
  3570     if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
  3571         return false;
  3573     MDefinition *rhsDef;
  3574     Type rhsType;
  3575     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  3576         return false;
  3578     switch (viewType) {
  3579       case ArrayBufferView::TYPE_INT8:
  3580       case ArrayBufferView::TYPE_INT16:
  3581       case ArrayBufferView::TYPE_INT32:
  3582       case ArrayBufferView::TYPE_UINT8:
  3583       case ArrayBufferView::TYPE_UINT16:
  3584       case ArrayBufferView::TYPE_UINT32:
  3585         if (!rhsType.isIntish())
  3586             return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
  3587         break;
  3588       case ArrayBufferView::TYPE_FLOAT32:
  3589         if (rhsType.isMaybeDouble())
  3590             rhsDef = f.unary<MToFloat32>(rhsDef);
  3591         else if (!rhsType.isFloatish())
  3592             return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars());
  3593         break;
  3594       case ArrayBufferView::TYPE_FLOAT64:
  3595         if (rhsType.isFloat())
  3596             rhsDef = f.unary<MToDouble>(rhsDef);
  3597         else if (!rhsType.isMaybeDouble())
  3598             return f.failf(lhs, "%s is not a subtype of float or double?", rhsType.toChars());
  3599         break;
  3600       default:
  3601         MOZ_ASSUME_UNREACHABLE("Unexpected view type");
  3604     f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
  3606     *def = rhsDef;
  3607     *type = rhsType;
  3608     return true;
  3611 static bool
  3612 CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
  3614     Rooted<PropertyName *> name(f.cx(), lhs->name());
  3616     MDefinition *rhsDef;
  3617     Type rhsType;
  3618     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  3619         return false;
  3621     if (const FunctionCompiler::Local *lhsVar = f.lookupLocal(name)) {
  3622         if (!(rhsType <= lhsVar->type)) {
  3623             return f.failf(lhs, "%s is not a subtype of %s",
  3624                            rhsType.toChars(), lhsVar->type.toType().toChars());
  3626         f.assign(*lhsVar, rhsDef);
  3627     } else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) {
  3628         if (global->which() != ModuleCompiler::Global::Variable)
  3629             return f.failName(lhs, "'%s' is not a mutable variable", name);
  3630         if (!(rhsType <= global->varOrConstType())) {
  3631             return f.failf(lhs, "%s is not a subtype of %s",
  3632                            rhsType.toChars(), global->varOrConstType().toType().toChars());
  3634         f.storeGlobalVar(*global, rhsDef);
  3635     } else {
  3636         return f.failName(lhs, "'%s' not found in local or asm.js module scope", name);
  3639     *def = rhsDef;
  3640     *type = rhsType;
  3641     return true;
  3644 static bool
  3645 CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type)
  3647     JS_ASSERT(assign->isKind(PNK_ASSIGN));
  3648     ParseNode *lhs = BinaryLeft(assign);
  3649     ParseNode *rhs = BinaryRight(assign);
  3651     if (lhs->getKind() == PNK_ELEM)
  3652         return CheckStoreArray(f, lhs, rhs, def, type);
  3654     if (lhs->getKind() == PNK_NAME)
  3655         return CheckAssignName(f, lhs, rhs, def, type);
  3657     return f.fail(assign, "left-hand side of assignment must be a variable or array access");
  3660 static bool
  3661 CheckMathIMul(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
  3663     if (CallArgListLength(call) != 2)
  3664         return f.fail(call, "Math.imul must be passed 2 arguments");
  3666     ParseNode *lhs = CallArgList(call);
  3667     ParseNode *rhs = NextNode(lhs);
  3669     MDefinition *lhsDef;
  3670     Type lhsType;
  3671     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  3672         return false;
  3674     MDefinition *rhsDef;
  3675     Type rhsType;
  3676     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  3677         return false;
  3679     if (!lhsType.isIntish())
  3680         return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
  3681     if (!rhsType.isIntish())
  3682         return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
  3683     if (retType != RetType::Signed)
  3684         return f.failf(call, "return type is signed, used as %s", retType.toType().toChars());
  3686     *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
  3687     *type = Type::Signed;
  3688     return true;
  3691 static bool
  3692 CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
  3694     if (CallArgListLength(call) != 1)
  3695         return f.fail(call, "Math.abs must be passed 1 argument");
  3697     ParseNode *arg = CallArgList(call);
  3699     MDefinition *argDef;
  3700     Type argType;
  3701     if (!CheckExpr(f, arg, &argDef, &argType))
  3702         return false;
  3704     if (argType.isSigned()) {
  3705         if (retType != RetType::Signed)
  3706             return f.failf(call, "return type is signed, used as %s", retType.toType().toChars());
  3707         *def = f.unary<MAbs>(argDef, MIRType_Int32);
  3708         *type = Type::Signed;
  3709         return true;
  3712     if (argType.isMaybeDouble()) {
  3713         if (retType != RetType::Double)
  3714             return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
  3715         *def = f.unary<MAbs>(argDef, MIRType_Double);
  3716         *type = Type::Double;
  3717         return true;
  3720     if (argType.isMaybeFloat()) {
  3721         if (retType != RetType::Float)
  3722             return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
  3723         *def = f.unary<MAbs>(argDef, MIRType_Float32);
  3724         *type = Type::Float;
  3725         return true;
  3728     return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
  3731 static bool
  3732 CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
  3734     if (CallArgListLength(call) != 1)
  3735         return f.fail(call, "Math.sqrt must be passed 1 argument");
  3737     ParseNode *arg = CallArgList(call);
  3739     MDefinition *argDef;
  3740     Type argType;
  3741     if (!CheckExpr(f, arg, &argDef, &argType))
  3742         return false;
  3744     if (argType.isMaybeDouble()) {
  3745         if (retType != RetType::Double)
  3746             return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
  3747         *def = f.unary<MSqrt>(argDef, MIRType_Double);
  3748         *type = Type::Double;
  3749         return true;
  3752     if (argType.isMaybeFloat()) {
  3753         if (retType != RetType::Float)
  3754             return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
  3755         *def = f.unary<MSqrt>(argDef, MIRType_Float32);
  3756         *type = Type::Float;
  3757         return true;
  3760     return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
  3763 static bool
  3764 CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type, bool isMax)
  3766     if (CallArgListLength(callNode) < 2)
  3767         return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
  3769     ParseNode *firstArg = CallArgList(callNode);
  3770     MDefinition *firstDef;
  3771     Type firstType;
  3772     if (!CheckExpr(f, firstArg, &firstDef, &firstType))
  3773         return false;
  3775     bool opIsDouble = firstType.isMaybeDouble();
  3776     bool opIsInteger = firstType.isInt();
  3777     MIRType opType = firstType.toMIRType();
  3779     if (!opIsDouble && !opIsInteger)
  3780         return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars());
  3782     MDefinition *lastDef = firstDef;
  3783     ParseNode *nextArg = NextNode(firstArg);
  3784     for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
  3785         MDefinition *nextDef;
  3786         Type nextType;
  3787         if (!CheckExpr(f, nextArg, &nextDef, &nextType))
  3788             return false;
  3790         if (opIsDouble && !nextType.isMaybeDouble())
  3791             return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars());
  3792         if (opIsInteger && !nextType.isInt())
  3793             return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars());
  3795         lastDef = f.minMax(lastDef, nextDef, opType, isMax);
  3798     if (opIsDouble && retType != RetType::Double)
  3799         return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars());
  3800     if (opIsInteger && retType != RetType::Signed)
  3801         return f.failf(callNode, "return type is int, used as %s", retType.toType().toChars());
  3803     *type = opIsDouble ? Type::Double : Type::Signed;
  3804     *def = lastDef;
  3805     return true;
  3808 typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
  3810 static bool
  3811 CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, CheckArgType checkArg,
  3812               FunctionCompiler::Call *call)
  3814     f.startCallArgs(call);
  3816     ParseNode *argNode = CallArgList(callNode);
  3817     for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) {
  3818         MDefinition *def;
  3819         Type type;
  3820         if (!CheckExpr(f, argNode, &def, &type))
  3821             return false;
  3823         if (!checkArg(f, argNode, type))
  3824             return false;
  3826         if (!f.passArg(def, VarType::FromCheckedType(type), call))
  3827             return false;
  3830     f.finishCallArgs(call);
  3831     return true;
  3834 static bool
  3835 CheckSignatureAgainstExisting(ModuleCompiler &m, ParseNode *usepn, const Signature &sig,
  3836                               const Signature &existing)
  3838     if (sig.args().length() != existing.args().length()) {
  3839         return m.failf(usepn, "incompatible number of arguments (%u here vs. %u before)",
  3840                        sig.args().length(), existing.args().length());
  3843     for (unsigned i = 0; i < sig.args().length(); i++) {
  3844         if (sig.arg(i) != existing.arg(i)) {
  3845             return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)",
  3846                            i, sig.arg(i).toType().toChars(), existing.arg(i).toType().toChars());
  3850     if (sig.retType() != existing.retType()) {
  3851         return m.failf(usepn, "%s incompatible with previous return of type %s",
  3852                        sig.retType().toType().toChars(), existing.retType().toType().toChars());
  3855     JS_ASSERT(sig == existing);
  3856     return true;
  3859 static bool
  3860 CheckFunctionSignature(ModuleCompiler &m, ParseNode *usepn, Signature &&sig, PropertyName *name,
  3861                        ModuleCompiler::Func **func)
  3863     ModuleCompiler::Func *existing = m.lookupFunction(name);
  3864     if (!existing) {
  3865         if (!CheckModuleLevelName(m, usepn, name))
  3866             return false;
  3867         return m.addFunction(name, Move(sig), func);
  3870     if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig()))
  3871         return false;
  3873     *func = existing;
  3874     return true;
  3877 static bool
  3878 CheckIsVarType(FunctionCompiler &f, ParseNode *argNode, Type type)
  3880     if (!type.isVarType())
  3881         return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars());
  3882     return true;
  3885 static bool
  3886 CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, PropertyName *calleeName,
  3887                   RetType retType, MDefinition **def, Type *type)
  3889     FunctionCompiler::Call call(f, callNode, retType);
  3891     if (!CheckCallArgs(f, callNode, CheckIsVarType, &call))
  3892         return false;
  3894     ModuleCompiler::Func *callee;
  3895     if (!CheckFunctionSignature(f.m(), callNode, Move(call.sig()), calleeName, &callee))
  3896         return false;
  3898     if (!f.internalCall(*callee, call, def))
  3899         return false;
  3901     *type = retType.toType();
  3902     return true;
  3905 static bool
  3906 CheckFuncPtrTableAgainstExisting(ModuleCompiler &m, ParseNode *usepn,
  3907                                  PropertyName *name, Signature &&sig, unsigned mask,
  3908                                  ModuleCompiler::FuncPtrTable **tableOut)
  3910     if (const ModuleCompiler::Global *existing = m.lookupGlobal(name)) {
  3911         if (existing->which() != ModuleCompiler::Global::FuncPtrTable)
  3912             return m.failName(usepn, "'%s' is not a function-pointer table", name);
  3914         ModuleCompiler::FuncPtrTable &table = m.funcPtrTable(existing->funcPtrTableIndex());
  3915         if (mask != table.mask())
  3916             return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
  3918         if (!CheckSignatureAgainstExisting(m, usepn, sig, table.sig()))
  3919             return false;
  3921         *tableOut = &table;
  3922         return true;
  3925     if (!CheckModuleLevelName(m, usepn, name))
  3926         return false;
  3928     return m.addFuncPtrTable(name, Move(sig), mask, tableOut);
  3931 static bool
  3932 CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type)
  3934     ParseNode *callee = CallCallee(callNode);
  3935     ParseNode *tableNode = ElemBase(callee);
  3936     ParseNode *indexExpr = ElemIndex(callee);
  3938     if (!tableNode->isKind(PNK_NAME))
  3939         return f.fail(tableNode, "expecting name of function-pointer array");
  3941     PropertyName *name = tableNode->name();
  3942     if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) {
  3943         if (existing->which() != ModuleCompiler::Global::FuncPtrTable)
  3944             return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
  3947     if (!indexExpr->isKind(PNK_BITAND))
  3948         return f.fail(indexExpr, "function-pointer table index expression needs & mask");
  3950     ParseNode *indexNode = BinaryLeft(indexExpr);
  3951     ParseNode *maskNode = BinaryRight(indexExpr);
  3953     uint32_t mask;
  3954     if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
  3955         return f.fail(maskNode, "function-pointer table index mask value must be a power of two");
  3957     MDefinition *indexDef;
  3958     Type indexType;
  3959     if (!CheckExpr(f, indexNode, &indexDef, &indexType))
  3960         return false;
  3962     if (!indexType.isIntish())
  3963         return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars());
  3965     FunctionCompiler::Call call(f, callNode, retType);
  3967     if (!CheckCallArgs(f, callNode, CheckIsVarType, &call))
  3968         return false;
  3970     ModuleCompiler::FuncPtrTable *table;
  3971     if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(call.sig()), mask, &table))
  3972         return false;
  3974     if (!f.funcPtrCall(*table, indexDef, call, def))
  3975         return false;
  3977     *type = retType.toType();
  3978     return true;
  3981 static bool
  3982 CheckIsExternType(FunctionCompiler &f, ParseNode *argNode, Type type)
  3984     if (!type.isExtern())
  3985         return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
  3986     return true;
  3989 static bool
  3990 CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetType retType,
  3991              MDefinition **def, Type *type)
  3993     PropertyName *calleeName = CallCallee(callNode)->name();
  3995     if (retType == RetType::Float)
  3996         return f.fail(callNode, "FFI calls can't return float");
  3998     FunctionCompiler::Call call(f, callNode, retType);
  3999     if (!CheckCallArgs(f, callNode, CheckIsExternType, &call))
  4000         return false;
  4002     unsigned exitIndex;
  4003     if (!f.m().addExit(ffiIndex, calleeName, Move(call.sig()), &exitIndex))
  4004         return false;
  4006     if (!f.ffiCall(exitIndex, call, retType.toMIRType(), def))
  4007         return false;
  4009     *type = retType.toType();
  4010     return true;
  4013 static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type);
  4015 static bool
  4016 CheckFRoundArg(FunctionCompiler &f, ParseNode *arg, MDefinition **def, Type *type)
  4018     if (arg->isKind(PNK_CALL))
  4019         return CheckCall(f, arg, RetType::Float, def, type);
  4021     MDefinition *inputDef;
  4022     Type inputType;
  4023     if (!CheckExpr(f, arg, &inputDef, &inputType))
  4024         return false;
  4026     if (inputType.isMaybeDouble() || inputType.isSigned())
  4027         *def = f.unary<MToFloat32>(inputDef);
  4028     else if (inputType.isUnsigned())
  4029         *def = f.unary<MAsmJSUnsignedToFloat32>(inputDef);
  4030     else if (inputType.isFloatish())
  4031         *def = inputDef;
  4032     else
  4033         return f.failf(arg, "%s is not a subtype of signed, unsigned, double? or floatish", inputType.toChars());
  4035     *type = Type::Float;
  4036     return true;
  4039 static bool
  4040 CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type)
  4042     ParseNode *argNode = nullptr;
  4043     if (!IsFloatCoercion(f.m(), callNode, &argNode))
  4044         return f.fail(callNode, "invalid call to fround");
  4046     MDefinition *operand;
  4047     Type operandType;
  4048     if (!CheckFRoundArg(f, argNode, &operand, &operandType))
  4049         return false;
  4051     JS_ASSERT(operandType == Type::Float);
  4053     switch (retType.which()) {
  4054       case RetType::Double:
  4055         *def = f.unary<MToDouble>(operand);
  4056         *type = Type::Double;
  4057         return true;
  4058       case RetType::Signed:
  4059         *def = f.unary<MTruncateToInt32>(operand);
  4060         *type = Type::Signed;
  4061         return true;
  4062       case RetType::Float:
  4063         *def = operand;
  4064         *type = Type::Float;
  4065         return true;
  4066       case RetType::Void:
  4067         // definition and return types should be ignored by the caller
  4068         return true;
  4071     MOZ_ASSUME_UNREACHABLE("return value of fround is ignored");
  4074 static bool
  4075 CheckIsMaybeDouble(FunctionCompiler &f, ParseNode *argNode, Type type)
  4077     if (!type.isMaybeDouble())
  4078         return f.failf(argNode, "%s is not a subtype of double?", type.toChars());
  4079     return true;
  4082 static bool
  4083 CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type)
  4085     if (!type.isMaybeFloat())
  4086         return f.failf(argNode, "%s is not a subtype of float?", type.toChars());
  4087     return true;
  4090 static bool
  4091 CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
  4092                      RetType retType, MDefinition **def, Type *type)
  4094     unsigned arity = 0;
  4095     AsmJSImmKind doubleCallee, floatCallee;
  4096     switch (func) {
  4097       case AsmJSMathBuiltin_imul:   return CheckMathIMul(f, callNode, retType, def, type);
  4098       case AsmJSMathBuiltin_abs:    return CheckMathAbs(f, callNode, retType, def, type);
  4099       case AsmJSMathBuiltin_sqrt:   return CheckMathSqrt(f, callNode, retType, def, type);
  4100       case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, retType, def, type);
  4101       case AsmJSMathBuiltin_min:    return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ false);
  4102       case AsmJSMathBuiltin_max:    return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ true);
  4103       case AsmJSMathBuiltin_ceil:   arity = 1; doubleCallee = AsmJSImm_CeilD;  floatCallee = AsmJSImm_CeilF;   break;
  4104       case AsmJSMathBuiltin_floor:  arity = 1; doubleCallee = AsmJSImm_FloorD; floatCallee = AsmJSImm_FloorF;  break;
  4105       case AsmJSMathBuiltin_sin:    arity = 1; doubleCallee = AsmJSImm_SinD;   floatCallee = AsmJSImm_Invalid; break;
  4106       case AsmJSMathBuiltin_cos:    arity = 1; doubleCallee = AsmJSImm_CosD;   floatCallee = AsmJSImm_Invalid; break;
  4107       case AsmJSMathBuiltin_tan:    arity = 1; doubleCallee = AsmJSImm_TanD;   floatCallee = AsmJSImm_Invalid; break;
  4108       case AsmJSMathBuiltin_asin:   arity = 1; doubleCallee = AsmJSImm_ASinD;  floatCallee = AsmJSImm_Invalid; break;
  4109       case AsmJSMathBuiltin_acos:   arity = 1; doubleCallee = AsmJSImm_ACosD;  floatCallee = AsmJSImm_Invalid; break;
  4110       case AsmJSMathBuiltin_atan:   arity = 1; doubleCallee = AsmJSImm_ATanD;  floatCallee = AsmJSImm_Invalid; break;
  4111       case AsmJSMathBuiltin_exp:    arity = 1; doubleCallee = AsmJSImm_ExpD;   floatCallee = AsmJSImm_Invalid; break;
  4112       case AsmJSMathBuiltin_log:    arity = 1; doubleCallee = AsmJSImm_LogD;   floatCallee = AsmJSImm_Invalid; break;
  4113       case AsmJSMathBuiltin_pow:    arity = 2; doubleCallee = AsmJSImm_PowD;   floatCallee = AsmJSImm_Invalid; break;
  4114       case AsmJSMathBuiltin_atan2:  arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break;
  4115       default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin function");
  4118     if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid)
  4119         return f.fail(callNode, "math builtin cannot be used as float");
  4120     if (retType != RetType::Double && retType != RetType::Float)
  4121         return f.failf(callNode, "return type of math function is double or float, used as %s", retType.toType().toChars());
  4123     FunctionCompiler::Call call(f, callNode, retType);
  4124     if (retType == RetType::Float && !CheckCallArgs(f, callNode, CheckIsMaybeFloat, &call))
  4125         return false;
  4126     if (retType == RetType::Double && !CheckCallArgs(f, callNode, CheckIsMaybeDouble, &call))
  4127         return false;
  4129     if (call.sig().args().length() != arity)
  4130         return f.failf(callNode, "call passed %u arguments, expected %u", call.sig().args().length(), arity);
  4132     if (retType == RetType::Float && !f.builtinCall(floatCallee, call, retType.toMIRType(), def))
  4133         return false;
  4134     if (retType == RetType::Double && !f.builtinCall(doubleCallee, call, retType.toMIRType(), def))
  4135         return false;
  4137     *type = retType.toType();
  4138     return true;
  4141 static bool
  4142 CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
  4144     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
  4146     ParseNode *callee = CallCallee(call);
  4148     if (callee->isKind(PNK_ELEM))
  4149         return CheckFuncPtrCall(f, call, retType, def, type);
  4151     if (!callee->isKind(PNK_NAME))
  4152         return f.fail(callee, "unexpected callee expression type");
  4154     PropertyName *calleeName = callee->name();
  4156     if (const ModuleCompiler::Global *global = f.lookupGlobal(calleeName)) {
  4157         switch (global->which()) {
  4158           case ModuleCompiler::Global::FFI:
  4159             return CheckFFICall(f, call, global->ffiIndex(), retType, def, type);
  4160           case ModuleCompiler::Global::MathBuiltinFunction:
  4161             return CheckMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
  4162           case ModuleCompiler::Global::ConstantLiteral:
  4163           case ModuleCompiler::Global::ConstantImport:
  4164           case ModuleCompiler::Global::Variable:
  4165           case ModuleCompiler::Global::FuncPtrTable:
  4166           case ModuleCompiler::Global::ArrayView:
  4167             return f.failName(callee, "'%s' is not callable function", callee->name());
  4168           case ModuleCompiler::Global::Function:
  4169             break;
  4173     return CheckInternalCall(f, call, calleeName, retType, def, type);
  4176 static bool
  4177 CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type)
  4179     JS_ASSERT(pos->isKind(PNK_POS));
  4180     ParseNode *operand = UnaryKid(pos);
  4182     if (operand->isKind(PNK_CALL))
  4183         return CheckCall(f, operand, RetType::Double, def, type);
  4185     MDefinition *operandDef;
  4186     Type operandType;
  4187     if (!CheckExpr(f, operand, &operandDef, &operandType))
  4188         return false;
  4190     if (operandType.isMaybeFloat() || operandType.isSigned())
  4191         *def = f.unary<MToDouble>(operandDef);
  4192     else if (operandType.isUnsigned())
  4193         *def = f.unary<MAsmJSUnsignedToDouble>(operandDef);
  4194     else if (operandType.isMaybeDouble())
  4195         *def = operandDef;
  4196     else
  4197         return f.failf(operand, "%s is not a subtype of signed, unsigned, float or double?", operandType.toChars());
  4199     *type = Type::Double;
  4200     return true;
  4203 static bool
  4204 CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4206     JS_ASSERT(expr->isKind(PNK_NOT));
  4207     ParseNode *operand = UnaryKid(expr);
  4209     MDefinition *operandDef;
  4210     Type operandType;
  4211     if (!CheckExpr(f, operand, &operandDef, &operandType))
  4212         return false;
  4214     if (!operandType.isInt())
  4215         return f.failf(operand, "%s is not a subtype of int", operandType.toChars());
  4217     *def = f.unary<MNot>(operandDef);
  4218     *type = Type::Int;
  4219     return true;
  4222 static bool
  4223 CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4225     JS_ASSERT(expr->isKind(PNK_NEG));
  4226     ParseNode *operand = UnaryKid(expr);
  4228     MDefinition *operandDef;
  4229     Type operandType;
  4230     if (!CheckExpr(f, operand, &operandDef, &operandType))
  4231         return false;
  4233     if (operandType.isInt()) {
  4234         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Int32);
  4235         *type = Type::Intish;
  4236         return true;
  4239     if (operandType.isMaybeDouble()) {
  4240         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double);
  4241         *type = Type::Double;
  4242         return true;
  4245     if (operandType.isMaybeFloat()) {
  4246         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Float32);
  4247         *type = Type::Floatish;
  4248         return true;
  4251     return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
  4254 static bool
  4255 CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4257     JS_ASSERT(expr->isKind(PNK_BITNOT));
  4258     ParseNode *operand = UnaryKid(expr);
  4260     MDefinition *operandDef;
  4261     Type operandType;
  4262     if (!CheckExpr(f, operand, &operandDef, &operandType))
  4263         return false;
  4265     if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
  4266         *def = f.unary<MTruncateToInt32>(operandDef);
  4267         *type = Type::Signed;
  4268         return true;
  4271     if (!operandType.isIntish())
  4272         return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
  4274     *def = operandDef;
  4275     *type = Type::Signed;
  4276     return true;
  4279 static bool
  4280 CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type)
  4282     JS_ASSERT(neg->isKind(PNK_BITNOT));
  4283     ParseNode *operand = UnaryKid(neg);
  4285     if (operand->isKind(PNK_BITNOT))
  4286         return CheckCoerceToInt(f, operand, def, type);
  4288     MDefinition *operandDef;
  4289     Type operandType;
  4290     if (!CheckExpr(f, operand, &operandDef, &operandType))
  4291         return false;
  4293     if (!operandType.isIntish())
  4294         return f.failf(operand, "%s is not a subtype of intish", operandType.toChars());
  4296     *def = f.bitwise<MBitNot>(operandDef);
  4297     *type = Type::Signed;
  4298     return true;
  4301 static bool
  4302 CheckComma(FunctionCompiler &f, ParseNode *comma, MDefinition **def, Type *type)
  4304     JS_ASSERT(comma->isKind(PNK_COMMA));
  4305     ParseNode *operands = ListHead(comma);
  4307     ParseNode *pn = operands;
  4308     for (; NextNode(pn); pn = NextNode(pn)) {
  4309         MDefinition *_1;
  4310         Type _2;
  4311         if (pn->isKind(PNK_CALL)) {
  4312             if (!CheckCall(f, pn, RetType::Void, &_1, &_2))
  4313                 return false;
  4314         } else {
  4315             if (!CheckExpr(f, pn, &_1, &_2))
  4316                 return false;
  4320     if (!CheckExpr(f, pn, def, type))
  4321         return false;
  4323     return true;
  4326 static bool
  4327 CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Type *type)
  4329     JS_ASSERT(ternary->isKind(PNK_CONDITIONAL));
  4330     ParseNode *cond = TernaryKid1(ternary);
  4331     ParseNode *thenExpr = TernaryKid2(ternary);
  4332     ParseNode *elseExpr = TernaryKid3(ternary);
  4334     MDefinition *condDef;
  4335     Type condType;
  4336     if (!CheckExpr(f, cond, &condDef, &condType))
  4337         return false;
  4339     if (!condType.isInt())
  4340         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
  4342     MBasicBlock *thenBlock = nullptr, *elseBlock = nullptr;
  4343     if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock, thenExpr, elseExpr))
  4344         return false;
  4346     MDefinition *thenDef;
  4347     Type thenType;
  4348     if (!CheckExpr(f, thenExpr, &thenDef, &thenType))
  4349         return false;
  4351     BlockVector thenBlocks(f.cx());
  4352     if (!f.appendThenBlock(&thenBlocks))
  4353         return false;
  4355     f.pushPhiInput(thenDef);
  4356     f.switchToElse(elseBlock);
  4358     MDefinition *elseDef;
  4359     Type elseType;
  4360     if (!CheckExpr(f, elseExpr, &elseDef, &elseType))
  4361         return false;
  4363     f.pushPhiInput(elseDef);
  4365     if (thenType.isInt() && elseType.isInt()) {
  4366         *type = Type::Int;
  4367     } else if (thenType.isDouble() && elseType.isDouble()) {
  4368         *type = Type::Double;
  4369     } else if (thenType.isFloat() && elseType.isFloat()) {
  4370         *type = Type::Float;
  4371     } else {
  4372         return f.failf(ternary, "then/else branches of conditional must both produce int or double, "
  4373                        "current types are %s and %s", thenType.toChars(), elseType.toChars());
  4376     if (!f.joinIfElse(thenBlocks, elseExpr))
  4377         return false;
  4379     *def = f.popPhiOutput();
  4380     return true;
  4383 static bool
  4384 IsValidIntMultiplyConstant(ModuleCompiler &m, ParseNode *expr)
  4386     if (!IsNumericLiteral(m, expr))
  4387         return false;
  4389     NumLit literal = ExtractNumericLiteral(m, expr);
  4390     switch (literal.which()) {
  4391       case NumLit::Fixnum:
  4392       case NumLit::NegativeInt:
  4393         if (abs(literal.toInt32()) < (1<<20))
  4394             return true;
  4395         return false;
  4396       case NumLit::BigUnsigned:
  4397       case NumLit::Double:
  4398       case NumLit::Float:
  4399       case NumLit::OutOfRangeInt:
  4400         return false;
  4403     MOZ_ASSUME_UNREACHABLE("Bad literal");
  4406 static bool
  4407 CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type)
  4409     JS_ASSERT(star->isKind(PNK_STAR));
  4410     ParseNode *lhs = BinaryLeft(star);
  4411     ParseNode *rhs = BinaryRight(star);
  4413     MDefinition *lhsDef;
  4414     Type lhsType;
  4415     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  4416         return false;
  4418     MDefinition *rhsDef;
  4419     Type rhsType;
  4420     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  4421         return false;
  4423     if (lhsType.isInt() && rhsType.isInt()) {
  4424         if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
  4425             return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
  4426         *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
  4427         *type = Type::Intish;
  4428         return true;
  4431     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
  4432         *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
  4433         *type = Type::Double;
  4434         return true;
  4437     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
  4438         *def = f.mul(lhsDef, rhsDef, MIRType_Float32, MMul::Normal);
  4439         *type = Type::Floatish;
  4440         return true;
  4443     return f.fail(star, "multiply operands must be both int, both double? or both float?");
  4446 static bool
  4447 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type,
  4448               unsigned *numAddOrSubOut = nullptr)
  4450     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
  4452     JS_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
  4453     ParseNode *lhs = BinaryLeft(expr);
  4454     ParseNode *rhs = BinaryRight(expr);
  4456     MDefinition *lhsDef, *rhsDef;
  4457     Type lhsType, rhsType;
  4458     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
  4460     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
  4461         if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub))
  4462             return false;
  4463         if (lhsType == Type::Intish)
  4464             lhsType = Type::Int;
  4465     } else {
  4466         if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  4467             return false;
  4468         lhsNumAddOrSub = 0;
  4471     if (rhs->isKind(PNK_ADD) || rhs->isKind(PNK_SUB)) {
  4472         if (!CheckAddOrSub(f, rhs, &rhsDef, &rhsType, &rhsNumAddOrSub))
  4473             return false;
  4474         if (rhsType == Type::Intish)
  4475             rhsType = Type::Int;
  4476     } else {
  4477         if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  4478             return false;
  4479         rhsNumAddOrSub = 0;
  4482     unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
  4483     if (numAddOrSub > (1<<20))
  4484         return f.fail(expr, "too many + or - without intervening coercion");
  4486     if (lhsType.isInt() && rhsType.isInt()) {
  4487         *def = expr->isKind(PNK_ADD)
  4488                ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
  4489                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
  4490         *type = Type::Intish;
  4491     } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
  4492         *def = expr->isKind(PNK_ADD)
  4493                ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
  4494                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
  4495         *type = Type::Double;
  4496     } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
  4497         *def = expr->isKind(PNK_ADD)
  4498                ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Float32)
  4499                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Float32);
  4500         *type = Type::Floatish;
  4501     } else {
  4502         return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
  4503                        lhsType.toChars(), rhsType.toChars());
  4506     if (numAddOrSubOut)
  4507         *numAddOrSubOut = numAddOrSub;
  4508     return true;
  4511 static bool
  4512 CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4514     JS_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
  4515     ParseNode *lhs = BinaryLeft(expr);
  4516     ParseNode *rhs = BinaryRight(expr);
  4518     MDefinition *lhsDef, *rhsDef;
  4519     Type lhsType, rhsType;
  4520     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  4521         return false;
  4522     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  4523         return false;
  4525     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
  4526         *def = expr->isKind(PNK_DIV)
  4527                ? f.div(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false)
  4528                : f.mod(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false);
  4529         *type = Type::Double;
  4530         return true;
  4533     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
  4534         if (expr->isKind(PNK_DIV))
  4535             *def = f.div(lhsDef, rhsDef, MIRType_Float32, /* unsignd = */ false);
  4536         else
  4537             return f.fail(expr, "modulo cannot receive float arguments");
  4538         *type = Type::Floatish;
  4539         return true;
  4542     if (lhsType.isSigned() && rhsType.isSigned()) {
  4543         if (expr->isKind(PNK_DIV))
  4544             *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false);
  4545         else
  4546             *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false);
  4547         *type = Type::Intish;
  4548         return true;
  4551     if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
  4552         if (expr->isKind(PNK_DIV))
  4553             *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true);
  4554         else
  4555             *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true);
  4556         *type = Type::Intish;
  4557         return true;
  4560     return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
  4561                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
  4564 static bool
  4565 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
  4567     JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
  4568               comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
  4569     ParseNode *lhs = BinaryLeft(comp);
  4570     ParseNode *rhs = BinaryRight(comp);
  4572     MDefinition *lhsDef, *rhsDef;
  4573     Type lhsType, rhsType;
  4574     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  4575         return false;
  4576     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  4577         return false;
  4579     if ((lhsType.isSigned() && rhsType.isSigned()) || (lhsType.isUnsigned() && rhsType.isUnsigned())) {
  4580         MCompare::CompareType compareType = (lhsType.isUnsigned() && rhsType.isUnsigned())
  4581                                             ? MCompare::Compare_UInt32
  4582                                             : MCompare::Compare_Int32;
  4583         *def = f.compare(lhsDef, rhsDef, comp->getOp(), compareType);
  4584         *type = Type::Int;
  4585         return true;
  4588     if (lhsType.isDouble() && rhsType.isDouble()) {
  4589         *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double);
  4590         *type = Type::Int;
  4591         return true;
  4594     if (lhsType.isFloat() && rhsType.isFloat()) {
  4595         *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Float32);
  4596         *type = Type::Int;
  4597         return true;
  4600     return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
  4601                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
  4604 static bool
  4605 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
  4607     ParseNode *lhs = BinaryLeft(bitwise);
  4608     ParseNode *rhs = BinaryRight(bitwise);
  4610     int32_t identityElement;
  4611     bool onlyOnRight;
  4612     switch (bitwise->getKind()) {
  4613       case PNK_BITOR:  identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
  4614       case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed;   break;
  4615       case PNK_BITXOR: identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
  4616       case PNK_LSH:    identityElement = 0;  onlyOnRight = true;  *type = Type::Signed;   break;
  4617       case PNK_RSH:    identityElement = 0;  onlyOnRight = true;  *type = Type::Signed;   break;
  4618       case PNK_URSH:   identityElement = 0;  onlyOnRight = true;  *type = Type::Unsigned; break;
  4619       default: MOZ_ASSUME_UNREACHABLE("not a bitwise op");
  4622     uint32_t i;
  4623     if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) && i == uint32_t(identityElement)) {
  4624         Type rhsType;
  4625         if (!CheckExpr(f, rhs, def, &rhsType))
  4626             return false;
  4627         if (!rhsType.isIntish())
  4628             return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars());
  4629         return true;
  4632     if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) {
  4633         if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL))
  4634             return CheckCall(f, lhs, RetType::Signed, def, type);
  4636         Type lhsType;
  4637         if (!CheckExpr(f, lhs, def, &lhsType))
  4638             return false;
  4639         if (!lhsType.isIntish())
  4640             return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars());
  4641         return true;
  4644     MDefinition *lhsDef;
  4645     Type lhsType;
  4646     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
  4647         return false;
  4649     MDefinition *rhsDef;
  4650     Type rhsType;
  4651     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
  4652         return false;
  4654     if (!lhsType.isIntish())
  4655         return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
  4656     if (!rhsType.isIntish())
  4657         return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
  4659     switch (bitwise->getKind()) {
  4660       case PNK_BITOR:  *def = f.bitwise<MBitOr>(lhsDef, rhsDef); break;
  4661       case PNK_BITAND: *def = f.bitwise<MBitAnd>(lhsDef, rhsDef); break;
  4662       case PNK_BITXOR: *def = f.bitwise<MBitXor>(lhsDef, rhsDef); break;
  4663       case PNK_LSH:    *def = f.bitwise<MLsh>(lhsDef, rhsDef); break;
  4664       case PNK_RSH:    *def = f.bitwise<MRsh>(lhsDef, rhsDef); break;
  4665       case PNK_URSH:   *def = f.bitwise<MUrsh>(lhsDef, rhsDef); break;
  4666       default: MOZ_ASSUME_UNREACHABLE("not a bitwise op");
  4669     return true;
  4672 static bool
  4673 CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4675     JS_ASSERT(expr->isKind(PNK_CALL));
  4677     ParseNode *arg;
  4678     if (!IsFloatCoercion(f.m(), expr, &arg)) {
  4679         return f.fail(expr, "all function calls must either be ignored (via f(); or "
  4680                             "comma-expression), coerced to signed (via f()|0), coerced to float "
  4681                             "(via fround(f())) or coerced to double (via +f())");
  4684     return CheckFRoundArg(f, arg, def, type);
  4687 static bool
  4688 CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
  4690     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
  4692     if (!f.mirGen().ensureBallast())
  4693         return false;
  4695     if (IsNumericLiteral(f.m(), expr))
  4696         return CheckNumericLiteral(f, expr, def, type);
  4698     switch (expr->getKind()) {
  4699       case PNK_NAME:        return CheckVarRef(f, expr, def, type);
  4700       case PNK_ELEM:        return CheckLoadArray(f, expr, def, type);
  4701       case PNK_ASSIGN:      return CheckAssign(f, expr, def, type);
  4702       case PNK_POS:         return CheckPos(f, expr, def, type);
  4703       case PNK_NOT:         return CheckNot(f, expr, def, type);
  4704       case PNK_NEG:         return CheckNeg(f, expr, def, type);
  4705       case PNK_BITNOT:      return CheckBitNot(f, expr, def, type);
  4706       case PNK_COMMA:       return CheckComma(f, expr, def, type);
  4707       case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type);
  4708       case PNK_STAR:        return CheckMultiply(f, expr, def, type);
  4709       case PNK_CALL:        return CheckUncoercedCall(f, expr, def, type);
  4711       case PNK_ADD:
  4712       case PNK_SUB:         return CheckAddOrSub(f, expr, def, type);
  4714       case PNK_DIV:
  4715       case PNK_MOD:         return CheckDivOrMod(f, expr, def, type);
  4717       case PNK_LT:
  4718       case PNK_LE:
  4719       case PNK_GT:
  4720       case PNK_GE:
  4721       case PNK_EQ:
  4722       case PNK_NE:          return CheckComparison(f, expr, def, type);
  4724       case PNK_BITOR:
  4725       case PNK_BITAND:
  4726       case PNK_BITXOR:
  4727       case PNK_LSH:
  4728       case PNK_RSH:
  4729       case PNK_URSH:        return CheckBitwise(f, expr, def, type);
  4731       default:;
  4734     return f.fail(expr, "unsupported expression");
  4737 static bool
  4738 CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels = nullptr);
  4740 static bool
  4741 CheckExprStatement(FunctionCompiler &f, ParseNode *exprStmt)
  4743     JS_ASSERT(exprStmt->isKind(PNK_SEMI));
  4744     ParseNode *expr = UnaryKid(exprStmt);
  4746     if (!expr)
  4747         return true;
  4749     MDefinition *_1;
  4750     Type _2;
  4752     if (expr->isKind(PNK_CALL))
  4753         return CheckCall(f, expr, RetType::Void, &_1, &_2);
  4755     return CheckExpr(f, UnaryKid(exprStmt), &_1, &_2);
  4758 static bool
  4759 CheckWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels)
  4761     JS_ASSERT(whileStmt->isKind(PNK_WHILE));
  4762     ParseNode *cond = BinaryLeft(whileStmt);
  4763     ParseNode *body = BinaryRight(whileStmt);
  4765     MBasicBlock *loopEntry;
  4766     if (!f.startPendingLoop(whileStmt, &loopEntry, body))
  4767         return false;
  4769     MDefinition *condDef;
  4770     Type condType;
  4771     if (!CheckExpr(f, cond, &condDef, &condType))
  4772         return false;
  4774     if (!condType.isInt())
  4775         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
  4777     MBasicBlock *afterLoop;
  4778     if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(whileStmt)))
  4779         return false;
  4781     if (!CheckStatement(f, body))
  4782         return false;
  4784     if (!f.bindContinues(whileStmt, maybeLabels))
  4785         return false;
  4787     return f.closeLoop(loopEntry, afterLoop);
  4790 static bool
  4791 CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels)
  4793     JS_ASSERT(forStmt->isKind(PNK_FOR));
  4794     ParseNode *forHead = BinaryLeft(forStmt);
  4795     ParseNode *body = BinaryRight(forStmt);
  4797     if (!forHead->isKind(PNK_FORHEAD))
  4798         return f.fail(forHead, "unsupported for-loop statement");
  4800     ParseNode *maybeInit = TernaryKid1(forHead);
  4801     ParseNode *maybeCond = TernaryKid2(forHead);
  4802     ParseNode *maybeInc = TernaryKid3(forHead);
  4804     if (maybeInit) {
  4805         MDefinition *_1;
  4806         Type _2;
  4807         if (!CheckExpr(f, maybeInit, &_1, &_2))
  4808             return false;
  4811     MBasicBlock *loopEntry;
  4812     if (!f.startPendingLoop(forStmt, &loopEntry, body))
  4813         return false;
  4815     MDefinition *condDef;
  4816     if (maybeCond) {
  4817         Type condType;
  4818         if (!CheckExpr(f, maybeCond, &condDef, &condType))
  4819             return false;
  4821         if (!condType.isInt())
  4822             return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars());
  4823     } else {
  4824         condDef = f.constant(Int32Value(1), Type::Int);
  4827     MBasicBlock *afterLoop;
  4828     if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(forStmt)))
  4829         return false;
  4831     if (!CheckStatement(f, body))
  4832         return false;
  4834     if (!f.bindContinues(forStmt, maybeLabels))
  4835         return false;
  4837     if (maybeInc) {
  4838         MDefinition *_1;
  4839         Type _2;
  4840         if (!CheckExpr(f, maybeInc, &_1, &_2))
  4841             return false;
  4844     return f.closeLoop(loopEntry, afterLoop);
  4847 static bool
  4848 CheckDoWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels)
  4850     JS_ASSERT(whileStmt->isKind(PNK_DOWHILE));
  4851     ParseNode *body = BinaryLeft(whileStmt);
  4852     ParseNode *cond = BinaryRight(whileStmt);
  4854     MBasicBlock *loopEntry;
  4855     if (!f.startPendingLoop(whileStmt, &loopEntry, body))
  4856         return false;
  4858     if (!CheckStatement(f, body))
  4859         return false;
  4861     if (!f.bindContinues(whileStmt, maybeLabels))
  4862         return false;
  4864     MDefinition *condDef;
  4865     Type condType;
  4866     if (!CheckExpr(f, cond, &condDef, &condType))
  4867         return false;
  4869     if (!condType.isInt())
  4870         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
  4872     return f.branchAndCloseDoWhileLoop(condDef, loopEntry, NextNode(whileStmt));
  4875 static bool
  4876 CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels)
  4878     JS_ASSERT(labeledStmt->isKind(PNK_LABEL));
  4879     PropertyName *label = LabeledStatementLabel(labeledStmt);
  4880     ParseNode *stmt = LabeledStatementStatement(labeledStmt);
  4882     if (maybeLabels) {
  4883         if (!maybeLabels->append(label))
  4884             return false;
  4885         if (!CheckStatement(f, stmt, maybeLabels))
  4886             return false;
  4887         return true;
  4890     LabelVector labels(f.cx());
  4891     if (!labels.append(label))
  4892         return false;
  4894     if (!CheckStatement(f, stmt, &labels))
  4895         return false;
  4897     return f.bindLabeledBreaks(&labels, labeledStmt);
  4900 static bool
  4901 CheckLeafCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt, ParseNode *elseOrJoinStmt,
  4902                    MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock)
  4904     MDefinition *condDef;
  4905     Type condType;
  4906     if (!CheckExpr(f, cond, &condDef, &condType))
  4907         return false;
  4908     if (!condType.isInt())
  4909         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
  4911     if (!f.branchAndStartThen(condDef, thenBlock, elseOrJoinBlock, thenStmt, elseOrJoinStmt))
  4912         return false;
  4913     return true;
  4916 static bool
  4917 CheckIfCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt, ParseNode *elseOrJoinStmt,
  4918                  MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock);
  4920 static bool
  4921 CheckIfConditional(FunctionCompiler &f, ParseNode *conditional, ParseNode *thenStmt, ParseNode *elseOrJoinStmt,
  4922                    MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock)
  4924     JS_ASSERT(conditional->isKind(PNK_CONDITIONAL));
  4926     // a ? b : c <=> (a && b) || (!a && c)
  4927     // b is always referred to the AND condition, as we need A and B to reach this test,
  4928     // c is always referred as the OR condition, as we reach it if we don't have A.
  4929     ParseNode *cond = TernaryKid1(conditional);
  4930     ParseNode *lhs = TernaryKid2(conditional);
  4931     ParseNode *rhs = TernaryKid3(conditional);
  4933     MBasicBlock *maybeAndTest = nullptr, *maybeOrTest = nullptr;
  4934     MBasicBlock **ifTrueBlock = &maybeAndTest, **ifFalseBlock = &maybeOrTest;
  4935     ParseNode *ifTrueBlockNode = lhs, *ifFalseBlockNode = rhs;
  4937     // Try to spot opportunities for short-circuiting in the AND subpart
  4938     uint32_t andTestLiteral = 0;
  4939     bool skipAndTest = false;
  4941     if (IsLiteralInt(f.m(), lhs, &andTestLiteral)) {
  4942         skipAndTest = true;
  4943         if (andTestLiteral == 0) {
  4944             // (a ? 0 : b) is equivalent to !a && b
  4945             // If a is true, jump to the elseBlock directly
  4946             ifTrueBlock = elseOrJoinBlock;
  4947             ifTrueBlockNode = elseOrJoinStmt;
  4948         } else {
  4949             // (a ? 1 : b) is equivalent to a || b
  4950             // If a is true, jump to the thenBlock directly
  4951             ifTrueBlock = thenBlock;
  4952             ifTrueBlockNode = thenStmt;
  4956     // Try to spot opportunities for short-circuiting in the OR subpart
  4957     uint32_t orTestLiteral = 0;
  4958     bool skipOrTest = false;
  4960     if (IsLiteralInt(f.m(), rhs, &orTestLiteral)) {
  4961         skipOrTest = true;
  4962         if (orTestLiteral == 0) {
  4963             // (a ? b : 0) is equivalent to a && b
  4964             // If a is false, jump to the elseBlock directly
  4965             ifFalseBlock = elseOrJoinBlock;
  4966             ifFalseBlockNode = elseOrJoinStmt;
  4967         } else {
  4968             // (a ? b : 1) is equivalent to !a || b
  4969             // If a is false, jump to the thenBlock directly
  4970             ifFalseBlock = thenBlock;
  4971             ifFalseBlockNode = thenStmt;
  4975     // Pathological cases: a ? 0 : 0 (i.e. false) or a ? 1 : 1 (i.e. true)
  4976     // These cases can't be optimized properly at this point: one of the blocks might be
  4977     // created and won't ever be executed. Furthermore, it introduces inconsistencies in the
  4978     // MIR graph (even if we try to create a block by hand, it will have no predecessor, which
  4979     // breaks graph assumptions). The only way we could optimize it is to do it directly in
  4980     // CheckIf by removing the control flow entirely.
  4981     if (skipOrTest && skipAndTest && (!!orTestLiteral == !!andTestLiteral))
  4982         return CheckLeafCondition(f, conditional, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock);
  4984     if (!CheckIfCondition(f, cond, ifTrueBlockNode, ifFalseBlockNode, ifTrueBlock, ifFalseBlock))
  4985         return false;
  4986     f.assertCurrentBlockIs(*ifTrueBlock);
  4988     // Add supplementary tests, if needed
  4989     if (!skipAndTest) {
  4990         if (!CheckIfCondition(f, lhs, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock))
  4991             return false;
  4992         f.assertCurrentBlockIs(*thenBlock);
  4995     if (!skipOrTest) {
  4996         f.switchToElse(*ifFalseBlock);
  4997         if (!CheckIfCondition(f, rhs, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock))
  4998             return false;
  4999         f.assertCurrentBlockIs(*thenBlock);
  5002     // We might not be on the thenBlock in one case
  5003     if (ifTrueBlock == elseOrJoinBlock) {
  5004         JS_ASSERT(skipAndTest && andTestLiteral == 0);
  5005         f.switchToElse(*thenBlock);
  5008     // Check post-conditions
  5009     f.assertCurrentBlockIs(*thenBlock);
  5010     JS_ASSERT_IF(!f.inDeadCode(), *thenBlock && *elseOrJoinBlock);
  5011     return true;
  5014 /*
  5015  * Recursive function that checks for a complex condition (formed with ternary
  5016  * conditionals) and creates the associated short-circuiting control flow graph.
  5018  * After a call to CheckCondition, the followings are true:
  5019  * - if *thenBlock and *elseOrJoinBlock were non-null on entry, their value is
  5020  *   not changed by this function.
  5021  * - *thenBlock and *elseOrJoinBlock are non-null on exit.
  5022  * - the current block on exit is the *thenBlock.
  5023  */
  5024 static bool
  5025 CheckIfCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt,
  5026                  ParseNode *elseOrJoinStmt, MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock)
  5028     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
  5030     if (cond->isKind(PNK_CONDITIONAL))
  5031         return CheckIfConditional(f, cond, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock);
  5033     // We've reached a leaf, i.e. an atomic condition
  5034     JS_ASSERT(!cond->isKind(PNK_CONDITIONAL));
  5035     if (!CheckLeafCondition(f, cond, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock))
  5036         return false;
  5038     // Check post-conditions
  5039     f.assertCurrentBlockIs(*thenBlock);
  5040     JS_ASSERT_IF(!f.inDeadCode(), *thenBlock && *elseOrJoinBlock);
  5041     return true;
  5044 static bool
  5045 CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
  5047     // Handle if/else-if chains using iteration instead of recursion. This
  5048     // avoids blowing the C stack quota for long if/else-if chains and also
  5049     // creates fewer MBasicBlocks at join points (by creating one join block
  5050     // for the entire if/else-if chain).
  5051     BlockVector thenBlocks(f.cx());
  5053     ParseNode *nextStmt = NextNode(ifStmt);
  5054   recurse:
  5055     JS_ASSERT(ifStmt->isKind(PNK_IF));
  5056     ParseNode *cond = TernaryKid1(ifStmt);
  5057     ParseNode *thenStmt = TernaryKid2(ifStmt);
  5058     ParseNode *elseStmt = TernaryKid3(ifStmt);
  5060     MBasicBlock *thenBlock = nullptr, *elseBlock = nullptr;
  5061     ParseNode *elseOrJoinStmt = elseStmt ? elseStmt : nextStmt;
  5063     if (!CheckIfCondition(f, cond, thenStmt, elseOrJoinStmt, &thenBlock, &elseBlock))
  5064         return false;
  5066     if (!CheckStatement(f, thenStmt))
  5067         return false;
  5069     if (!f.appendThenBlock(&thenBlocks))
  5070         return false;
  5072     if (!elseStmt) {
  5073         if (!f.joinIf(thenBlocks, elseBlock))
  5074             return false;
  5075     } else {
  5076         f.switchToElse(elseBlock);
  5078         if (elseStmt->isKind(PNK_IF)) {
  5079             ifStmt = elseStmt;
  5080             goto recurse;
  5083         if (!CheckStatement(f, elseStmt))
  5084             return false;
  5086         if (!f.joinIfElse(thenBlocks, nextStmt))
  5087             return false;
  5090     return true;
  5093 static bool
  5094 CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value)
  5096     if (!IsNumericLiteral(f.m(), caseExpr))
  5097         return f.fail(caseExpr, "switch case expression must be an integer literal");
  5099     NumLit literal = ExtractNumericLiteral(f.m(), caseExpr);
  5100     switch (literal.which()) {
  5101       case NumLit::Fixnum:
  5102       case NumLit::NegativeInt:
  5103         *value = literal.toInt32();
  5104         break;
  5105       case NumLit::OutOfRangeInt:
  5106       case NumLit::BigUnsigned:
  5107         return f.fail(caseExpr, "switch case expression out of integer range");
  5108       case NumLit::Double:
  5109       case NumLit::Float:
  5110         return f.fail(caseExpr, "switch case expression must be an integer literal");
  5113     return true;
  5116 static bool
  5117 CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt)
  5119     for (; stmt; stmt = NextNode(stmt)) {
  5120         JS_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT));
  5121         if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != nullptr)
  5122             return f.fail(stmt, "default label must be at the end");
  5125     return true;
  5128 static bool
  5129 CheckSwitchRange(FunctionCompiler &f, ParseNode *stmt, int32_t *low, int32_t *high,
  5130                  int32_t *tableLength)
  5132     if (stmt->isKind(PNK_DEFAULT)) {
  5133         *low = 0;
  5134         *high = -1;
  5135         *tableLength = 0;
  5136         return true;
  5139     int32_t i = 0;
  5140     if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
  5141         return false;
  5143     *low = *high = i;
  5145     ParseNode *initialStmt = stmt;
  5146     for (stmt = NextNode(stmt); stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
  5147         int32_t i = 0;
  5148         if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
  5149             return false;
  5151         *low = Min(*low, i);
  5152         *high = Max(*high, i);
  5155     int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
  5156     if (i64 > 4*1024*1024)
  5157         return f.fail(initialStmt, "all switch statements generate tables; this table would be too big");
  5159     *tableLength = int32_t(i64);
  5160     return true;
  5163 static bool
  5164 CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
  5166     JS_ASSERT(switchStmt->isKind(PNK_SWITCH));
  5167     ParseNode *switchExpr = BinaryLeft(switchStmt);
  5168     ParseNode *switchBody = BinaryRight(switchStmt);
  5170     if (!switchBody->isKind(PNK_STATEMENTLIST))
  5171         return f.fail(switchBody, "switch body may not contain 'let' declarations");
  5173     MDefinition *exprDef;
  5174     Type exprType;
  5175     if (!CheckExpr(f, switchExpr, &exprDef, &exprType))
  5176         return false;
  5178     if (!exprType.isSigned())
  5179         return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
  5181     ParseNode *stmt = ListHead(switchBody);
  5183     if (!CheckDefaultAtEnd(f, stmt))
  5184         return false;
  5186     if (!stmt)
  5187         return true;
  5189     int32_t low = 0, high = 0, tableLength = 0;
  5190     if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
  5191         return false;
  5193     BlockVector cases(f.cx());
  5194     if (!cases.resize(tableLength))
  5195         return false;
  5197     MBasicBlock *switchBlock;
  5198     if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock))
  5199         return false;
  5201     for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
  5202         int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32();
  5203         unsigned caseIndex = caseValue - low;
  5205         if (cases[caseIndex])
  5206             return f.fail(stmt, "no duplicate case labels");
  5208         if (!f.startSwitchCase(switchBlock, &cases[caseIndex], stmt))
  5209             return false;
  5211         if (!CheckStatement(f, CaseBody(stmt)))
  5212             return false;
  5215     MBasicBlock *defaultBlock;
  5216     if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock, stmt))
  5217         return false;
  5219     if (stmt && stmt->isKind(PNK_DEFAULT)) {
  5220         if (!CheckStatement(f, CaseBody(stmt)))
  5221             return false;
  5224     return f.joinSwitch(switchBlock, cases, defaultBlock);
  5227 static bool
  5228 CheckReturnType(FunctionCompiler &f, ParseNode *usepn, RetType retType)
  5230     if (!f.hasAlreadyReturned()) {
  5231         f.setReturnedType(retType);
  5232         return true;
  5235     if (f.returnedType() != retType) {
  5236         return f.failf(usepn, "%s incompatible with previous return of type %s",
  5237                        retType.toType().toChars(), f.returnedType().toType().toChars());
  5240     return true;
  5243 static bool
  5244 CheckReturn(FunctionCompiler &f, ParseNode *returnStmt)
  5246     ParseNode *expr = ReturnExpr(returnStmt);
  5248     if (!expr) {
  5249         if (!CheckReturnType(f, returnStmt, RetType::Void))
  5250             return false;
  5252         f.returnVoid();
  5253         return true;
  5256     MDefinition *def;
  5257     Type type;
  5258     if (!CheckExpr(f, expr, &def, &type))
  5259         return false;
  5261     RetType retType;
  5262     if (type.isSigned())
  5263         retType = RetType::Signed;
  5264     else if (type.isDouble())
  5265         retType = RetType::Double;
  5266     else if (type.isFloat())
  5267         retType = RetType::Float;
  5268     else if (type.isVoid())
  5269         retType = RetType::Void;
  5270     else
  5271         return f.failf(expr, "%s is not a valid return type", type.toChars());
  5273     if (!CheckReturnType(f, expr, retType))
  5274         return false;
  5276     if (retType == RetType::Void)
  5277         f.returnVoid();
  5278     else
  5279         f.returnExpr(def);
  5280     return true;
  5283 static bool
  5284 CheckStatementList(FunctionCompiler &f, ParseNode *stmtList)
  5286     JS_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
  5288     for (ParseNode *stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
  5289         if (!CheckStatement(f, stmt))
  5290             return false;
  5293     return true;
  5296 static bool
  5297 CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels)
  5299     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
  5301     if (!f.mirGen().ensureBallast())
  5302         return false;
  5304     switch (stmt->getKind()) {
  5305       case PNK_SEMI:          return CheckExprStatement(f, stmt);
  5306       case PNK_WHILE:         return CheckWhile(f, stmt, maybeLabels);
  5307       case PNK_FOR:           return CheckFor(f, stmt, maybeLabels);
  5308       case PNK_DOWHILE:       return CheckDoWhile(f, stmt, maybeLabels);
  5309       case PNK_LABEL:         return CheckLabel(f, stmt, maybeLabels);
  5310       case PNK_IF:            return CheckIf(f, stmt);
  5311       case PNK_SWITCH:        return CheckSwitch(f, stmt);
  5312       case PNK_RETURN:        return CheckReturn(f, stmt);
  5313       case PNK_STATEMENTLIST: return CheckStatementList(f, stmt);
  5314       case PNK_BREAK:         return f.addBreak(LoopControlMaybeLabel(stmt));
  5315       case PNK_CONTINUE:      return f.addContinue(LoopControlMaybeLabel(stmt));
  5316       default:;
  5319     return f.fail(stmt, "unexpected statement kind");
  5322 static bool
  5323 ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
  5325     TokenStream &tokenStream = m.tokenStream();
  5327     DebugOnly<TokenKind> tk = tokenStream.getToken();
  5328     JS_ASSERT(tk == TOK_FUNCTION);
  5330     RootedPropertyName name(m.cx());
  5332     TokenKind tt = tokenStream.getToken();
  5333     if (tt == TOK_NAME) {
  5334         name = tokenStream.currentName();
  5335     } else if (tt == TOK_YIELD) {
  5336         if (!m.parser().checkYieldNameValidity())
  5337             return false;
  5338         name = m.cx()->names().yield;
  5339     } else {
  5340         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
  5343     ParseNode *fn = m.parser().handler.newFunctionDefinition();
  5344     if (!fn)
  5345         return false;
  5347     // This flows into FunctionBox, so must be tenured.
  5348     RootedFunction fun(m.cx(), NewFunction(m.cx(), NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
  5349                                            m.cx()->global(), name, JSFunction::FinalizeKind,
  5350                                            TenuredObject));
  5351     if (!fun)
  5352         return false;
  5354     AsmJSParseContext *outerpc = m.parser().pc;
  5356     Directives directives(outerpc);
  5357     FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
  5358     if (!funbox)
  5359         return false;
  5361     Directives newDirectives = directives;
  5362     AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives,
  5363                             outerpc->staticLevel + 1, outerpc->blockidGen,
  5364                             /* blockScopeDepth = */ 0);
  5365     if (!funpc.init(tokenStream))
  5366         return false;
  5368     if (!m.parser().functionArgsAndBodyGeneric(fn, fun, Normal, Statement, &newDirectives))
  5369         return false;
  5371     if (tokenStream.hadError() || directives != newDirectives)
  5372         return false;
  5374     outerpc->blockidGen = funpc.blockidGen;
  5375     fn->pn_blockid = outerpc->blockid();
  5377     *fnOut = fn;
  5378     return true;
  5381 static bool
  5382 CheckFunction(ModuleCompiler &m, LifoAlloc &lifo, MIRGenerator **mir, ModuleCompiler::Func **funcOut)
  5384     int64_t before = PRMJ_Now();
  5386     // asm.js modules can be quite large when represented as parse trees so pop
  5387     // the backing LifoAlloc after parsing/compiling each function.
  5388     AsmJSParser::Mark mark = m.parser().mark();
  5390     ParseNode *fn;
  5391     if (!ParseFunction(m, &fn))
  5392         return false;
  5394     if (!CheckFunctionHead(m, fn))
  5395         return false;
  5397     FunctionCompiler f(m, fn, lifo);
  5398     if (!f.init())
  5399         return false;
  5401     ParseNode *stmtIter = ListHead(FunctionStatementList(fn));
  5403     VarTypeVector argTypes(m.lifo());
  5404     if (!CheckArguments(f, &stmtIter, &argTypes))
  5405         return false;
  5407     if (!CheckVariables(f, &stmtIter))
  5408         return false;
  5410     if (!f.prepareToEmitMIR(argTypes))
  5411         return false;
  5413     ParseNode *lastNonEmptyStmt = nullptr;
  5414     for (; stmtIter; stmtIter = NextNode(stmtIter)) {
  5415         if (!CheckStatement(f, stmtIter))
  5416             return false;
  5417         if (!IsEmptyStatement(stmtIter))
  5418             lastNonEmptyStmt = stmtIter;
  5421     RetType retType;
  5422     if (!CheckFinalReturn(f, lastNonEmptyStmt, &retType))
  5423         return false;
  5425     if (!CheckReturnType(f, lastNonEmptyStmt, retType))
  5426         return false;
  5428     Signature sig(Move(argTypes), retType);
  5429     ModuleCompiler::Func *func = nullptr;
  5430     if (!CheckFunctionSignature(m, fn, Move(sig), FunctionName(fn), &func))
  5431         return false;
  5433     if (func->defined())
  5434         return m.failName(fn, "function '%s' already defined", FunctionName(fn));
  5436     uint32_t funcBegin = fn->pn_pos.begin;
  5437     uint32_t funcEnd = fn->pn_pos.end;
  5438     // The begin/end char range is relative to the beginning of the module,
  5439     // hence the assertions.
  5440     JS_ASSERT(funcBegin > m.moduleStart());
  5441     JS_ASSERT(funcEnd > m.moduleStart());
  5442     funcBegin -= m.moduleStart();
  5443     funcEnd -= m.moduleStart();
  5444     func->finish(funcBegin, funcEnd);
  5446     func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
  5448     m.parser().release(mark);
  5450     // Copy the cumulative minimum heap size constraint to the MIR for use in analysis.  The length
  5451     // is also constrained to particular lengths, so firstly round up - a larger 'heap required
  5452     // length' can help range analysis to prove that bounds checks are not needed.
  5453     uint32_t len = js::RoundUpToNextValidAsmJSHeapLength(m.minHeapLength());
  5454     m.requireHeapLengthToBeAtLeast(len);
  5456     *mir = f.extractMIR();
  5457     (*mir)->noteMinAsmJSHeapLength(len);
  5458     *funcOut = func;
  5459     return true;
  5462 static bool
  5463 GenerateCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mir, LIRGraph &lir)
  5465     int64_t before = PRMJ_Now();
  5467     // A single MacroAssembler is reused for all function compilations so
  5468     // that there is a single linear code segment for each module. To avoid
  5469     // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
  5470     // after each function is compiled. This method is responsible for cleaning
  5471     // out any dangling pointers that the MacroAssembler may have kept.
  5472     m.masm().resetForNewCodeGenerator(mir.alloc());
  5474     m.masm().bind(func.code());
  5476     ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm()));
  5477     if (!codegen || !codegen->generateAsmJS(&m.stackOverflowLabel()))
  5478         return m.fail(nullptr, "internal codegen failure (probably out of memory)");
  5480 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
  5481     // Profiling might not be active now, but it may be activated later (perhaps
  5482     // after the module has been cached and reloaded from the cache). Function
  5483     // profiling info isn't huge, so store it always (in --enable-profiling
  5484     // builds, which is only Nightly builds, but default).
  5485     if (!m.trackProfiledFunction(func, m.masm().currentOffset()))
  5486         return false;
  5487 #endif
  5489 #ifdef JS_ION_PERF
  5490     // Per-block profiling info uses significantly more memory so only store
  5491     // this information if it is actively requested.
  5492     if (PerfBlockEnabled()) {
  5493         if (!m.trackPerfProfiledBlocks(mir.perfSpewer(), func, m.masm().currentOffset()))
  5494             return false;
  5496 #endif
  5498     // Align internal function headers.
  5499     m.masm().align(CodeAlignment);
  5501     func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
  5502     if (!m.maybeReportCompileTime(func))
  5503         return false;
  5505     // Unlike regular IonMonkey which links and generates a new JitCode for
  5506     // every function, we accumulate all the functions in the module in a
  5507     // single MacroAssembler and link at end. Linking asm.js doesn't require a
  5508     // CodeGenerator so we can destroy it now.
  5509     return true;
  5512 static bool
  5513 CheckAllFunctionsDefined(ModuleCompiler &m)
  5515     for (unsigned i = 0; i < m.numFunctions(); i++) {
  5516         if (!m.function(i).code()->bound())
  5517             return m.failName(nullptr, "missing definition of function %s", m.function(i).name());
  5520     return true;
  5523 static bool
  5524 CheckFunctionsSequential(ModuleCompiler &m)
  5526     // Use a single LifoAlloc to allocate all the temporary compiler IR.
  5527     // All allocated LifoAlloc'd memory is released after compiling each
  5528     // function by the LifoAllocScope inside the loop.
  5529     LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
  5531     while (PeekToken(m.parser()) == TOK_FUNCTION) {
  5532         LifoAllocScope scope(&lifo);
  5534         MIRGenerator *mir;
  5535         ModuleCompiler::Func *func;
  5536         if (!CheckFunction(m, lifo, &mir, &func))
  5537             return false;
  5539         int64_t before = PRMJ_Now();
  5541         IonContext icx(m.cx(), &mir->alloc());
  5543         IonSpewNewFunction(&mir->graph(), NullPtr());
  5545         if (!OptimizeMIR(mir))
  5546             return m.failOffset(func->srcOffset(), "internal compiler failure (probably out of memory)");
  5548         LIRGraph *lir = GenerateLIR(mir);
  5549         if (!lir)
  5550             return m.failOffset(func->srcOffset(), "internal compiler failure (probably out of memory)");
  5552         func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
  5554         if (!GenerateCode(m, *func, *mir, *lir))
  5555             return false;
  5557         IonSpewEndFunction();
  5560     if (!CheckAllFunctionsDefined(m))
  5561         return false;
  5563     return true;
  5566 #ifdef JS_THREADSAFE
  5568 // Currently, only one asm.js parallel compilation is allowed at a time.
  5569 // This RAII class attempts to claim this parallel compilation using atomic ops
  5570 // on rt->workerThreadState->asmJSCompilationInProgress.
  5571 class ParallelCompilationGuard
  5573     bool parallelState_;
  5574   public:
  5575     ParallelCompilationGuard() : parallelState_(false) {}
  5576     ~ParallelCompilationGuard() {
  5577         if (parallelState_) {
  5578             JS_ASSERT(WorkerThreadState().asmJSCompilationInProgress == true);
  5579             WorkerThreadState().asmJSCompilationInProgress = false;
  5582     bool claim() {
  5583         JS_ASSERT(!parallelState_);
  5584         if (!WorkerThreadState().asmJSCompilationInProgress.compareExchange(false, true))
  5585             return false;
  5586         parallelState_ = true;
  5587         return true;
  5589 };
  5591 static bool
  5592 ParallelCompilationEnabled(ExclusiveContext *cx)
  5594     // If 'cx' isn't a JSContext, then we are already off the main thread so
  5595     // off-thread compilation must be enabled. However, since there are a fixed
  5596     // number of worker threads and one is already being consumed by this
  5597     // parsing task, ensure that there another free thread to avoid deadlock.
  5598     // (Note: there is at most one thread used for parsing so we don't have to
  5599     // worry about general dining philosophers.)
  5600     if (WorkerThreadState().threadCount <= 1)
  5601         return false;
  5603     if (!cx->isJSContext())
  5604         return true;
  5605     return cx->asJSContext()->runtime()->canUseParallelIonCompilation();
  5608 // State of compilation as tracked and updated by the main thread.
  5609 struct ParallelGroupState
  5611     js::Vector<AsmJSParallelTask> &tasks;
  5612     int32_t outstandingJobs; // Good work, jobs!
  5613     uint32_t compiledJobs;
  5615     ParallelGroupState(js::Vector<AsmJSParallelTask> &tasks)
  5616       : tasks(tasks), outstandingJobs(0), compiledJobs(0)
  5617     { }
  5618 };
  5620 // Block until a worker-assigned LifoAlloc becomes finished.
  5621 static AsmJSParallelTask *
  5622 GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
  5624     AutoLockWorkerThreadState lock;
  5626     while (!WorkerThreadState().asmJSWorkerFailed()) {
  5627         if (!WorkerThreadState().asmJSFinishedList().empty()) {
  5628             group.outstandingJobs--;
  5629             return WorkerThreadState().asmJSFinishedList().popCopy();
  5631         WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
  5634     return nullptr;
  5637 static bool
  5638 GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSParallelTask **outTask)
  5640     // Block until a used LifoAlloc becomes available.
  5641     AsmJSParallelTask *task = GetFinishedCompilation(m, group);
  5642     if (!task)
  5643         return false;
  5645     ModuleCompiler::Func &func = *reinterpret_cast<ModuleCompiler::Func *>(task->func);
  5646     func.accumulateCompileTime(task->compileTime);
  5649         // Perform code generation on the main thread.
  5650         IonContext ionContext(m.cx(), &task->mir->alloc());
  5651         if (!GenerateCode(m, func, *task->mir, *task->lir))
  5652             return false;
  5655     group.compiledJobs++;
  5657     // Clear the LifoAlloc for use by another worker.
  5658     TempAllocator &tempAlloc = task->mir->alloc();
  5659     tempAlloc.TempAllocator::~TempAllocator();
  5660     task->lifo.releaseAll();
  5662     *outTask = task;
  5663     return true;
  5666 static inline bool
  5667 GetUnusedTask(ParallelGroupState &group, uint32_t i, AsmJSParallelTask **outTask)
  5669     // Since functions are dispatched in order, if fewer than |numLifos| functions
  5670     // have been generated, then the |i'th| LifoAlloc must never have been
  5671     // assigned to a worker thread.
  5672     if (i >= group.tasks.length())
  5673         return false;
  5674     *outTask = &group.tasks[i];
  5675     return true;
  5678 static bool
  5679 CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
  5681 #ifdef DEBUG
  5683         AutoLockWorkerThreadState lock;
  5684         JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
  5685         JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
  5687 #endif
  5688     WorkerThreadState().resetAsmJSFailureState();
  5690     for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
  5691         // Get exclusive access to an empty LifoAlloc from the thread group's pool.
  5692         AsmJSParallelTask *task = nullptr;
  5693         if (!GetUnusedTask(group, i, &task) && !GenerateCodeForFinishedJob(m, group, &task))
  5694             return false;
  5696         // Generate MIR into the LifoAlloc on the main thread.
  5697         MIRGenerator *mir;
  5698         ModuleCompiler::Func *func;
  5699         if (!CheckFunction(m, task->lifo, &mir, &func))
  5700             return false;
  5702         // Perform optimizations and LIR generation on a worker thread.
  5703         task->init(m.cx()->compartment()->runtimeFromAnyThread(), func, mir);
  5704         if (!StartOffThreadAsmJSCompile(m.cx(), task))
  5705             return false;
  5707         group.outstandingJobs++;
  5710     // Block for all outstanding workers to complete.
  5711     while (group.outstandingJobs > 0) {
  5712         AsmJSParallelTask *ignored = nullptr;
  5713         if (!GenerateCodeForFinishedJob(m, group, &ignored))
  5714             return false;
  5717     if (!CheckAllFunctionsDefined(m))
  5718         return false;
  5720     JS_ASSERT(group.outstandingJobs == 0);
  5721     JS_ASSERT(group.compiledJobs == m.numFunctions());
  5722 #ifdef DEBUG
  5724         AutoLockWorkerThreadState lock;
  5725         JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
  5726         JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
  5728 #endif
  5729     JS_ASSERT(!WorkerThreadState().asmJSWorkerFailed());
  5730     return true;
  5733 static void
  5734 CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
  5736     // This is failure-handling code, so it's not allowed to fail.
  5737     // The problem is that all memory for compilation is stored in LifoAllocs
  5738     // maintained in the scope of CheckFunctionsParallel() -- so in order
  5739     // for that function to safely return, and thereby remove the LifoAllocs,
  5740     // none of that memory can be in use or reachable by workers.
  5742     JS_ASSERT(group.outstandingJobs >= 0);
  5743     if (!group.outstandingJobs)
  5744         return;
  5746     AutoLockWorkerThreadState lock;
  5748     // From the compiling tasks, eliminate those waiting for worker assignation.
  5749     group.outstandingJobs -= WorkerThreadState().asmJSWorklist().length();
  5750     WorkerThreadState().asmJSWorklist().clear();
  5752     // From the compiling tasks, eliminate those waiting for codegen.
  5753     group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
  5754     WorkerThreadState().asmJSFinishedList().clear();
  5756     // Eliminate tasks that failed without adding to the finished list.
  5757     group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
  5759     // Any remaining tasks are therefore undergoing active compilation.
  5760     JS_ASSERT(group.outstandingJobs >= 0);
  5761     while (group.outstandingJobs > 0) {
  5762         WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
  5764         group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
  5765         group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
  5766         WorkerThreadState().asmJSFinishedList().clear();
  5769     JS_ASSERT(group.outstandingJobs == 0);
  5770     JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
  5771     JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
  5774 static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
  5776 static bool
  5777 CheckFunctionsParallel(ModuleCompiler &m)
  5779     // If parallel compilation isn't enabled (not enough cores, disabled by
  5780     // pref, etc) or another thread is currently compiling asm.js in parallel,
  5781     // fall back to sequential compilation. (We could lift the latter
  5782     // constraint by hoisting asmJS* state out of WorkerThreadState so multiple
  5783     // concurrent asm.js parallel compilations don't race.)
  5784     ParallelCompilationGuard g;
  5785     if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
  5786         return CheckFunctionsSequential(m);
  5788     IonSpew(IonSpew_Logs, "Can't log asm.js script. (Compiled on background thread.)");
  5790     // Saturate all worker threads plus the main thread.
  5791     size_t numParallelJobs = WorkerThreadState().threadCount + 1;
  5793     // Allocate scoped AsmJSParallelTask objects. Each contains a unique
  5794     // LifoAlloc that provides all necessary memory for compilation.
  5795     js::Vector<AsmJSParallelTask, 0> tasks(m.cx());
  5796     if (!tasks.initCapacity(numParallelJobs))
  5797         return false;
  5799     for (size_t i = 0; i < numParallelJobs; i++)
  5800         tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
  5802     // With compilation memory in-scope, dispatch worker threads.
  5803     ParallelGroupState group(tasks);
  5804     if (!CheckFunctionsParallelImpl(m, group)) {
  5805         CancelOutstandingJobs(m, group);
  5807         // If failure was triggered by a worker thread, report error.
  5808         if (void *maybeFunc = WorkerThreadState().maybeAsmJSFailedFunction()) {
  5809             ModuleCompiler::Func *func = reinterpret_cast<ModuleCompiler::Func *>(maybeFunc);
  5810             return m.failOffset(func->srcOffset(), "allocation failure during compilation");
  5813         // Otherwise, the error occurred on the main thread and was already reported.
  5814         return false;
  5816     return true;
  5818 #endif // JS_THREADSAFE
  5820 static bool
  5821 CheckFuncPtrTable(ModuleCompiler &m, ParseNode *var)
  5823     if (!IsDefinition(var))
  5824         return m.fail(var, "function-pointer table name must be unique");
  5826     ParseNode *arrayLiteral = MaybeDefinitionInitializer(var);
  5827     if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY))
  5828         return m.fail(var, "function-pointer table's initializer must be an array literal");
  5830     unsigned length = ListLength(arrayLiteral);
  5832     if (!IsPowerOfTwo(length))
  5833         return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
  5835     unsigned mask = length - 1;
  5837     ModuleCompiler::FuncPtrVector elems(m.cx());
  5838     const Signature *firstSig = nullptr;
  5840     for (ParseNode *elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
  5841         if (!elem->isKind(PNK_NAME))
  5842             return m.fail(elem, "function-pointer table's elements must be names of functions");
  5844         PropertyName *funcName = elem->name();
  5845         const ModuleCompiler::Func *func = m.lookupFunction(funcName);
  5846         if (!func)
  5847             return m.fail(elem, "function-pointer table's elements must be names of functions");
  5849         if (firstSig) {
  5850             if (*firstSig != func->sig())
  5851                 return m.fail(elem, "all functions in table must have same signature");
  5852         } else {
  5853             firstSig = &func->sig();
  5856         if (!elems.append(func))
  5857             return false;
  5860     Signature sig(m.lifo());
  5861     if (!sig.copy(*firstSig))
  5862         return false;
  5864     ModuleCompiler::FuncPtrTable *table;
  5865     if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(sig), mask, &table))
  5866         return false;
  5868     table->initElems(Move(elems));
  5869     return true;
  5872 static bool
  5873 CheckFuncPtrTables(ModuleCompiler &m)
  5875     while (true) {
  5876         ParseNode *varStmt;
  5877         if (!ParseVarOrConstStatement(m.parser(), &varStmt))
  5878             return false;
  5879         if (!varStmt)
  5880             break;
  5881         for (ParseNode *var = VarListHead(varStmt); var; var = NextNode(var)) {
  5882             if (!CheckFuncPtrTable(m, var))
  5883                 return false;
  5887     for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
  5888         if (!m.funcPtrTable(i).initialized())
  5889             return m.fail(nullptr, "expecting function-pointer table");
  5892     return true;
  5895 static bool
  5896 CheckModuleExportFunction(ModuleCompiler &m, ParseNode *returnExpr)
  5898     if (!returnExpr->isKind(PNK_NAME))
  5899         return m.fail(returnExpr, "export statement must be of the form 'return name'");
  5901     PropertyName *funcName = returnExpr->name();
  5903     const ModuleCompiler::Func *func = m.lookupFunction(funcName);
  5904     if (!func)
  5905         return m.failName(returnExpr, "exported function name '%s' not found", funcName);
  5907     return m.addExportedFunction(func, /* maybeFieldName = */ nullptr);
  5910 static bool
  5911 CheckModuleExportObject(ModuleCompiler &m, ParseNode *object)
  5913     JS_ASSERT(object->isKind(PNK_OBJECT));
  5915     for (ParseNode *pn = ListHead(object); pn; pn = NextNode(pn)) {
  5916         if (!IsNormalObjectField(m.cx(), pn))
  5917             return m.fail(pn, "only normal object properties may be used in the export object literal");
  5919         PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn);
  5921         ParseNode *initNode = ObjectFieldInitializer(pn);
  5922         if (!initNode->isKind(PNK_NAME))
  5923             return m.fail(initNode, "initializer of exported object literal must be name of function");
  5925         PropertyName *funcName = initNode->name();
  5927         const ModuleCompiler::Func *func = m.lookupFunction(funcName);
  5928         if (!func)
  5929             return m.failName(initNode, "exported function name '%s' not found", funcName);
  5931         if (!m.addExportedFunction(func, fieldName))
  5932             return false;
  5935     return true;
  5938 static bool
  5939 CheckModuleReturn(ModuleCompiler &m)
  5941     if (PeekToken(m.parser()) != TOK_RETURN) {
  5942         TokenKind tk = PeekToken(m.parser());
  5943         if (tk == TOK_RC || tk == TOK_EOF)
  5944             return m.fail(nullptr, "expecting return statement");
  5945         return m.fail(nullptr, "invalid asm.js statement");
  5948     ParseNode *returnStmt = m.parser().statement();
  5949     if (!returnStmt)
  5950         return false;
  5952     ParseNode *returnExpr = ReturnExpr(returnStmt);
  5953     if (!returnExpr)
  5954         return m.fail(returnStmt, "export statement must return something");
  5956     if (returnExpr->isKind(PNK_OBJECT)) {
  5957         if (!CheckModuleExportObject(m, returnExpr))
  5958             return false;
  5959     } else {
  5960         if (!CheckModuleExportFunction(m, returnExpr))
  5961             return false;
  5964     // Function statements are not added to the lexical scope in ParseContext
  5965     // (since cx->tempLifoAlloc is marked/released after each function
  5966     // statement) and thus all the identifiers in the return statement will be
  5967     // mistaken as free variables and added to lexdeps. Clear these now.
  5968     m.parser().pc->lexdeps->clear();
  5969     return true;
  5972 // All registers except the stack pointer.
  5973 static const RegisterSet AllRegsExceptSP =
  5974     RegisterSet(GeneralRegisterSet(Registers::AllMask &
  5975                                    ~(uint32_t(1) << Registers::StackPointer)),
  5976                 FloatRegisterSet(FloatRegisters::AllMask));
  5977 #if defined(JS_CODEGEN_ARM)
  5978 // The ARM system ABI also includes d15 in the non volatile float registers.
  5979 static const RegisterSet NonVolatileRegs =
  5980     RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
  5981                     FloatRegisterSet(FloatRegisters::NonVolatileMask | (1 << FloatRegisters::d15)));
  5982 #else
  5983 static const RegisterSet NonVolatileRegs =
  5984     RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
  5985                 FloatRegisterSet(FloatRegisters::NonVolatileMask));
  5986 #endif
  5988 static void
  5989 LoadAsmJSActivationIntoRegister(MacroAssembler &masm, Register reg)
  5991     masm.movePtr(AsmJSImm_Runtime, reg);
  5992     size_t offset = offsetof(JSRuntime, mainThread) +
  5993                     PerThreadData::offsetOfAsmJSActivationStackReadOnly();
  5994     masm.loadPtr(Address(reg, offset), reg);
  5997 static void
  5998 LoadJSContextFromActivation(MacroAssembler &masm, Register activation, Register dest)
  6000     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfContext()), dest);
  6003 static void
  6004 AssertStackAlignment(MacroAssembler &masm)
  6006     JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
  6007 #ifdef DEBUG
  6008     Label ok;
  6009     JS_ASSERT(IsPowerOfTwo(StackAlignment));
  6010     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
  6011     masm.assumeUnreachable("Stack should be aligned.");
  6012     masm.bind(&ok);
  6013 #endif
  6016 template <class VectorT>
  6017 static unsigned
  6018 StackArgBytes(const VectorT &argTypes)
  6020     ABIArgIter<VectorT> iter(argTypes);
  6021     while (!iter.done())
  6022         iter++;
  6023     return iter.stackBytesConsumedSoFar();
  6026 static unsigned
  6027 StackDecrementForCall(MacroAssembler &masm, unsigned bytesToPush)
  6029     // Include extra padding so that, after pushing the bytesToPush,
  6030     // the stack is aligned for a call instruction.
  6031     unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed();
  6032     return AlignBytes(alreadyPushed + bytesToPush, StackAlignment) - alreadyPushed;
  6035 template <class VectorT>
  6036 static unsigned
  6037 StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned extraBytes = 0)
  6039     return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes);
  6042 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
  6043                                              NonVolatileRegs.fpus().size() * sizeof(double);
  6045 // On arm, we need to include an extra word of space at the top of the stack so
  6046 // we can explicitly store the return address before making the call to C++ or
  6047 // Ion. On x86/x64, this isn't necessary since the call instruction pushes the
  6048 // return address.
  6049 #ifdef JS_CODEGEN_ARM
  6050 static const unsigned MaybeRetAddr = sizeof(void*);
  6051 #else
  6052 static const unsigned MaybeRetAddr = 0;
  6053 #endif
  6055 static bool
  6056 GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
  6058     MacroAssembler &masm = m.masm();
  6060     // In constrast to the system ABI, the Ion convention is that all registers
  6061     // are clobbered by calls. Thus, we must save the caller's non-volatile
  6062     // registers.
  6063     //
  6064     // NB: GenerateExits assumes that masm.framePushed() == 0 before
  6065     // PushRegsInMask(NonVolatileRegs).
  6066     masm.setFramePushed(0);
  6067     masm.PushRegsInMask(NonVolatileRegs);
  6068     JS_ASSERT(masm.framePushed() == FramePushedAfterSave);
  6070     // Remember the stack pointer in the current AsmJSActivation. This will be
  6071     // used by error exit paths to set the stack pointer back to what it was
  6072     // right after the (C++) caller's non-volatile registers were saved so that
  6073     // they can be restored.
  6074     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
  6075     LoadAsmJSActivationIntoRegister(masm, activation);
  6076     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()));
  6078     // ARM has a globally-pinned GlobalReg (x64 uses RIP-relative addressing,
  6079     // x86 uses immediates in effective addresses) and NaN register (used as
  6080     // part of the out-of-bounds handling in heap loads/stores).
  6081 #if defined(JS_CODEGEN_ARM)
  6082     masm.movePtr(IntArgReg1, GlobalReg);
  6083     masm.ma_vimm(GenericNaN(), NANReg);
  6084 #endif
  6086     // ARM and x64 have a globally-pinned HeapReg (x86 uses immediates in
  6087     // effective addresses).
  6088 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
  6089     masm.loadPtr(Address(IntArgReg1, m.module().heapOffset()), HeapReg);
  6090 #endif
  6092     // Get 'argv' into a non-arg register and save it on the stack.
  6093     Register argv = ABIArgGenerator::NonArgReturnVolatileReg0;
  6094     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
  6095 #if defined(JS_CODEGEN_X86)
  6096     masm.loadPtr(Address(StackPointer, NativeFrameSize + masm.framePushed()), argv);
  6097 #else
  6098     masm.movePtr(IntArgReg0, argv);
  6099 #endif
  6100     masm.Push(argv);
  6102     // Bump the stack for the call.
  6103     const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name());
  6104     unsigned stackDec = StackDecrementForCall(masm, func.sig().args());
  6105     masm.reserveStack(stackDec);
  6107     // Copy parameters out of argv and into the registers/stack-slots specified by
  6108     // the system ABI.
  6109     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
  6110         unsigned argOffset = iter.index() * sizeof(uint64_t);
  6111         Address src(argv, argOffset);
  6112         switch (iter->kind()) {
  6113           case ABIArg::GPR:
  6114             masm.load32(src, iter->gpr());
  6115             break;
  6116           case ABIArg::FPU:
  6117             masm.loadDouble(src, iter->fpu());
  6118             break;
  6119           case ABIArg::Stack:
  6120             if (iter.mirType() == MIRType_Int32) {
  6121                 masm.load32(src, scratch);
  6122                 masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
  6123             } else {
  6124                 JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32);
  6125                 masm.loadDouble(src, ScratchFloatReg);
  6126                 masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase()));
  6128             break;
  6132     // Call into the real function.
  6133     AssertStackAlignment(masm);
  6134     masm.call(CallSiteDesc::Entry(), func.code());
  6136     // Pop the stack and recover the original 'argv' argument passed to the
  6137     // trampoline (which was pushed on the stack).
  6138     masm.freeStack(stackDec);
  6139     masm.Pop(argv);
  6141     // Store the return value in argv[0]
  6142     switch (func.sig().retType().which()) {
  6143       case RetType::Void:
  6144         break;
  6145       case RetType::Signed:
  6146         masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
  6147         break;
  6148       case RetType::Float:
  6149         masm.convertFloat32ToDouble(ReturnFloatReg, ReturnFloatReg);
  6150         // Fall through as ReturnFloatReg now contains a Double
  6151       case RetType::Double:
  6152         masm.canonicalizeDouble(ReturnFloatReg);
  6153         masm.storeDouble(ReturnFloatReg, Address(argv, 0));
  6154         break;
  6157     // Restore clobbered non-volatile registers of the caller.
  6158     masm.PopRegsInMask(NonVolatileRegs);
  6160     JS_ASSERT(masm.framePushed() == 0);
  6162     masm.move32(Imm32(true), ReturnReg);
  6163     masm.abiret();
  6164     return true;
  6167 static inline bool
  6168 TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
  6169                int32_t argc, Value *argv)
  6171     if (!fun->hasScript())
  6172         return true;
  6174     // Test if the function is Ion compiled
  6175     JSScript *script = fun->nonLazyScript();
  6176     if (!script->hasIonScript())
  6177         return true;
  6179     // Currently we can't rectify arguments. Therefore disabling if argc is too low.
  6180     if (fun->nargs() > size_t(argc))
  6181         return true;
  6183     // Normally the types should corresond, since we just ran with those types,
  6184     // but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only.
  6185     if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType()))
  6186         return true;
  6187     for(uint32_t i = 0; i < fun->nargs(); i++) {
  6188         types::StackTypeSet *typeset = types::TypeScript::ArgTypes(script, i);
  6189         types::Type type = types::Type::DoubleType();
  6190         if (!argv[i].isDouble())
  6191             type = types::Type::PrimitiveType(argv[i].extractNonDoubleType());
  6192         if (!typeset->hasType(type))
  6193             return true;
  6196     // Enable
  6197     IonScript *ionScript = script->ionScript();
  6198     if (!ionScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
  6199         return false;
  6201     module.exitIndexToGlobalDatum(exitIndex).exit = module.ionExitTrampoline(module.exit(exitIndex));
  6202     return true;
  6205 namespace js {
  6207 int32_t
  6208 InvokeFromAsmJS_Ignore(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv)
  6210     AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module();
  6212     RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
  6213     RootedValue fval(cx, ObjectValue(*fun));
  6214     RootedValue rval(cx);
  6215     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval))
  6216         return false;
  6218     if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv))
  6219         return false;
  6221     return true;
  6224 int32_t
  6225 InvokeFromAsmJS_ToInt32(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv)
  6227     AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module();
  6229     RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
  6230     RootedValue fval(cx, ObjectValue(*fun));
  6231     RootedValue rval(cx);
  6232     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval))
  6233         return false;
  6235     if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv))
  6236         return false;
  6238     int32_t i32;
  6239     if (!ToInt32(cx, rval, &i32))
  6240         return false;
  6241     argv[0] = Int32Value(i32);
  6243     return true;
  6246 int32_t
  6247 InvokeFromAsmJS_ToNumber(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv)
  6249     AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module();
  6251     RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
  6252     RootedValue fval(cx, ObjectValue(*fun));
  6253     RootedValue rval(cx);
  6254     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval))
  6255         return false;
  6257     if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv))
  6258         return false;
  6260     double dbl;
  6261     if (!ToNumber(cx, rval, &dbl))
  6262         return false;
  6263     argv[0] = DoubleValue(dbl);
  6265     return true;
  6268 }  // namespace js
  6270 static void
  6271 FillArgumentArray(ModuleCompiler &m, const VarTypeVector &argTypes,
  6272                   unsigned offsetToArgs, unsigned offsetToCallerStackArgs,
  6273                   Register scratch)
  6275     MacroAssembler &masm = m.masm();
  6277     for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
  6278         Address dstAddr = Address(StackPointer, offsetToArgs + i.index() * sizeof(Value));
  6279         switch (i->kind()) {
  6280           case ABIArg::GPR:
  6281             masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr);
  6282             break;
  6283           case ABIArg::FPU: {
  6284               masm.canonicalizeDouble(i->fpu());
  6285               masm.storeDouble(i->fpu(), dstAddr);
  6286               break;
  6288           case ABIArg::Stack:
  6289             if (i.mirType() == MIRType_Int32) {
  6290                 Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase());
  6291 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
  6292                 masm.load32(src, scratch);
  6293                 masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr);
  6294 #else
  6295                 masm.memIntToValue(src, dstAddr);
  6296 #endif
  6297             } else {
  6298                 JS_ASSERT(i.mirType() == MIRType_Double);
  6299                 Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase());
  6300                 masm.loadDouble(src, ScratchFloatReg);
  6301                 masm.canonicalizeDouble(ScratchFloatReg);
  6302                 masm.storeDouble(ScratchFloatReg, dstAddr);
  6304             break;
  6309 static void
  6310 GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
  6311                            unsigned exitIndex, Label *throwLabel)
  6313     MacroAssembler &masm = m.masm();
  6314     masm.align(CodeAlignment);
  6315     m.setInterpExitOffset(exitIndex);
  6316     masm.setFramePushed(0);
  6317 #if defined(JS_CODEGEN_ARM)
  6318     masm.Push(lr);
  6319 #endif
  6321     MIRType typeArray[] = { MIRType_Pointer,   // cx
  6322                             MIRType_Pointer,   // exitDatum
  6323                             MIRType_Int32,     // argc
  6324                             MIRType_Pointer }; // argv
  6325     MIRTypeVector invokeArgTypes(m.cx());
  6326     invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
  6328     // The stack layout looks like:
  6329     // | return address | stack arguments | array of values |
  6330     unsigned arraySize = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value);
  6331     unsigned stackDec = StackDecrementForCall(masm, invokeArgTypes, arraySize + MaybeRetAddr);
  6332     masm.reserveStack(stackDec);
  6334     // Fill the argument array.
  6335     unsigned offsetToCallerStackArgs = AlignmentAtPrologue + masm.framePushed();
  6336     unsigned offsetToArgv = StackArgBytes(invokeArgTypes) + MaybeRetAddr;
  6337     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
  6338     FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
  6340     // Prepare the arguments for the call to InvokeFromAsmJS_*.
  6341     ABIArgMIRTypeIter i(invokeArgTypes);
  6342     Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
  6343     LoadAsmJSActivationIntoRegister(masm, activation);
  6345     // Record sp in the AsmJSActivation for stack-walking.
  6346     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
  6348     // argument 0: cx
  6349     if (i->kind() == ABIArg::GPR) {
  6350         LoadJSContextFromActivation(masm, activation, i->gpr());
  6351     } else {
  6352         LoadJSContextFromActivation(masm, activation, scratch);
  6353         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
  6355     i++;
  6357     // argument 1: exitIndex
  6358     if (i->kind() == ABIArg::GPR)
  6359         masm.mov(ImmWord(exitIndex), i->gpr());
  6360     else
  6361         masm.store32(Imm32(exitIndex), Address(StackPointer, i->offsetFromArgBase()));
  6362     i++;
  6364     // argument 2: argc
  6365     unsigned argc = exit.sig().args().length();
  6366     if (i->kind() == ABIArg::GPR)
  6367         masm.mov(ImmWord(argc), i->gpr());
  6368     else
  6369         masm.store32(Imm32(argc), Address(StackPointer, i->offsetFromArgBase()));
  6370     i++;
  6372     // argument 3: argv
  6373     Address argv(StackPointer, offsetToArgv);
  6374     if (i->kind() == ABIArg::GPR) {
  6375         masm.computeEffectiveAddress(argv, i->gpr());
  6376     } else {
  6377         masm.computeEffectiveAddress(argv, scratch);
  6378         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
  6380     i++;
  6381     JS_ASSERT(i.done());
  6383     // Make the call, test whether it succeeded, and extract the return value.
  6384     AssertStackAlignment(masm);
  6385     switch (exit.sig().retType().which()) {
  6386       case RetType::Void:
  6387         masm.callExit(AsmJSImm_InvokeFromAsmJS_Ignore, i.stackBytesConsumedSoFar());
  6388         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
  6389         break;
  6390       case RetType::Signed:
  6391         masm.callExit(AsmJSImm_InvokeFromAsmJS_ToInt32, i.stackBytesConsumedSoFar());
  6392         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
  6393         masm.unboxInt32(argv, ReturnReg);
  6394         break;
  6395       case RetType::Double:
  6396         masm.callExit(AsmJSImm_InvokeFromAsmJS_ToNumber, i.stackBytesConsumedSoFar());
  6397         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
  6398         masm.loadDouble(argv, ReturnFloatReg);
  6399         break;
  6400       case RetType::Float:
  6401         MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
  6402         break;
  6405     // Note: the caller is IonMonkey code which means there are no non-volatile
  6406     // registers to restore.
  6407     masm.freeStack(stackDec);
  6408     masm.ret();
  6411 static void
  6412 GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
  6414     MacroAssembler &masm = m.masm();
  6416     MIRType typeArray[] = { MIRType_Pointer,   // cx
  6417                             MIRType_Pointer }; // argv
  6418     MIRTypeVector callArgTypes(m.cx());
  6419     callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
  6421     // The stack is assumed to be aligned.  The frame is allocated by GenerateFFIIonExit and
  6422     // the stack usage here needs to kept in sync with GenerateFFIIonExit.
  6424     // Store value
  6425     unsigned offsetToArgv = StackArgBytes(callArgTypes) + MaybeRetAddr;
  6426     masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToArgv));
  6428     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
  6429     Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
  6430     LoadAsmJSActivationIntoRegister(masm, activation);
  6432     // Record sp in the AsmJSActivation for stack-walking.
  6433     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
  6435     // Store real arguments
  6436     ABIArgMIRTypeIter i(callArgTypes);
  6438     // argument 0: cx
  6439     if (i->kind() == ABIArg::GPR) {
  6440         LoadJSContextFromActivation(masm, activation, i->gpr());
  6441     } else {
  6442         LoadJSContextFromActivation(masm, activation, scratch);
  6443         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
  6445     i++;
  6447     // argument 1: argv
  6448     Address argv(StackPointer, offsetToArgv);
  6449     if (i->kind() == ABIArg::GPR) {
  6450         masm.computeEffectiveAddress(argv, i->gpr());
  6451     } else {
  6452         masm.computeEffectiveAddress(argv, scratch);
  6453         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
  6455     i++;
  6456     JS_ASSERT(i.done());
  6458     // Call
  6459     AssertStackAlignment(masm);
  6460     switch (retType.which()) {
  6461       case RetType::Signed:
  6462         masm.callExit(AsmJSImm_CoerceInPlace_ToInt32, i.stackBytesConsumedSoFar());
  6463         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
  6464         masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
  6465         break;
  6466       case RetType::Double:
  6467         masm.callExit(AsmJSImm_CoerceInPlace_ToNumber, i.stackBytesConsumedSoFar());
  6468         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
  6469         masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnFloatReg);
  6470         break;
  6471       default:
  6472         MOZ_ASSUME_UNREACHABLE("Unsupported convert type");
  6476 static void
  6477 GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
  6478                          unsigned exitIndex, Label *throwLabel)
  6480     MacroAssembler &masm = m.masm();
  6481     masm.align(CodeAlignment);
  6482     m.setIonExitOffset(exitIndex);
  6483     masm.setFramePushed(0);
  6485 #if defined(JS_CODEGEN_X64)
  6486     masm.Push(HeapReg);
  6487 #elif defined(JS_CODEGEN_ARM)
  6488     // The lr register holds the return address and needs to be saved.  The GlobalReg
  6489     // (r10) and HeapReg (r11) also need to be restored before returning to asm.js code.
  6490     // The NANReg also needs to be restored, but is a constant and is reloaded before
  6491     // returning to asm.js code.
  6492     masm.PushRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
  6493                                                        (1<<HeapReg.code()) |
  6494                                                        (1<<lr.code())),
  6495                                     FloatRegisterSet(uint32_t(0))));
  6496 #endif
  6498     // The stack frame is used for the call into Ion and also for calls into C for OOL
  6499     // conversion of the result.  A frame large enough for both is allocated.
  6500     //
  6501     // Arguments to the Ion function are in the following order on the stack:
  6502     // | return address | descriptor | callee | argc | this | arg1 | arg2 | ...
  6503     unsigned argBytes = 3 * sizeof(size_t) + (1 + exit.sig().args().length()) * sizeof(Value);
  6504     unsigned offsetToArgs = MaybeRetAddr;
  6505     unsigned stackDecForIonCall = StackDecrementForCall(masm, argBytes + offsetToArgs);
  6507     // Reserve space for a call to AsmJSImm_CoerceInPlace_* and an array of values used by
  6508     // OOLConvert which reuses the same frame. This code needs to be kept in sync with the
  6509     // stack usage in GenerateOOLConvert.
  6510     MIRType typeArray[] = { MIRType_Pointer, MIRType_Pointer }; // cx, argv
  6511     MIRTypeVector callArgTypes(m.cx());
  6512     callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
  6513     unsigned oolExtraBytes = sizeof(Value) + MaybeRetAddr;
  6514     unsigned stackDecForOOLCall = StackDecrementForCall(masm, callArgTypes, oolExtraBytes);
  6516     // Allocate a frame large enough for both of the above calls.
  6517     unsigned stackDec = Max(stackDecForIonCall, stackDecForOOLCall);
  6519     masm.reserveStack(stackDec);
  6520     AssertStackAlignment(masm);
  6522     // 1. Descriptor
  6523     size_t argOffset = offsetToArgs;
  6524     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_Entry);
  6525     masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset));
  6526     argOffset += sizeof(size_t);
  6528     // 2. Callee
  6529     Register callee = ABIArgGenerator::NonArgReturnVolatileReg0;
  6530     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
  6532     // 2.1. Get ExitDatum
  6533     unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
  6534 #if defined(JS_CODEGEN_X64)
  6535     CodeOffsetLabel label2 = masm.leaRipRelative(callee);
  6536     m.masm().append(AsmJSGlobalAccess(label2.offset(), globalDataOffset));
  6537 #elif defined(JS_CODEGEN_X86)
  6538     CodeOffsetLabel label2 = masm.movlWithPatch(Imm32(0), callee);
  6539     m.masm().append(AsmJSGlobalAccess(label2.offset(), globalDataOffset));
  6540 #else
  6541     masm.lea(Operand(GlobalReg, globalDataOffset), callee);
  6542 #endif
  6544     // 2.2. Get callee
  6545     masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee);
  6547     // 2.3. Save callee
  6548     masm.storePtr(callee, Address(StackPointer, argOffset));
  6549     argOffset += sizeof(size_t);
  6551     // 3. Argc
  6552     unsigned argc = exit.sig().args().length();
  6553     masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset));
  6554     argOffset += sizeof(size_t);
  6556     // 4. |this| value
  6557     masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset));
  6558     argOffset += sizeof(Value);
  6560     // 5. Fill the arguments
  6561     unsigned offsetToCallerStackArgs = masm.framePushed();
  6562 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
  6563     offsetToCallerStackArgs += NativeFrameSize;
  6564 #endif
  6565     FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
  6566     argOffset += exit.sig().args().length() * sizeof(Value);
  6567     JS_ASSERT(argOffset == offsetToArgs + argBytes);
  6569     // Get the pointer to the ion code
  6570     Label done, oolConvert;
  6571     Label *maybeDebugBreakpoint = nullptr;
  6573 #ifdef DEBUG
  6574     Label ionFailed;
  6575     maybeDebugBreakpoint = &ionFailed;
  6576     masm.branchIfFunctionHasNoScript(callee, &ionFailed);
  6577 #endif
  6579     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
  6580     masm.loadBaselineOrIonNoArgCheck(callee, callee, SequentialExecution, maybeDebugBreakpoint);
  6582     AssertStackAlignment(masm);
  6585         // Enable Activation.
  6586         //
  6587         // This sequence requires four registers, and needs to preserve the 'callee'
  6588         // register, so there are five live registers.
  6589         JS_ASSERT(callee == AsmJSIonExitRegCallee);
  6590         Register reg0 = AsmJSIonExitRegE0;
  6591         Register reg1 = AsmJSIonExitRegE1;
  6592         Register reg2 = AsmJSIonExitRegE2;
  6593         Register reg3 = AsmJSIonExitRegE3;
  6595         LoadAsmJSActivationIntoRegister(masm, reg0);
  6597         // Record sp in the AsmJSActivation for stack-walking.
  6598         masm.storePtr(StackPointer, Address(reg0, AsmJSActivation::offsetOfExitSP()));
  6600         // The following is inlined:
  6601         //   JSContext *cx = activation->cx();
  6602         //   Activation *act = cx->mainThread().activation();
  6603         //   act.active_ = true;
  6604         //   act.prevIonTop_ = cx->mainThread().ionTop;
  6605         //   act.prevJitJSContext_ = cx->mainThread().jitJSContext;
  6606         //   cx->mainThread().jitJSContext = cx;
  6607         // On the ARM store8() uses the secondScratchReg (lr) as a temp.
  6608         size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
  6609                                     PerThreadData::offsetOfActivation();
  6610         size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop);
  6611         size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
  6612                                       offsetof(PerThreadData, jitJSContext);
  6613         masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
  6614         masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
  6615         masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
  6616         masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8()));
  6617         masm.loadPtr(Address(reg0, offsetOfIonTop), reg2);
  6618         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevIonTop()));
  6619         masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
  6620         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
  6621         masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
  6624     // 2. Call
  6625     AssertStackAlignment(masm);
  6626     masm.callIonFromAsmJS(callee);
  6627     AssertStackAlignment(masm);
  6630         // Disable Activation.
  6631         //
  6632         // This sequence needs three registers, and must preserve the JSReturnReg_Data and
  6633         // JSReturnReg_Type, so there are five live registers.
  6634         JS_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
  6635         JS_ASSERT(JSReturnReg_Type == AsmJSIonExitRegReturnType);
  6636         Register reg0 = AsmJSIonExitRegD0;
  6637         Register reg1 = AsmJSIonExitRegD1;
  6638         Register reg2 = AsmJSIonExitRegD2;
  6640         LoadAsmJSActivationIntoRegister(masm, reg0);
  6642         // The following is inlined:
  6643         //   JSContext *cx = activation->cx();
  6644         //   Activation *act = cx->mainThread().activation();
  6645         //   act.active_ = false;
  6646         //   cx->mainThread().ionTop = prevIonTop_;
  6647         //   cx->mainThread().jitJSContext = prevJitJSContext_;
  6648         // On the ARM store8() uses the secondScratchReg (lr) as a temp.
  6649         size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
  6650                                     PerThreadData::offsetOfActivation();
  6651         size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop);
  6652         size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
  6653                                       offsetof(PerThreadData, jitJSContext);
  6654         masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg0);
  6655         masm.loadPtr(Address(reg0, JSContext::offsetOfRuntime()), reg0);
  6656         masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
  6657         masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8()));
  6658         masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevIonTop()), reg2);
  6659         masm.storePtr(reg2, Address(reg0, offsetOfIonTop));
  6660         masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2);
  6661         masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext));
  6664 #ifdef DEBUG
  6665     masm.branchTestMagicValue(Assembler::Equal, JSReturnOperand, JS_ION_ERROR, throwLabel);
  6666     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &ionFailed);
  6667 #else
  6668     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
  6669 #endif
  6671     uint32_t oolConvertFramePushed = masm.framePushed();
  6672     switch (exit.sig().retType().which()) {
  6673       case RetType::Void:
  6674         break;
  6675       case RetType::Signed:
  6676         masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
  6677                                  /* -0 check */ false);
  6678         break;
  6679       case RetType::Double:
  6680         masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
  6681         break;
  6682       case RetType::Float:
  6683         MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
  6684         break;
  6687     masm.bind(&done);
  6688     masm.freeStack(stackDec);
  6689 #if defined(JS_CODEGEN_ARM)
  6690     masm.ma_vimm(GenericNaN(), NANReg);
  6691     masm.PopRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
  6692                                                       (1<<HeapReg.code()) |
  6693                                                       (1<<pc.code())),
  6694                                    FloatRegisterSet(uint32_t(0))));
  6695 #else
  6696 # if defined(JS_CODEGEN_X64)
  6697     masm.Pop(HeapReg);
  6698 # endif
  6699     masm.ret();
  6700 #endif
  6701     JS_ASSERT(masm.framePushed() == 0);
  6703     // oolConvert
  6704     if (oolConvert.used()) {
  6705         masm.bind(&oolConvert);
  6706         masm.setFramePushed(oolConvertFramePushed);
  6707         GenerateOOLConvert(m, exit.sig().retType(), throwLabel);
  6708         masm.setFramePushed(0);
  6709         masm.jump(&done);
  6712 #ifdef DEBUG
  6713     masm.bind(&ionFailed);
  6714     masm.assumeUnreachable("AsmJS to IonMonkey call failed.");
  6715 #endif
  6718 // See "asm.js FFI calls" comment above.
  6719 static void
  6720 GenerateFFIExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex,
  6721                 Label *throwLabel)
  6723     // Generate the slow path through the interpreter
  6724     GenerateFFIInterpreterExit(m, exit, exitIndex, throwLabel);
  6726     // Generate the fast path
  6727     GenerateFFIIonExit(m, exit, exitIndex, throwLabel);
  6730 // The stack-overflow exit is called when the stack limit has definitely been
  6731 // exceeded. In this case, we can clobber everything since we are about to pop
  6732 // all the frames.
  6733 static bool
  6734 GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
  6736     MacroAssembler &masm = m.masm();
  6737     masm.align(CodeAlignment);
  6738     masm.bind(&m.stackOverflowLabel());
  6740     // The overflow check always occurs before the initial function-specific
  6741     // stack-size adjustment. See CodeGenerator::generateAsmJSPrologue.
  6742     masm.setFramePushed(AlignmentMidPrologue - AlignmentAtPrologue);
  6744     MIRTypeVector argTypes(m.cx());
  6745     argTypes.infallibleAppend(MIRType_Pointer); // cx
  6747     unsigned stackDec = StackDecrementForCall(masm, argTypes, MaybeRetAddr);
  6748     masm.reserveStack(stackDec);
  6750     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
  6751     LoadAsmJSActivationIntoRegister(masm, activation);
  6753     // Record sp in the AsmJSActivation for stack-walking.
  6754     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
  6756     ABIArgMIRTypeIter i(argTypes);
  6758     // argument 0: cx
  6759     if (i->kind() == ABIArg::GPR) {
  6760         LoadJSContextFromActivation(masm, activation, i->gpr());
  6761     } else {
  6762         LoadJSContextFromActivation(masm, activation, activation);
  6763         masm.storePtr(activation, Address(StackPointer, i->offsetFromArgBase()));
  6765     i++;
  6767     JS_ASSERT(i.done());
  6769     AssertStackAlignment(masm);
  6770     masm.callExit(AsmJSImm_ReportOverRecursed, i.stackBytesConsumedSoFar());
  6772     // Don't worry about restoring the stack; throwLabel will pop everything.
  6773     masm.jump(throwLabel);
  6774     return !masm.oom();
  6777 // The operation-callback exit is called from arbitrarily-interrupted asm.js
  6778 // code. That means we must first save *all* registers and restore *all*
  6779 // registers (except the stack pointer) when we resume. The address to resume to
  6780 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
  6781 // execution should be aborted) is stored in AsmJSActivation::resumePC_.
  6782 // Unfortunately, loading this requires a scratch register which we don't have
  6783 // after restoring all registers. To hack around this, push the resumePC on the
  6784 // stack so that it can be popped directly into PC.
  6785 static bool
  6786 GenerateInterruptExit(ModuleCompiler &m, Label *throwLabel)
  6788     MacroAssembler &masm = m.masm();
  6789     masm.align(CodeAlignment);
  6790     masm.bind(&m.interruptLabel());
  6792 #ifndef JS_CODEGEN_ARM
  6793     // Be very careful here not to perturb the machine state before saving it
  6794     // to the stack. In particular, add/sub instructions may set conditions in
  6795     // the flags register.
  6796     masm.push(Imm32(0));            // space for resumePC
  6797     masm.pushFlags();               // after this we are safe to use sub
  6798     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
  6799     masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP)
  6801     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
  6802     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
  6804     // Store resumePC into the reserved space.
  6805     LoadAsmJSActivationIntoRegister(masm, activation);
  6806     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfResumePC()), scratch);
  6807     masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*)));
  6809     // We know that StackPointer is word-aligned, but not necessarily
  6810     // stack-aligned, so we need to align it dynamically.
  6811     masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg);
  6812 #if defined(JS_CODEGEN_X86)
  6813     // Ensure that at least one slot is pushed for passing 'cx' below.
  6814     masm.push(Imm32(0));
  6815 #endif
  6816     masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer);
  6817     if (ShadowStackSpace)
  6818         masm.subPtr(Imm32(ShadowStackSpace), StackPointer);
  6820     // argument 0: cx
  6821 #if defined(JS_CODEGEN_X86)
  6822     LoadJSContextFromActivation(masm, activation, scratch);
  6823     masm.storePtr(scratch, Address(StackPointer, 0));
  6824 #elif defined(JS_CODEGEN_X64)
  6825     LoadJSContextFromActivation(masm, activation, IntArgReg0);
  6826 #endif
  6828     masm.call(AsmJSImm_HandleExecutionInterrupt);
  6829     masm.branchIfFalseBool(ReturnReg, throwLabel);
  6831     // Restore the StackPointer to it's position before the call.
  6832     masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer);
  6834     // Restore the machine state to before the interrupt.
  6835     masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
  6836     masm.popFlags();              // after this, nothing that sets conditions
  6837     masm.ret();                   // pop resumePC into PC
  6838 #else
  6839     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
  6840     masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)), FloatRegisterSet(uint32_t(0))));   // save all GP registers,excep sp
  6842     // Save both the APSR and FPSCR in non-volatile registers.
  6843     masm.as_mrs(r4);
  6844     masm.as_vmrs(r5);
  6845     // Save the stack pointer in a non-volatile register.
  6846     masm.mov(sp,r6);
  6847     // Align the stack.
  6848     masm.ma_and(Imm32(~7), sp, sp);
  6850     // Store resumePC into the return PC stack slot.
  6851     LoadAsmJSActivationIntoRegister(masm, IntArgReg0);
  6852     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
  6853     masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
  6855     // argument 0: cx
  6856     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfContext()), IntArgReg0);
  6858     masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllMask)));   // save all FP registers
  6859     masm.call(AsmJSImm_HandleExecutionInterrupt);
  6860     masm.branchIfFalseBool(ReturnReg, throwLabel);
  6862     // Restore the machine state to before the interrupt. this will set the pc!
  6863     masm.PopRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllMask)));   // restore all FP registers
  6864     masm.mov(r6,sp);
  6865     masm.as_vmsr(r5);
  6866     masm.as_msr(r4);
  6867     // Restore all GP registers
  6868     masm.startDataTransferM(IsLoad, sp, IA, WriteBack);
  6869     masm.transferReg(r0);
  6870     masm.transferReg(r1);
  6871     masm.transferReg(r2);
  6872     masm.transferReg(r3);
  6873     masm.transferReg(r4);
  6874     masm.transferReg(r5);
  6875     masm.transferReg(r6);
  6876     masm.transferReg(r7);
  6877     masm.transferReg(r8);
  6878     masm.transferReg(r9);
  6879     masm.transferReg(r10);
  6880     masm.transferReg(r11);
  6881     masm.transferReg(r12);
  6882     masm.transferReg(lr);
  6883     masm.finishDataTransfer();
  6884     masm.ret();
  6886 #endif
  6888     return !masm.oom();
  6891 // If an exception is thrown, simply pop all frames (since asm.js does not
  6892 // contain try/catch). To do this:
  6893 //  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
  6894 //  2. PopRegsInMask to restore the caller's non-volatile registers.
  6895 //  3. Return (to CallAsmJS).
  6896 static bool
  6897 GenerateThrowExit(ModuleCompiler &m, Label *throwLabel)
  6899     MacroAssembler &masm = m.masm();
  6900     masm.align(CodeAlignment);
  6901     masm.bind(throwLabel);
  6903     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
  6904     LoadAsmJSActivationIntoRegister(masm, activation);
  6906     masm.setFramePushed(FramePushedAfterSave);
  6907     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer);
  6909     masm.PopRegsInMask(NonVolatileRegs);
  6910     JS_ASSERT(masm.framePushed() == 0);
  6912     masm.mov(ImmWord(0), ReturnReg);
  6913     masm.abiret();
  6915     return !masm.oom();
  6918 static bool
  6919 GenerateStubs(ModuleCompiler &m)
  6921     for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) {
  6922         m.setEntryOffset(i);
  6923         if (!GenerateEntry(m, m.module().exportedFunction(i)))
  6924             return false;
  6925         if (m.masm().oom())
  6926             return false;
  6929     Label throwLabel;
  6931     // The order of the iterations here is non-deterministic, since
  6932     // m.allExits() is a hash keyed by pointer values!
  6933     for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
  6934         GenerateFFIExit(m, r.front().key(), r.front().value(), &throwLabel);
  6935         if (m.masm().oom())
  6936             return false;
  6939     if (m.stackOverflowLabel().used()) {
  6940         if (!GenerateStackOverflowExit(m, &throwLabel))
  6941             return false;
  6944     if (!GenerateInterruptExit(m, &throwLabel))
  6945         return false;
  6947     if (!GenerateThrowExit(m, &throwLabel))
  6948         return false;
  6950     return true;
  6953 static bool
  6954 FinishModule(ModuleCompiler &m,
  6955              ScopedJSDeletePtr<AsmJSModule> *module)
  6957     LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
  6958     TempAllocator alloc(&lifo);
  6959     IonContext ionContext(m.cx(), &alloc);
  6961     m.masm().resetForNewCodeGenerator(alloc);
  6963     if (!GenerateStubs(m))
  6964         return false;
  6966     return m.finish(module);
  6969 static bool
  6970 CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
  6971             ScopedJSDeletePtr<AsmJSModule> *moduleOut,
  6972             ScopedJSFreePtr<char> *compilationTimeReport)
  6974     if (!LookupAsmJSModuleInCache(cx, parser, moduleOut, compilationTimeReport))
  6975         return false;
  6976     if (*moduleOut)
  6977         return true;
  6979     ModuleCompiler m(cx, parser);
  6980     if (!m.init())
  6981         return false;
  6983     if (PropertyName *moduleFunctionName = FunctionName(m.moduleFunctionNode())) {
  6984         if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName))
  6985             return false;
  6986         m.initModuleFunctionName(moduleFunctionName);
  6989     if (!CheckFunctionHead(m, m.moduleFunctionNode()))
  6990         return false;
  6992     if (!CheckModuleArguments(m, m.moduleFunctionNode()))
  6993         return false;
  6995     if (!CheckPrecedingStatements(m, stmtList))
  6996         return false;
  6998     if (!CheckModuleGlobals(m))
  6999         return false;
  7001 #ifdef JS_THREADSAFE
  7002     if (!CheckFunctionsParallel(m))
  7003         return false;
  7004 #else
  7005     if (!CheckFunctionsSequential(m))
  7006         return false;
  7007 #endif
  7009     m.finishFunctionBodies();
  7011     if (!CheckFuncPtrTables(m))
  7012         return false;
  7014     if (!CheckModuleReturn(m))
  7015         return false;
  7017     TokenKind tk = PeekToken(m.parser());
  7018     if (tk != TOK_EOF && tk != TOK_RC)
  7019         return m.fail(nullptr, "top-level export (return) must be the last statement");
  7021     // The instruction cache is flushed when dynamically linking, so can inhibit now.
  7022     AutoFlushICache afc("CheckModule", /* inhibit= */ true);
  7024     ScopedJSDeletePtr<AsmJSModule> module;
  7025     if (!FinishModule(m, &module))
  7026         return false;
  7028     bool storedInCache = StoreAsmJSModuleInCache(parser, *module, cx);
  7029     module->staticallyLink(cx);
  7031     m.buildCompilationTimeReport(storedInCache, compilationTimeReport);
  7032     *moduleOut = module.forget();
  7033     return true;
  7036 static bool
  7037 Warn(AsmJSParser &parser, int errorNumber, const char *str)
  7039     parser.reportNoOffset(ParseWarning, /* strict = */ false, errorNumber, str ? str : "");
  7040     return false;
  7043 static bool
  7044 EstablishPreconditions(ExclusiveContext *cx, AsmJSParser &parser)
  7046     if (!cx->jitSupportsFloatingPoint())
  7047         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
  7049     if (!cx->signalHandlersInstalled())
  7050         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
  7052     if (cx->gcSystemPageSize() != AsmJSPageSize)
  7053         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
  7055     if (!parser.options().asmJSOption)
  7056         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
  7058     if (!parser.options().compileAndGo)
  7059         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Temporarily disabled for event-handler and other cloneable scripts");
  7061     if (cx->compartment()->debugMode())
  7062         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
  7064     if (parser.pc->isGenerator())
  7065         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
  7067     if (parser.pc->isArrowFunction())
  7068         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
  7070 #ifdef JS_THREADSAFE
  7071     if (ParallelCompilationEnabled(cx))
  7072         EnsureWorkerThreadsInitialized(cx);
  7073 #endif
  7075     return true;
  7078 static bool
  7079 NoExceptionPending(ExclusiveContext *cx)
  7081     return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
  7084 bool
  7085 js::CompileAsmJS(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList, bool *validated)
  7087     *validated = false;
  7089     if (!EstablishPreconditions(cx, parser))
  7090         return NoExceptionPending(cx);
  7092     ScopedJSFreePtr<char> compilationTimeReport;
  7093     ScopedJSDeletePtr<AsmJSModule> module;
  7094     if (!CheckModule(cx, parser, stmtList, &module, &compilationTimeReport))
  7095         return NoExceptionPending(cx);
  7097     RootedObject moduleObj(cx, AsmJSModuleObject::create(cx, &module));
  7098     if (!moduleObj)
  7099         return false;
  7101     FunctionBox *funbox = parser.pc->maybeFunction->pn_funbox;
  7102     RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj));
  7103     if (!moduleFun)
  7104         return false;
  7106     JS_ASSERT(funbox->function()->isInterpreted());
  7107     funbox->object = moduleFun;
  7109     *validated = true;
  7110     Warn(parser, JSMSG_USE_ASM_TYPE_OK, compilationTimeReport.get());
  7111     return NoExceptionPending(cx);
  7114 bool
  7115 js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
  7117     CallArgs args = CallArgsFromVp(argc, vp);
  7119     // See EstablishPreconditions.
  7120     bool available = cx->jitSupportsFloatingPoint() &&
  7121                      cx->signalHandlersInstalled() &&
  7122                      cx->gcSystemPageSize() == AsmJSPageSize &&
  7123                      !cx->compartment()->debugMode() &&
  7124                      cx->runtime()->options().asmJS();
  7126     args.rval().set(BooleanValue(available));
  7127     return true;

mercurial