1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/AsmJS.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7128 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/AsmJS.h" 1.11 + 1.12 +#include "mozilla/Move.h" 1.13 + 1.14 +#ifdef MOZ_VTUNE 1.15 +# include "vtune/VTuneWrapper.h" 1.16 +#endif 1.17 + 1.18 +#include "jsmath.h" 1.19 +#include "jsprf.h" 1.20 +#include "jsworkers.h" 1.21 +#include "prmjtime.h" 1.22 + 1.23 +#include "assembler/assembler/MacroAssembler.h" 1.24 +#include "frontend/Parser.h" 1.25 +#include "jit/AsmJSLink.h" 1.26 +#include "jit/AsmJSModule.h" 1.27 +#include "jit/AsmJSSignalHandlers.h" 1.28 +#include "jit/CodeGenerator.h" 1.29 +#include "jit/CompileWrappers.h" 1.30 +#include "jit/MIR.h" 1.31 +#include "jit/MIRGraph.h" 1.32 +#ifdef JS_ION_PERF 1.33 +# include "jit/PerfSpewer.h" 1.34 +#endif 1.35 +#include "vm/Interpreter.h" 1.36 + 1.37 +#include "jsinferinlines.h" 1.38 +#include "jsobjinlines.h" 1.39 + 1.40 +#include "frontend/ParseNode-inl.h" 1.41 +#include "frontend/Parser-inl.h" 1.42 + 1.43 +using namespace js; 1.44 +using namespace js::frontend; 1.45 +using namespace js::jit; 1.46 + 1.47 +using mozilla::AddToHash; 1.48 +using mozilla::ArrayLength; 1.49 +using mozilla::CountLeadingZeroes32; 1.50 +using mozilla::DebugOnly; 1.51 +using mozilla::HashGeneric; 1.52 +using mozilla::IsNaN; 1.53 +using mozilla::IsNegativeZero; 1.54 +using mozilla::Maybe; 1.55 +using mozilla::Move; 1.56 +using mozilla::PositiveInfinity; 1.57 +using JS::GenericNaN; 1.58 + 1.59 +static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; 1.60 + 1.61 +/*****************************************************************************/ 1.62 +// ParseNode utilities 1.63 + 1.64 +static inline ParseNode * 1.65 +NextNode(ParseNode *pn) 1.66 +{ 1.67 + return pn->pn_next; 1.68 +} 1.69 + 1.70 +static inline ParseNode * 1.71 +UnaryKid(ParseNode *pn) 1.72 +{ 1.73 + JS_ASSERT(pn->isArity(PN_UNARY)); 1.74 + return pn->pn_kid; 1.75 +} 1.76 + 1.77 +static inline ParseNode * 1.78 +ReturnExpr(ParseNode *pn) 1.79 +{ 1.80 + JS_ASSERT(pn->isKind(PNK_RETURN)); 1.81 + return UnaryKid(pn); 1.82 +} 1.83 + 1.84 +static inline ParseNode * 1.85 +BinaryRight(ParseNode *pn) 1.86 +{ 1.87 + JS_ASSERT(pn->isArity(PN_BINARY)); 1.88 + return pn->pn_right; 1.89 +} 1.90 + 1.91 +static inline ParseNode * 1.92 +BinaryLeft(ParseNode *pn) 1.93 +{ 1.94 + JS_ASSERT(pn->isArity(PN_BINARY)); 1.95 + return pn->pn_left; 1.96 +} 1.97 + 1.98 +static inline ParseNode * 1.99 +TernaryKid1(ParseNode *pn) 1.100 +{ 1.101 + JS_ASSERT(pn->isArity(PN_TERNARY)); 1.102 + return pn->pn_kid1; 1.103 +} 1.104 + 1.105 +static inline ParseNode * 1.106 +TernaryKid2(ParseNode *pn) 1.107 +{ 1.108 + JS_ASSERT(pn->isArity(PN_TERNARY)); 1.109 + return pn->pn_kid2; 1.110 +} 1.111 + 1.112 +static inline ParseNode * 1.113 +TernaryKid3(ParseNode *pn) 1.114 +{ 1.115 + JS_ASSERT(pn->isArity(PN_TERNARY)); 1.116 + return pn->pn_kid3; 1.117 +} 1.118 + 1.119 +static inline ParseNode * 1.120 +ListHead(ParseNode *pn) 1.121 +{ 1.122 + JS_ASSERT(pn->isArity(PN_LIST)); 1.123 + return pn->pn_head; 1.124 +} 1.125 + 1.126 +static inline unsigned 1.127 +ListLength(ParseNode *pn) 1.128 +{ 1.129 + JS_ASSERT(pn->isArity(PN_LIST)); 1.130 + return pn->pn_count; 1.131 +} 1.132 + 1.133 +static inline ParseNode * 1.134 +CallCallee(ParseNode *pn) 1.135 +{ 1.136 + JS_ASSERT(pn->isKind(PNK_CALL)); 1.137 + return ListHead(pn); 1.138 +} 1.139 + 1.140 +static inline unsigned 1.141 +CallArgListLength(ParseNode *pn) 1.142 +{ 1.143 + JS_ASSERT(pn->isKind(PNK_CALL)); 1.144 + JS_ASSERT(ListLength(pn) >= 1); 1.145 + return ListLength(pn) - 1; 1.146 +} 1.147 + 1.148 +static inline ParseNode * 1.149 +CallArgList(ParseNode *pn) 1.150 +{ 1.151 + JS_ASSERT(pn->isKind(PNK_CALL)); 1.152 + return NextNode(ListHead(pn)); 1.153 +} 1.154 + 1.155 +static inline ParseNode * 1.156 +VarListHead(ParseNode *pn) 1.157 +{ 1.158 + JS_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)); 1.159 + return ListHead(pn); 1.160 +} 1.161 + 1.162 +static inline ParseNode * 1.163 +CaseExpr(ParseNode *pn) 1.164 +{ 1.165 + JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); 1.166 + return BinaryLeft(pn); 1.167 +} 1.168 + 1.169 +static inline ParseNode * 1.170 +CaseBody(ParseNode *pn) 1.171 +{ 1.172 + JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); 1.173 + return BinaryRight(pn); 1.174 +} 1.175 + 1.176 +static inline bool 1.177 +IsExpressionStatement(ParseNode *pn) 1.178 +{ 1.179 + return pn->isKind(PNK_SEMI); 1.180 +} 1.181 + 1.182 +static inline ParseNode * 1.183 +ExpressionStatementExpr(ParseNode *pn) 1.184 +{ 1.185 + JS_ASSERT(pn->isKind(PNK_SEMI)); 1.186 + return UnaryKid(pn); 1.187 +} 1.188 + 1.189 +static inline PropertyName * 1.190 +LoopControlMaybeLabel(ParseNode *pn) 1.191 +{ 1.192 + JS_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE)); 1.193 + JS_ASSERT(pn->isArity(PN_NULLARY)); 1.194 + return pn->as<LoopControlStatement>().label(); 1.195 +} 1.196 + 1.197 +static inline PropertyName * 1.198 +LabeledStatementLabel(ParseNode *pn) 1.199 +{ 1.200 + return pn->as<LabeledStatement>().label(); 1.201 +} 1.202 + 1.203 +static inline ParseNode * 1.204 +LabeledStatementStatement(ParseNode *pn) 1.205 +{ 1.206 + return pn->as<LabeledStatement>().statement(); 1.207 +} 1.208 + 1.209 +static double 1.210 +NumberNodeValue(ParseNode *pn) 1.211 +{ 1.212 + JS_ASSERT(pn->isKind(PNK_NUMBER)); 1.213 + return pn->pn_dval; 1.214 +} 1.215 + 1.216 +static bool 1.217 +NumberNodeHasFrac(ParseNode *pn) 1.218 +{ 1.219 + JS_ASSERT(pn->isKind(PNK_NUMBER)); 1.220 + return pn->pn_u.number.decimalPoint == HasDecimal; 1.221 +} 1.222 + 1.223 +static ParseNode * 1.224 +DotBase(ParseNode *pn) 1.225 +{ 1.226 + JS_ASSERT(pn->isKind(PNK_DOT)); 1.227 + JS_ASSERT(pn->isArity(PN_NAME)); 1.228 + return pn->expr(); 1.229 +} 1.230 + 1.231 +static PropertyName * 1.232 +DotMember(ParseNode *pn) 1.233 +{ 1.234 + JS_ASSERT(pn->isKind(PNK_DOT)); 1.235 + JS_ASSERT(pn->isArity(PN_NAME)); 1.236 + return pn->pn_atom->asPropertyName(); 1.237 +} 1.238 + 1.239 +static ParseNode * 1.240 +ElemBase(ParseNode *pn) 1.241 +{ 1.242 + JS_ASSERT(pn->isKind(PNK_ELEM)); 1.243 + return BinaryLeft(pn); 1.244 +} 1.245 + 1.246 +static ParseNode * 1.247 +ElemIndex(ParseNode *pn) 1.248 +{ 1.249 + JS_ASSERT(pn->isKind(PNK_ELEM)); 1.250 + return BinaryRight(pn); 1.251 +} 1.252 + 1.253 +static inline JSFunction * 1.254 +FunctionObject(ParseNode *fn) 1.255 +{ 1.256 + JS_ASSERT(fn->isKind(PNK_FUNCTION)); 1.257 + JS_ASSERT(fn->isArity(PN_CODE)); 1.258 + return fn->pn_funbox->function(); 1.259 +} 1.260 + 1.261 +static inline PropertyName * 1.262 +FunctionName(ParseNode *fn) 1.263 +{ 1.264 + if (JSAtom *atom = FunctionObject(fn)->atom()) 1.265 + return atom->asPropertyName(); 1.266 + return nullptr; 1.267 +} 1.268 + 1.269 +static inline ParseNode * 1.270 +FunctionStatementList(ParseNode *fn) 1.271 +{ 1.272 + JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); 1.273 + ParseNode *last = fn->pn_body->last(); 1.274 + JS_ASSERT(last->isKind(PNK_STATEMENTLIST)); 1.275 + return last; 1.276 +} 1.277 + 1.278 +static inline bool 1.279 +IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn) 1.280 +{ 1.281 + JS_ASSERT(pn->isKind(PNK_COLON)); 1.282 + return pn->getOp() == JSOP_INITPROP && 1.283 + BinaryLeft(pn)->isKind(PNK_NAME) && 1.284 + BinaryLeft(pn)->name() != cx->names().proto; 1.285 +} 1.286 + 1.287 +static inline PropertyName * 1.288 +ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn) 1.289 +{ 1.290 + JS_ASSERT(IsNormalObjectField(cx, pn)); 1.291 + return BinaryLeft(pn)->name(); 1.292 +} 1.293 + 1.294 +static inline ParseNode * 1.295 +ObjectFieldInitializer(ParseNode *pn) 1.296 +{ 1.297 + JS_ASSERT(pn->isKind(PNK_COLON)); 1.298 + return BinaryRight(pn); 1.299 +} 1.300 + 1.301 +static inline bool 1.302 +IsDefinition(ParseNode *pn) 1.303 +{ 1.304 + return pn->isKind(PNK_NAME) && pn->isDefn(); 1.305 +} 1.306 + 1.307 +static inline ParseNode * 1.308 +MaybeDefinitionInitializer(ParseNode *pn) 1.309 +{ 1.310 + JS_ASSERT(IsDefinition(pn)); 1.311 + return pn->expr(); 1.312 +} 1.313 + 1.314 +static inline bool 1.315 +IsUseOfName(ParseNode *pn, PropertyName *name) 1.316 +{ 1.317 + return pn->isKind(PNK_NAME) && pn->name() == name; 1.318 +} 1.319 + 1.320 +static inline bool 1.321 +IsEmptyStatement(ParseNode *pn) 1.322 +{ 1.323 + return pn->isKind(PNK_SEMI) && !UnaryKid(pn); 1.324 +} 1.325 + 1.326 +static inline ParseNode * 1.327 +SkipEmptyStatements(ParseNode *pn) 1.328 +{ 1.329 + while (pn && IsEmptyStatement(pn)) 1.330 + pn = pn->pn_next; 1.331 + return pn; 1.332 +} 1.333 + 1.334 +static inline ParseNode * 1.335 +NextNonEmptyStatement(ParseNode *pn) 1.336 +{ 1.337 + return SkipEmptyStatements(pn->pn_next); 1.338 +} 1.339 + 1.340 +static TokenKind 1.341 +PeekToken(AsmJSParser &parser) 1.342 +{ 1.343 + TokenStream &ts = parser.tokenStream; 1.344 + while (ts.peekToken(TokenStream::Operand) == TOK_SEMI) 1.345 + ts.consumeKnownToken(TOK_SEMI); 1.346 + return ts.peekToken(TokenStream::Operand); 1.347 +} 1.348 + 1.349 +static bool 1.350 +ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var) 1.351 +{ 1.352 + TokenKind tk = PeekToken(parser); 1.353 + if (tk != TOK_VAR && tk != TOK_CONST) { 1.354 + *var = nullptr; 1.355 + return true; 1.356 + } 1.357 + 1.358 + *var = parser.statement(); 1.359 + if (!*var) 1.360 + return false; 1.361 + 1.362 + JS_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST)); 1.363 + return true; 1.364 +} 1.365 + 1.366 +/*****************************************************************************/ 1.367 + 1.368 +namespace { 1.369 + 1.370 +// Respresents the type of a general asm.js expression. 1.371 +class Type 1.372 +{ 1.373 + public: 1.374 + enum Which { 1.375 + Double, 1.376 + MaybeDouble, 1.377 + Float, 1.378 + MaybeFloat, 1.379 + Floatish, 1.380 + Fixnum, 1.381 + Int, 1.382 + Signed, 1.383 + Unsigned, 1.384 + Intish, 1.385 + Void 1.386 + }; 1.387 + 1.388 + private: 1.389 + Which which_; 1.390 + 1.391 + public: 1.392 + Type() : which_(Which(-1)) {} 1.393 + Type(Which w) : which_(w) {} 1.394 + 1.395 + bool operator==(Type rhs) const { return which_ == rhs.which_; } 1.396 + bool operator!=(Type rhs) const { return which_ != rhs.which_; } 1.397 + 1.398 + bool isSigned() const { 1.399 + return which_ == Signed || which_ == Fixnum; 1.400 + } 1.401 + 1.402 + bool isUnsigned() const { 1.403 + return which_ == Unsigned || which_ == Fixnum; 1.404 + } 1.405 + 1.406 + bool isInt() const { 1.407 + return isSigned() || isUnsigned() || which_ == Int; 1.408 + } 1.409 + 1.410 + bool isIntish() const { 1.411 + return isInt() || which_ == Intish; 1.412 + } 1.413 + 1.414 + bool isDouble() const { 1.415 + return which_ == Double; 1.416 + } 1.417 + 1.418 + bool isMaybeDouble() const { 1.419 + return isDouble() || which_ == MaybeDouble; 1.420 + } 1.421 + 1.422 + bool isFloat() const { 1.423 + return which_ == Float; 1.424 + } 1.425 + 1.426 + bool isMaybeFloat() const { 1.427 + return isFloat() || which_ == MaybeFloat; 1.428 + } 1.429 + 1.430 + bool isFloatish() const { 1.431 + return isMaybeFloat() || which_ == Floatish; 1.432 + } 1.433 + 1.434 + bool isVoid() const { 1.435 + return which_ == Void; 1.436 + } 1.437 + 1.438 + bool isExtern() const { 1.439 + return isDouble() || isSigned(); 1.440 + } 1.441 + 1.442 + bool isVarType() const { 1.443 + return isInt() || isDouble() || isFloat(); 1.444 + } 1.445 + 1.446 + MIRType toMIRType() const { 1.447 + switch (which_) { 1.448 + case Double: 1.449 + case MaybeDouble: 1.450 + return MIRType_Double; 1.451 + case Float: 1.452 + case Floatish: 1.453 + case MaybeFloat: 1.454 + return MIRType_Float32; 1.455 + case Fixnum: 1.456 + case Int: 1.457 + case Signed: 1.458 + case Unsigned: 1.459 + case Intish: 1.460 + return MIRType_Int32; 1.461 + case Void: 1.462 + return MIRType_None; 1.463 + } 1.464 + MOZ_ASSUME_UNREACHABLE("Invalid Type"); 1.465 + } 1.466 + 1.467 + const char *toChars() const { 1.468 + switch (which_) { 1.469 + case Double: return "double"; 1.470 + case MaybeDouble: return "double?"; 1.471 + case Float: return "float"; 1.472 + case Floatish: return "floatish"; 1.473 + case MaybeFloat: return "float?"; 1.474 + case Fixnum: return "fixnum"; 1.475 + case Int: return "int"; 1.476 + case Signed: return "signed"; 1.477 + case Unsigned: return "unsigned"; 1.478 + case Intish: return "intish"; 1.479 + case Void: return "void"; 1.480 + } 1.481 + MOZ_ASSUME_UNREACHABLE("Invalid Type"); 1.482 + } 1.483 +}; 1.484 + 1.485 +} /* anonymous namespace */ 1.486 + 1.487 +// Represents the subset of Type that can be used as the return type of a 1.488 +// function. 1.489 +class RetType 1.490 +{ 1.491 + public: 1.492 + enum Which { 1.493 + Void = Type::Void, 1.494 + Signed = Type::Signed, 1.495 + Double = Type::Double, 1.496 + Float = Type::Float 1.497 + }; 1.498 + 1.499 + private: 1.500 + Which which_; 1.501 + 1.502 + public: 1.503 + RetType() : which_(Which(-1)) {} 1.504 + RetType(Which w) : which_(w) {} 1.505 + RetType(AsmJSCoercion coercion) { 1.506 + switch (coercion) { 1.507 + case AsmJS_ToInt32: which_ = Signed; break; 1.508 + case AsmJS_ToNumber: which_ = Double; break; 1.509 + case AsmJS_FRound: which_ = Float; break; 1.510 + } 1.511 + } 1.512 + Which which() const { 1.513 + return which_; 1.514 + } 1.515 + Type toType() const { 1.516 + return Type::Which(which_); 1.517 + } 1.518 + AsmJSModule::ReturnType toModuleReturnType() const { 1.519 + switch (which_) { 1.520 + case Void: return AsmJSModule::Return_Void; 1.521 + case Signed: return AsmJSModule::Return_Int32; 1.522 + case Float: // will be converted to a Double 1.523 + case Double: return AsmJSModule::Return_Double; 1.524 + } 1.525 + MOZ_ASSUME_UNREACHABLE("Unexpected return type"); 1.526 + } 1.527 + MIRType toMIRType() const { 1.528 + switch (which_) { 1.529 + case Void: return MIRType_None; 1.530 + case Signed: return MIRType_Int32; 1.531 + case Double: return MIRType_Double; 1.532 + case Float: return MIRType_Float32; 1.533 + } 1.534 + MOZ_ASSUME_UNREACHABLE("Unexpected return type"); 1.535 + } 1.536 + bool operator==(RetType rhs) const { return which_ == rhs.which_; } 1.537 + bool operator!=(RetType rhs) const { return which_ != rhs.which_; } 1.538 +}; 1.539 + 1.540 +namespace { 1.541 + 1.542 +// Represents the subset of Type that can be used as a variable or 1.543 +// argument's type. Note: AsmJSCoercion and VarType are kept separate to 1.544 +// make very clear the signed/int distinction: a coercion may explicitly sign 1.545 +// an *expression* but, when stored as a variable, this signedness information 1.546 +// is explicitly thrown away by the asm.js type system. E.g., in 1.547 +// 1.548 +// function f(i) { 1.549 +// i = i | 0; (1) 1.550 +// if (...) 1.551 +// i = foo() >>> 0; 1.552 +// else 1.553 +// i = bar() | 0; 1.554 +// return i | 0; (2) 1.555 +// } 1.556 +// 1.557 +// the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when 1.558 +// translated to an VarType, the result is a plain Int since, as shown, it 1.559 +// is legal to assign both Signed and Unsigned (or some other Int) values to 1.560 +// it. For (2), the AsmJSCoercion is also Signed but, when translated to an 1.561 +// RetType, the result is Signed since callers (asm.js and non-asm.js) can 1.562 +// rely on the return value being Signed. 1.563 +class VarType 1.564 +{ 1.565 + public: 1.566 + enum Which { 1.567 + Int = Type::Int, 1.568 + Double = Type::Double, 1.569 + Float = Type::Float 1.570 + }; 1.571 + 1.572 + private: 1.573 + Which which_; 1.574 + 1.575 + public: 1.576 + VarType() 1.577 + : which_(Which(-1)) {} 1.578 + VarType(Which w) 1.579 + : which_(w) {} 1.580 + VarType(AsmJSCoercion coercion) { 1.581 + switch (coercion) { 1.582 + case AsmJS_ToInt32: which_ = Int; break; 1.583 + case AsmJS_ToNumber: which_ = Double; break; 1.584 + case AsmJS_FRound: which_ = Float; break; 1.585 + } 1.586 + } 1.587 + Which which() const { 1.588 + return which_; 1.589 + } 1.590 + Type toType() const { 1.591 + return Type::Which(which_); 1.592 + } 1.593 + MIRType toMIRType() const { 1.594 + switch(which_) { 1.595 + case Int: return MIRType_Int32; 1.596 + case Double: return MIRType_Double; 1.597 + case Float: return MIRType_Float32; 1.598 + } 1.599 + MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); 1.600 + } 1.601 + AsmJSCoercion toCoercion() const { 1.602 + switch(which_) { 1.603 + case Int: return AsmJS_ToInt32; 1.604 + case Double: return AsmJS_ToNumber; 1.605 + case Float: return AsmJS_FRound; 1.606 + } 1.607 + MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); 1.608 + } 1.609 + static VarType FromCheckedType(Type type) { 1.610 + JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish()); 1.611 + if (type.isMaybeDouble()) 1.612 + return Double; 1.613 + else if (type.isFloatish()) 1.614 + return Float; 1.615 + else 1.616 + return Int; 1.617 + } 1.618 + bool operator==(VarType rhs) const { return which_ == rhs.which_; } 1.619 + bool operator!=(VarType rhs) const { return which_ != rhs.which_; } 1.620 +}; 1.621 + 1.622 +} /* anonymous namespace */ 1.623 + 1.624 +// Implements <: (subtype) operator when the rhs is an VarType 1.625 +static inline bool 1.626 +operator<=(Type lhs, VarType rhs) 1.627 +{ 1.628 + switch (rhs.which()) { 1.629 + case VarType::Int: return lhs.isInt(); 1.630 + case VarType::Double: return lhs.isDouble(); 1.631 + case VarType::Float: return lhs.isFloat(); 1.632 + } 1.633 + MOZ_ASSUME_UNREACHABLE("Unexpected rhs type"); 1.634 +} 1.635 + 1.636 +/*****************************************************************************/ 1.637 + 1.638 +static inline MIRType ToMIRType(MIRType t) { return t; } 1.639 +static inline MIRType ToMIRType(VarType t) { return t.toMIRType(); } 1.640 + 1.641 +namespace { 1.642 + 1.643 +template <class VecT> 1.644 +class ABIArgIter 1.645 +{ 1.646 + ABIArgGenerator gen_; 1.647 + const VecT &types_; 1.648 + unsigned i_; 1.649 + 1.650 + void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); } 1.651 + 1.652 + public: 1.653 + ABIArgIter(const VecT &types) : types_(types), i_(0) { settle(); } 1.654 + void operator++(int) { JS_ASSERT(!done()); i_++; settle(); } 1.655 + bool done() const { return i_ == types_.length(); } 1.656 + 1.657 + ABIArg *operator->() { JS_ASSERT(!done()); return &gen_.current(); } 1.658 + ABIArg &operator*() { JS_ASSERT(!done()); return gen_.current(); } 1.659 + 1.660 + unsigned index() const { JS_ASSERT(!done()); return i_; } 1.661 + MIRType mirType() const { JS_ASSERT(!done()); return ToMIRType(types_[i_]); } 1.662 + uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); } 1.663 +}; 1.664 + 1.665 +typedef js::Vector<MIRType, 8> MIRTypeVector; 1.666 +typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter; 1.667 + 1.668 +typedef js::Vector<VarType, 8, LifoAllocPolicy> VarTypeVector; 1.669 +typedef ABIArgIter<VarTypeVector> ABIArgTypeIter; 1.670 + 1.671 +class Signature 1.672 +{ 1.673 + VarTypeVector argTypes_; 1.674 + RetType retType_; 1.675 + 1.676 + public: 1.677 + Signature(LifoAlloc &alloc) 1.678 + : argTypes_(alloc) {} 1.679 + Signature(LifoAlloc &alloc, RetType retType) 1.680 + : argTypes_(alloc), retType_(retType) {} 1.681 + Signature(VarTypeVector &&argTypes, RetType retType) 1.682 + : argTypes_(Move(argTypes)), retType_(Move(retType)) {} 1.683 + Signature(Signature &&rhs) 1.684 + : argTypes_(Move(rhs.argTypes_)), retType_(Move(rhs.retType_)) {} 1.685 + 1.686 + bool copy(const Signature &rhs) { 1.687 + if (!argTypes_.resize(rhs.argTypes_.length())) 1.688 + return false; 1.689 + for (unsigned i = 0; i < argTypes_.length(); i++) 1.690 + argTypes_[i] = rhs.argTypes_[i]; 1.691 + retType_ = rhs.retType_; 1.692 + return true; 1.693 + } 1.694 + 1.695 + bool appendArg(VarType type) { return argTypes_.append(type); } 1.696 + VarType arg(unsigned i) const { return argTypes_[i]; } 1.697 + const VarTypeVector &args() const { return argTypes_; } 1.698 + VarTypeVector &&extractArgs() { return Move(argTypes_); } 1.699 + 1.700 + RetType retType() const { return retType_; } 1.701 +}; 1.702 + 1.703 +} /* namespace anonymous */ 1.704 + 1.705 +static 1.706 +bool operator==(const Signature &lhs, const Signature &rhs) 1.707 +{ 1.708 + if (lhs.retType() != rhs.retType()) 1.709 + return false; 1.710 + if (lhs.args().length() != rhs.args().length()) 1.711 + return false; 1.712 + for (unsigned i = 0; i < lhs.args().length(); i++) { 1.713 + if (lhs.arg(i) != rhs.arg(i)) 1.714 + return false; 1.715 + } 1.716 + return true; 1.717 +} 1.718 + 1.719 +static inline 1.720 +bool operator!=(const Signature &lhs, const Signature &rhs) 1.721 +{ 1.722 + return !(lhs == rhs); 1.723 +} 1.724 + 1.725 +/*****************************************************************************/ 1.726 +// Typed array utilities 1.727 + 1.728 +static Type 1.729 +TypedArrayLoadType(ArrayBufferView::ViewType viewType) 1.730 +{ 1.731 + switch (viewType) { 1.732 + case ArrayBufferView::TYPE_INT8: 1.733 + case ArrayBufferView::TYPE_INT16: 1.734 + case ArrayBufferView::TYPE_INT32: 1.735 + case ArrayBufferView::TYPE_UINT8: 1.736 + case ArrayBufferView::TYPE_UINT16: 1.737 + case ArrayBufferView::TYPE_UINT32: 1.738 + return Type::Intish; 1.739 + case ArrayBufferView::TYPE_FLOAT32: 1.740 + return Type::MaybeFloat; 1.741 + case ArrayBufferView::TYPE_FLOAT64: 1.742 + return Type::MaybeDouble; 1.743 + default:; 1.744 + } 1.745 + MOZ_ASSUME_UNREACHABLE("Unexpected array type"); 1.746 +} 1.747 + 1.748 +enum NeedsBoundsCheck { 1.749 + NO_BOUNDS_CHECK, 1.750 + NEEDS_BOUNDS_CHECK 1.751 +}; 1.752 + 1.753 +namespace { 1.754 + 1.755 +typedef js::Vector<PropertyName*,1> LabelVector; 1.756 +typedef js::Vector<MBasicBlock*,8> BlockVector; 1.757 + 1.758 +// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over 1.759 +// the course of an ModuleCompiler object's lifetime, many FunctionCompiler 1.760 +// objects will be created and destroyed in sequence, one for each function in 1.761 +// the module. 1.762 +// 1.763 +// *** asm.js FFI calls *** 1.764 +// 1.765 +// asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type 1.766 +// system does not place any constraints on the FFI call. In particular: 1.767 +// - an FFI call's target is not known or speculated at module-compile time; 1.768 +// - a single external function can be called with different signatures. 1.769 +// 1.770 +// If performance didn't matter, all FFI calls could simply box their arguments 1.771 +// and call js::Invoke. However, we'd like to be able to specialize FFI calls 1.772 +// to be more efficient in several cases: 1.773 +// 1.774 +// - for calls to JS functions which have been jitted, we'd like to call 1.775 +// directly into JIT code without going through C++. 1.776 +// 1.777 +// - for calls to certain builtins, we'd like to be call directly into the C++ 1.778 +// code for the builtin without going through the general call path. 1.779 +// 1.780 +// All of this requires dynamic specialization techniques which must happen 1.781 +// after module compilation. To support this, at module-compilation time, each 1.782 +// FFI call generates a call signature according to the system ABI, as if the 1.783 +// callee was a C++ function taking/returning the same types as the caller was 1.784 +// passing/expecting. The callee is loaded from a fixed offset in the global 1.785 +// data array which allows the callee to change at runtime. Initially, the 1.786 +// callee is stub which boxes its arguments and calls js::Invoke. 1.787 +// 1.788 +// To do this, we need to generate a callee stub for each pairing of FFI callee 1.789 +// and signature. We call this pairing an "exit". For example, this code has 1.790 +// two external functions and three exits: 1.791 +// 1.792 +// function f(global, imports) { 1.793 +// "use asm"; 1.794 +// var foo = imports.foo; 1.795 +// var bar = imports.bar; 1.796 +// function g() { 1.797 +// foo(1); // Exit #1: (int) -> void 1.798 +// foo(1.5); // Exit #2: (double) -> void 1.799 +// bar(1)|0; // Exit #3: (int) -> int 1.800 +// bar(2)|0; // Exit #3: (int) -> int 1.801 +// } 1.802 +// } 1.803 +// 1.804 +// The ModuleCompiler maintains a hash table (ExitMap) which allows a call site 1.805 +// to add a new exit or reuse an existing one. The key is an ExitDescriptor 1.806 +// (which holds the exit pairing) and the value is an index into the 1.807 +// Vector<Exit> stored in the AsmJSModule. 1.808 +// 1.809 +// Rooting note: ModuleCompiler is a stack class that contains unrooted 1.810 +// PropertyName (JSAtom) pointers. This is safe because it cannot be 1.811 +// constructed without a TokenStream reference. TokenStream is itself a stack 1.812 +// class that cannot be constructed without an AutoKeepAtoms being live on the 1.813 +// stack, which prevents collection of atoms. 1.814 +// 1.815 +// ModuleCompiler is marked as rooted in the rooting analysis. Don't add 1.816 +// non-JSAtom pointers, or this will break! 1.817 +class MOZ_STACK_CLASS ModuleCompiler 1.818 +{ 1.819 + public: 1.820 + class Func 1.821 + { 1.822 + PropertyName *name_; 1.823 + bool defined_; 1.824 + uint32_t srcOffset_; 1.825 + uint32_t endOffset_; 1.826 + Signature sig_; 1.827 + Label *code_; 1.828 + unsigned compileTime_; 1.829 + 1.830 + public: 1.831 + Func(PropertyName *name, Signature &&sig, Label *code) 1.832 + : name_(name), defined_(false), srcOffset_(0), endOffset_(0), sig_(Move(sig)), 1.833 + code_(code), compileTime_(0) 1.834 + {} 1.835 + 1.836 + PropertyName *name() const { return name_; } 1.837 + 1.838 + bool defined() const { return defined_; } 1.839 + void finish(uint32_t start, uint32_t end) { 1.840 + JS_ASSERT(!defined_); 1.841 + defined_ = true; 1.842 + srcOffset_ = start; 1.843 + endOffset_ = end; 1.844 + } 1.845 + 1.846 + uint32_t srcOffset() const { JS_ASSERT(defined_); return srcOffset_; } 1.847 + uint32_t endOffset() const { JS_ASSERT(defined_); return endOffset_; } 1.848 + Signature &sig() { return sig_; } 1.849 + const Signature &sig() const { return sig_; } 1.850 + Label *code() const { return code_; } 1.851 + unsigned compileTime() const { return compileTime_; } 1.852 + void accumulateCompileTime(unsigned ms) { compileTime_ += ms; } 1.853 + }; 1.854 + 1.855 + class Global 1.856 + { 1.857 + public: 1.858 + enum Which { 1.859 + Variable, 1.860 + ConstantLiteral, 1.861 + ConstantImport, 1.862 + Function, 1.863 + FuncPtrTable, 1.864 + FFI, 1.865 + ArrayView, 1.866 + MathBuiltinFunction 1.867 + }; 1.868 + 1.869 + private: 1.870 + Which which_; 1.871 + union { 1.872 + struct { 1.873 + VarType::Which type_; 1.874 + uint32_t index_; 1.875 + Value literalValue_; 1.876 + } varOrConst; 1.877 + uint32_t funcIndex_; 1.878 + uint32_t funcPtrTableIndex_; 1.879 + uint32_t ffiIndex_; 1.880 + ArrayBufferView::ViewType viewType_; 1.881 + AsmJSMathBuiltinFunction mathBuiltinFunc_; 1.882 + } u; 1.883 + 1.884 + friend class ModuleCompiler; 1.885 + friend class js::LifoAlloc; 1.886 + 1.887 + Global(Which which) : which_(which) {} 1.888 + 1.889 + public: 1.890 + Which which() const { 1.891 + return which_; 1.892 + } 1.893 + VarType varOrConstType() const { 1.894 + JS_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport); 1.895 + return VarType(u.varOrConst.type_); 1.896 + } 1.897 + uint32_t varOrConstIndex() const { 1.898 + JS_ASSERT(which_ == Variable || which_ == ConstantImport); 1.899 + return u.varOrConst.index_; 1.900 + } 1.901 + bool isConst() const { 1.902 + return which_ == ConstantLiteral || which_ == ConstantImport; 1.903 + } 1.904 + Value constLiteralValue() const { 1.905 + JS_ASSERT(which_ == ConstantLiteral); 1.906 + return u.varOrConst.literalValue_; 1.907 + } 1.908 + uint32_t funcIndex() const { 1.909 + JS_ASSERT(which_ == Function); 1.910 + return u.funcIndex_; 1.911 + } 1.912 + uint32_t funcPtrTableIndex() const { 1.913 + JS_ASSERT(which_ == FuncPtrTable); 1.914 + return u.funcPtrTableIndex_; 1.915 + } 1.916 + unsigned ffiIndex() const { 1.917 + JS_ASSERT(which_ == FFI); 1.918 + return u.ffiIndex_; 1.919 + } 1.920 + ArrayBufferView::ViewType viewType() const { 1.921 + JS_ASSERT(which_ == ArrayView); 1.922 + return u.viewType_; 1.923 + } 1.924 + AsmJSMathBuiltinFunction mathBuiltinFunction() const { 1.925 + JS_ASSERT(which_ == MathBuiltinFunction); 1.926 + return u.mathBuiltinFunc_; 1.927 + } 1.928 + }; 1.929 + 1.930 + typedef js::Vector<const Func*> FuncPtrVector; 1.931 + 1.932 + class FuncPtrTable 1.933 + { 1.934 + Signature sig_; 1.935 + uint32_t mask_; 1.936 + uint32_t globalDataOffset_; 1.937 + FuncPtrVector elems_; 1.938 + 1.939 + public: 1.940 + FuncPtrTable(ExclusiveContext *cx, Signature &&sig, uint32_t mask, uint32_t gdo) 1.941 + : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), elems_(cx) 1.942 + {} 1.943 + 1.944 + FuncPtrTable(FuncPtrTable &&rhs) 1.945 + : sig_(Move(rhs.sig_)), mask_(rhs.mask_), globalDataOffset_(rhs.globalDataOffset_), 1.946 + elems_(Move(rhs.elems_)) 1.947 + {} 1.948 + 1.949 + Signature &sig() { return sig_; } 1.950 + const Signature &sig() const { return sig_; } 1.951 + unsigned mask() const { return mask_; } 1.952 + unsigned globalDataOffset() const { return globalDataOffset_; } 1.953 + 1.954 + bool initialized() const { return !elems_.empty(); } 1.955 + void initElems(FuncPtrVector &&elems) { elems_ = Move(elems); JS_ASSERT(initialized()); } 1.956 + unsigned numElems() const { JS_ASSERT(initialized()); return elems_.length(); } 1.957 + const Func &elem(unsigned i) const { return *elems_[i]; } 1.958 + }; 1.959 + 1.960 + typedef js::Vector<FuncPtrTable> FuncPtrTableVector; 1.961 + 1.962 + class ExitDescriptor 1.963 + { 1.964 + PropertyName *name_; 1.965 + Signature sig_; 1.966 + 1.967 + public: 1.968 + ExitDescriptor(PropertyName *name, Signature &&sig) 1.969 + : name_(name), sig_(Move(sig)) {} 1.970 + ExitDescriptor(ExitDescriptor &&rhs) 1.971 + : name_(rhs.name_), sig_(Move(rhs.sig_)) 1.972 + {} 1.973 + const Signature &sig() const { 1.974 + return sig_; 1.975 + } 1.976 + 1.977 + // ExitDescriptor is a HashPolicy: 1.978 + typedef ExitDescriptor Lookup; 1.979 + static HashNumber hash(const ExitDescriptor &d) { 1.980 + HashNumber hn = HashGeneric(d.name_, d.sig_.retType().which()); 1.981 + const VarTypeVector &args = d.sig_.args(); 1.982 + for (unsigned i = 0; i < args.length(); i++) 1.983 + hn = AddToHash(hn, args[i].which()); 1.984 + return hn; 1.985 + } 1.986 + static bool match(const ExitDescriptor &lhs, const ExitDescriptor &rhs) { 1.987 + return lhs.name_ == rhs.name_ && lhs.sig_ == rhs.sig_; 1.988 + } 1.989 + }; 1.990 + 1.991 + typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap; 1.992 + 1.993 + struct MathBuiltin 1.994 + { 1.995 + enum Kind { Function, Constant }; 1.996 + Kind kind; 1.997 + 1.998 + union { 1.999 + double cst; 1.1000 + AsmJSMathBuiltinFunction func; 1.1001 + } u; 1.1002 + 1.1003 + MathBuiltin() : kind(Kind(-1)) {} 1.1004 + MathBuiltin(double cst) : kind(Constant) { 1.1005 + u.cst = cst; 1.1006 + } 1.1007 + MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) { 1.1008 + u.func = func; 1.1009 + } 1.1010 + }; 1.1011 + 1.1012 + private: 1.1013 + struct SlowFunction 1.1014 + { 1.1015 + PropertyName *name; 1.1016 + unsigned ms; 1.1017 + unsigned line; 1.1018 + unsigned column; 1.1019 + }; 1.1020 + 1.1021 + typedef HashMap<PropertyName*, MathBuiltin> MathNameMap; 1.1022 + typedef HashMap<PropertyName*, Global*> GlobalMap; 1.1023 + typedef js::Vector<Func*> FuncVector; 1.1024 + typedef js::Vector<AsmJSGlobalAccess> GlobalAccessVector; 1.1025 + typedef js::Vector<SlowFunction> SlowFunctionVector; 1.1026 + 1.1027 + ExclusiveContext * cx_; 1.1028 + AsmJSParser & parser_; 1.1029 + 1.1030 + MacroAssembler masm_; 1.1031 + 1.1032 + ScopedJSDeletePtr<AsmJSModule> module_; 1.1033 + LifoAlloc moduleLifo_; 1.1034 + ParseNode * moduleFunctionNode_; 1.1035 + PropertyName * moduleFunctionName_; 1.1036 + 1.1037 + GlobalMap globals_; 1.1038 + FuncVector functions_; 1.1039 + FuncPtrTableVector funcPtrTables_; 1.1040 + ExitMap exits_; 1.1041 + MathNameMap standardLibraryMathNames_; 1.1042 + Label stackOverflowLabel_; 1.1043 + Label interruptLabel_; 1.1044 + 1.1045 + char * errorString_; 1.1046 + uint32_t errorOffset_; 1.1047 + bool errorOverRecursed_; 1.1048 + 1.1049 + int64_t usecBefore_; 1.1050 + SlowFunctionVector slowFunctions_; 1.1051 + 1.1052 + DebugOnly<bool> finishedFunctionBodies_; 1.1053 + 1.1054 + bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) { 1.1055 + JSAtom *atom = Atomize(cx_, name, strlen(name)); 1.1056 + if (!atom) 1.1057 + return false; 1.1058 + MathBuiltin builtin(func); 1.1059 + return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin); 1.1060 + } 1.1061 + bool addStandardLibraryMathName(const char *name, double cst) { 1.1062 + JSAtom *atom = Atomize(cx_, name, strlen(name)); 1.1063 + if (!atom) 1.1064 + return false; 1.1065 + MathBuiltin builtin(cst); 1.1066 + return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin); 1.1067 + } 1.1068 + 1.1069 + public: 1.1070 + ModuleCompiler(ExclusiveContext *cx, AsmJSParser &parser) 1.1071 + : cx_(cx), 1.1072 + parser_(parser), 1.1073 + masm_(MacroAssembler::AsmJSToken()), 1.1074 + moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE), 1.1075 + moduleFunctionNode_(parser.pc->maybeFunction), 1.1076 + moduleFunctionName_(nullptr), 1.1077 + globals_(cx), 1.1078 + functions_(cx), 1.1079 + funcPtrTables_(cx), 1.1080 + exits_(cx), 1.1081 + standardLibraryMathNames_(cx), 1.1082 + errorString_(nullptr), 1.1083 + errorOffset_(UINT32_MAX), 1.1084 + errorOverRecursed_(false), 1.1085 + usecBefore_(PRMJ_Now()), 1.1086 + slowFunctions_(cx), 1.1087 + finishedFunctionBodies_(false) 1.1088 + { 1.1089 + JS_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox()); 1.1090 + } 1.1091 + 1.1092 + ~ModuleCompiler() { 1.1093 + if (errorString_) { 1.1094 + JS_ASSERT(errorOffset_ != UINT32_MAX); 1.1095 + tokenStream().reportAsmJSError(errorOffset_, 1.1096 + JSMSG_USE_ASM_TYPE_FAIL, 1.1097 + errorString_); 1.1098 + js_free(errorString_); 1.1099 + } 1.1100 + if (errorOverRecursed_) 1.1101 + js_ReportOverRecursed(cx_); 1.1102 + 1.1103 + // Avoid spurious Label assertions on compilation failure. 1.1104 + if (!stackOverflowLabel_.bound()) 1.1105 + stackOverflowLabel_.bind(0); 1.1106 + if (!interruptLabel_.bound()) 1.1107 + interruptLabel_.bind(0); 1.1108 + } 1.1109 + 1.1110 + bool init() { 1.1111 + if (!globals_.init() || !exits_.init()) 1.1112 + return false; 1.1113 + 1.1114 + if (!standardLibraryMathNames_.init() || 1.1115 + !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) || 1.1116 + !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) || 1.1117 + !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) || 1.1118 + !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) || 1.1119 + !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) || 1.1120 + !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) || 1.1121 + !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) || 1.1122 + !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) || 1.1123 + !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) || 1.1124 + !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) || 1.1125 + !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) || 1.1126 + !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) || 1.1127 + !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) || 1.1128 + !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) || 1.1129 + !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) || 1.1130 + !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) || 1.1131 + !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) || 1.1132 + !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) || 1.1133 + !addStandardLibraryMathName("E", M_E) || 1.1134 + !addStandardLibraryMathName("LN10", M_LN10) || 1.1135 + !addStandardLibraryMathName("LN2", M_LN2) || 1.1136 + !addStandardLibraryMathName("LOG2E", M_LOG2E) || 1.1137 + !addStandardLibraryMathName("LOG10E", M_LOG10E) || 1.1138 + !addStandardLibraryMathName("PI", M_PI) || 1.1139 + !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) || 1.1140 + !addStandardLibraryMathName("SQRT2", M_SQRT2)) 1.1141 + { 1.1142 + return false; 1.1143 + } 1.1144 + 1.1145 + uint32_t funcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin; 1.1146 + uint32_t offsetToEndOfUseAsm = tokenStream().currentToken().pos.end; 1.1147 + 1.1148 + // "use strict" should be added to the source if we are in an implicit 1.1149 + // strict context, see also comment above addUseStrict in 1.1150 + // js::FunctionToString. 1.1151 + bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict(); 1.1152 + 1.1153 + module_ = cx_->new_<AsmJSModule>(parser_.ss, funcStart, offsetToEndOfUseAsm, strict); 1.1154 + if (!module_) 1.1155 + return false; 1.1156 + 1.1157 + return true; 1.1158 + } 1.1159 + 1.1160 + bool failOffset(uint32_t offset, const char *str) { 1.1161 + JS_ASSERT(!errorString_); 1.1162 + JS_ASSERT(errorOffset_ == UINT32_MAX); 1.1163 + JS_ASSERT(str); 1.1164 + errorOffset_ = offset; 1.1165 + errorString_ = js_strdup(cx_, str); 1.1166 + return false; 1.1167 + } 1.1168 + 1.1169 + bool fail(ParseNode *pn, const char *str) { 1.1170 + if (pn) 1.1171 + return failOffset(pn->pn_pos.begin, str); 1.1172 + 1.1173 + // The exact rooting static analysis does not perform dataflow analysis, so it believes 1.1174 + // that unrooted things on the stack during compilation may still be accessed after this. 1.1175 + // Since pn is typically only null under OOM, this suppression simply forces any GC to be 1.1176 + // delayed until the compilation is off the stack and more memory can be freed. 1.1177 + gc::AutoSuppressGC nogc(cx_); 1.1178 + return failOffset(tokenStream().peekTokenPos().begin, str); 1.1179 + } 1.1180 + 1.1181 + bool failfVA(ParseNode *pn, const char *fmt, va_list ap) { 1.1182 + JS_ASSERT(!errorString_); 1.1183 + JS_ASSERT(errorOffset_ == UINT32_MAX); 1.1184 + JS_ASSERT(fmt); 1.1185 + errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end; 1.1186 + errorString_ = JS_vsmprintf(fmt, ap); 1.1187 + return false; 1.1188 + } 1.1189 + 1.1190 + bool failf(ParseNode *pn, const char *fmt, ...) { 1.1191 + va_list ap; 1.1192 + va_start(ap, fmt); 1.1193 + failfVA(pn, fmt, ap); 1.1194 + va_end(ap); 1.1195 + return false; 1.1196 + } 1.1197 + 1.1198 + bool failName(ParseNode *pn, const char *fmt, PropertyName *name) { 1.1199 + // This function is invoked without the caller properly rooting its locals. 1.1200 + gc::AutoSuppressGC suppress(cx_); 1.1201 + JSAutoByteString bytes; 1.1202 + if (AtomToPrintableString(cx_, name, &bytes)) 1.1203 + failf(pn, fmt, bytes.ptr()); 1.1204 + return false; 1.1205 + } 1.1206 + 1.1207 + bool failOverRecursed() { 1.1208 + errorOverRecursed_ = true; 1.1209 + return false; 1.1210 + } 1.1211 + 1.1212 + static const unsigned SLOW_FUNCTION_THRESHOLD_MS = 250; 1.1213 + 1.1214 + bool maybeReportCompileTime(const Func &func) { 1.1215 + if (func.compileTime() < SLOW_FUNCTION_THRESHOLD_MS) 1.1216 + return true; 1.1217 + SlowFunction sf; 1.1218 + sf.name = func.name(); 1.1219 + sf.ms = func.compileTime(); 1.1220 + tokenStream().srcCoords.lineNumAndColumnIndex(func.srcOffset(), &sf.line, &sf.column); 1.1221 + return slowFunctions_.append(sf); 1.1222 + } 1.1223 + 1.1224 + /*************************************************** Read-only interface */ 1.1225 + 1.1226 + ExclusiveContext *cx() const { return cx_; } 1.1227 + AsmJSParser &parser() const { return parser_; } 1.1228 + TokenStream &tokenStream() const { return parser_.tokenStream; } 1.1229 + MacroAssembler &masm() { return masm_; } 1.1230 + Label &stackOverflowLabel() { return stackOverflowLabel_; } 1.1231 + Label &interruptLabel() { return interruptLabel_; } 1.1232 + bool hasError() const { return errorString_ != nullptr; } 1.1233 + const AsmJSModule &module() const { return *module_.get(); } 1.1234 + uint32_t moduleStart() const { return module_->funcStart(); } 1.1235 + 1.1236 + ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; } 1.1237 + PropertyName *moduleFunctionName() const { return moduleFunctionName_; } 1.1238 + 1.1239 + const Global *lookupGlobal(PropertyName *name) const { 1.1240 + if (GlobalMap::Ptr p = globals_.lookup(name)) 1.1241 + return p->value(); 1.1242 + return nullptr; 1.1243 + } 1.1244 + Func *lookupFunction(PropertyName *name) { 1.1245 + if (GlobalMap::Ptr p = globals_.lookup(name)) { 1.1246 + Global *value = p->value(); 1.1247 + if (value->which() == Global::Function) 1.1248 + return functions_[value->funcIndex()]; 1.1249 + } 1.1250 + return nullptr; 1.1251 + } 1.1252 + unsigned numFunctions() const { 1.1253 + return functions_.length(); 1.1254 + } 1.1255 + Func &function(unsigned i) { 1.1256 + return *functions_[i]; 1.1257 + } 1.1258 + unsigned numFuncPtrTables() const { 1.1259 + return funcPtrTables_.length(); 1.1260 + } 1.1261 + FuncPtrTable &funcPtrTable(unsigned i) { 1.1262 + return funcPtrTables_[i]; 1.1263 + } 1.1264 + bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const { 1.1265 + if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) { 1.1266 + *mathBuiltin = p->value(); 1.1267 + return true; 1.1268 + } 1.1269 + return false; 1.1270 + } 1.1271 + ExitMap::Range allExits() const { 1.1272 + return exits_.all(); 1.1273 + } 1.1274 + 1.1275 + /***************************************************** Mutable interface */ 1.1276 + 1.1277 + void initModuleFunctionName(PropertyName *name) { moduleFunctionName_ = name; } 1.1278 + 1.1279 + void initGlobalArgumentName(PropertyName *n) { module_->initGlobalArgumentName(n); } 1.1280 + void initImportArgumentName(PropertyName *n) { module_->initImportArgumentName(n); } 1.1281 + void initBufferArgumentName(PropertyName *n) { module_->initBufferArgumentName(n); } 1.1282 + 1.1283 + bool addGlobalVarInit(PropertyName *varName, VarType type, const Value &v, bool isConst) { 1.1284 + uint32_t index; 1.1285 + if (!module_->addGlobalVarInit(v, type.toCoercion(), &index)) 1.1286 + return false; 1.1287 + 1.1288 + Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; 1.1289 + Global *global = moduleLifo_.new_<Global>(which); 1.1290 + if (!global) 1.1291 + return false; 1.1292 + global->u.varOrConst.index_ = index; 1.1293 + global->u.varOrConst.type_ = type.which(); 1.1294 + if (isConst) 1.1295 + global->u.varOrConst.literalValue_ = v; 1.1296 + 1.1297 + return globals_.putNew(varName, global); 1.1298 + } 1.1299 + bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion, 1.1300 + bool isConst) { 1.1301 + uint32_t index; 1.1302 + if (!module_->addGlobalVarImport(fieldName, coercion, &index)) 1.1303 + return false; 1.1304 + 1.1305 + Global::Which which = isConst ? Global::ConstantImport : Global::Variable; 1.1306 + Global *global = moduleLifo_.new_<Global>(which); 1.1307 + if (!global) 1.1308 + return false; 1.1309 + global->u.varOrConst.index_ = index; 1.1310 + global->u.varOrConst.type_ = VarType(coercion).which(); 1.1311 + 1.1312 + return globals_.putNew(varName, global); 1.1313 + } 1.1314 + bool addFunction(PropertyName *name, Signature &&sig, Func **func) { 1.1315 + JS_ASSERT(!finishedFunctionBodies_); 1.1316 + Global *global = moduleLifo_.new_<Global>(Global::Function); 1.1317 + if (!global) 1.1318 + return false; 1.1319 + global->u.funcIndex_ = functions_.length(); 1.1320 + if (!globals_.putNew(name, global)) 1.1321 + return false; 1.1322 + Label *code = moduleLifo_.new_<Label>(); 1.1323 + if (!code) 1.1324 + return false; 1.1325 + *func = moduleLifo_.new_<Func>(name, Move(sig), code); 1.1326 + if (!*func) 1.1327 + return false; 1.1328 + return functions_.append(*func); 1.1329 + } 1.1330 + bool addFuncPtrTable(PropertyName *name, Signature &&sig, uint32_t mask, FuncPtrTable **table) { 1.1331 + Global *global = moduleLifo_.new_<Global>(Global::FuncPtrTable); 1.1332 + if (!global) 1.1333 + return false; 1.1334 + global->u.funcPtrTableIndex_ = funcPtrTables_.length(); 1.1335 + if (!globals_.putNew(name, global)) 1.1336 + return false; 1.1337 + uint32_t globalDataOffset; 1.1338 + if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset)) 1.1339 + return false; 1.1340 + FuncPtrTable tmpTable(cx_, Move(sig), mask, globalDataOffset); 1.1341 + if (!funcPtrTables_.append(Move(tmpTable))) 1.1342 + return false; 1.1343 + *table = &funcPtrTables_.back(); 1.1344 + return true; 1.1345 + } 1.1346 + bool addFFI(PropertyName *varName, PropertyName *field) { 1.1347 + Global *global = moduleLifo_.new_<Global>(Global::FFI); 1.1348 + if (!global) 1.1349 + return false; 1.1350 + uint32_t index; 1.1351 + if (!module_->addFFI(field, &index)) 1.1352 + return false; 1.1353 + global->u.ffiIndex_ = index; 1.1354 + return globals_.putNew(varName, global); 1.1355 + } 1.1356 + bool addArrayView(PropertyName *varName, ArrayBufferView::ViewType vt, PropertyName *fieldName) { 1.1357 + Global *global = moduleLifo_.new_<Global>(Global::ArrayView); 1.1358 + if (!global) 1.1359 + return false; 1.1360 + if (!module_->addArrayView(vt, fieldName)) 1.1361 + return false; 1.1362 + global->u.viewType_ = vt; 1.1363 + return globals_.putNew(varName, global); 1.1364 + } 1.1365 + bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) { 1.1366 + if (!module_->addMathBuiltinFunction(func, fieldName)) 1.1367 + return false; 1.1368 + Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction); 1.1369 + if (!global) 1.1370 + return false; 1.1371 + global->u.mathBuiltinFunc_ = func; 1.1372 + return globals_.putNew(varName, global); 1.1373 + } 1.1374 + private: 1.1375 + bool addGlobalDoubleConstant(PropertyName *varName, double constant) { 1.1376 + Global *global = moduleLifo_.new_<Global>(Global::ConstantLiteral); 1.1377 + if (!global) 1.1378 + return false; 1.1379 + global->u.varOrConst.literalValue_ = DoubleValue(constant); 1.1380 + global->u.varOrConst.type_ = VarType::Double; 1.1381 + return globals_.putNew(varName, global); 1.1382 + } 1.1383 + public: 1.1384 + bool addMathBuiltinConstant(PropertyName *varName, double constant, PropertyName *fieldName) { 1.1385 + if (!module_->addMathBuiltinConstant(constant, fieldName)) 1.1386 + return false; 1.1387 + return addGlobalDoubleConstant(varName, constant); 1.1388 + } 1.1389 + bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) { 1.1390 + if (!module_->addGlobalConstant(constant, fieldName)) 1.1391 + return false; 1.1392 + return addGlobalDoubleConstant(varName, constant); 1.1393 + } 1.1394 + bool addExportedFunction(const Func *func, PropertyName *maybeFieldName) { 1.1395 + AsmJSModule::ArgCoercionVector argCoercions; 1.1396 + const VarTypeVector &args = func->sig().args(); 1.1397 + if (!argCoercions.resize(args.length())) 1.1398 + return false; 1.1399 + for (unsigned i = 0; i < args.length(); i++) 1.1400 + argCoercions[i] = args[i].toCoercion(); 1.1401 + AsmJSModule::ReturnType retType = func->sig().retType().toModuleReturnType(); 1.1402 + return module_->addExportedFunction(func->name(), func->srcOffset(), func->endOffset(), 1.1403 + maybeFieldName, Move(argCoercions), retType); 1.1404 + } 1.1405 + bool addExit(unsigned ffiIndex, PropertyName *name, Signature &&sig, unsigned *exitIndex) { 1.1406 + ExitDescriptor exitDescriptor(name, Move(sig)); 1.1407 + ExitMap::AddPtr p = exits_.lookupForAdd(exitDescriptor); 1.1408 + if (p) { 1.1409 + *exitIndex = p->value(); 1.1410 + return true; 1.1411 + } 1.1412 + if (!module_->addExit(ffiIndex, exitIndex)) 1.1413 + return false; 1.1414 + return exits_.add(p, Move(exitDescriptor), *exitIndex); 1.1415 + } 1.1416 + bool addFunctionName(PropertyName *name, uint32_t *index) { 1.1417 + return module_->addFunctionName(name, index); 1.1418 + } 1.1419 + 1.1420 + // Note a constraint on the minimum size of the heap. The heap size is 1.1421 + // constrained when linking to be at least the maximum of all such constraints. 1.1422 + void requireHeapLengthToBeAtLeast(uint32_t len) { 1.1423 + module_->requireHeapLengthToBeAtLeast(len); 1.1424 + } 1.1425 + uint32_t minHeapLength() const { 1.1426 + return module_->minHeapLength(); 1.1427 + } 1.1428 + LifoAlloc &lifo() { 1.1429 + return moduleLifo_; 1.1430 + } 1.1431 + 1.1432 +#if defined(MOZ_VTUNE) || defined(JS_ION_PERF) 1.1433 + bool trackProfiledFunction(const Func &func, unsigned endCodeOffset) { 1.1434 + unsigned lineno = 0U, columnIndex = 0U; 1.1435 + tokenStream().srcCoords.lineNumAndColumnIndex(func.srcOffset(), &lineno, &columnIndex); 1.1436 + unsigned startCodeOffset = func.code()->offset(); 1.1437 + return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset, 1.1438 + lineno, columnIndex); 1.1439 + } 1.1440 +#endif 1.1441 + 1.1442 +#ifdef JS_ION_PERF 1.1443 + bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) { 1.1444 + unsigned startCodeOffset = func.code()->offset(); 1.1445 + perfSpewer.noteBlocksOffsets(); 1.1446 + unsigned endInlineCodeOffset = perfSpewer.endInlineCode.offset(); 1.1447 + return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endInlineCodeOffset, 1.1448 + endCodeOffset, perfSpewer.basicBlocks()); 1.1449 + } 1.1450 +#endif 1.1451 + 1.1452 + void finishFunctionBodies() { 1.1453 + JS_ASSERT(!finishedFunctionBodies_); 1.1454 + masm_.align(AsmJSPageSize); 1.1455 + finishedFunctionBodies_ = true; 1.1456 + module_->initFunctionBytes(masm_.currentOffset()); 1.1457 + } 1.1458 + 1.1459 + void setInterpExitOffset(unsigned exitIndex) { 1.1460 + module_->exit(exitIndex).initInterpOffset(masm_.currentOffset()); 1.1461 + } 1.1462 + void setIonExitOffset(unsigned exitIndex) { 1.1463 + module_->exit(exitIndex).initIonOffset(masm_.currentOffset()); 1.1464 + } 1.1465 + void setEntryOffset(unsigned exportIndex) { 1.1466 + module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset()); 1.1467 + } 1.1468 + 1.1469 + void buildCompilationTimeReport(bool storedInCache, ScopedJSFreePtr<char> *out) { 1.1470 + ScopedJSFreePtr<char> slowFuns; 1.1471 +#ifndef JS_MORE_DETERMINISTIC 1.1472 + int64_t usecAfter = PRMJ_Now(); 1.1473 + int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC; 1.1474 + if (!slowFunctions_.empty()) { 1.1475 + slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length())); 1.1476 + if (!slowFuns) 1.1477 + return; 1.1478 + for (unsigned i = 0; i < slowFunctions_.length(); i++) { 1.1479 + SlowFunction &func = slowFunctions_[i]; 1.1480 + JSAutoByteString name; 1.1481 + if (!AtomToPrintableString(cx_, func.name, &name)) 1.1482 + return; 1.1483 + slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(), 1.1484 + name.ptr(), func.line, func.column, func.ms, 1.1485 + i+1 < slowFunctions_.length() ? ", " : "")); 1.1486 + if (!slowFuns) 1.1487 + return; 1.1488 + } 1.1489 + } 1.1490 + out->reset(JS_smprintf("total compilation time %dms; %s%s", 1.1491 + msTotal, 1.1492 + storedInCache ? "stored in cache" : "not stored in cache", 1.1493 + slowFuns ? slowFuns.get() : "")); 1.1494 +#endif 1.1495 + } 1.1496 + 1.1497 + bool finish(ScopedJSDeletePtr<AsmJSModule> *module) 1.1498 + { 1.1499 + module_->initFuncEnd(tokenStream().currentToken().pos.end, 1.1500 + tokenStream().peekTokenPos().end); 1.1501 + masm_.finish(); 1.1502 + if (masm_.oom()) 1.1503 + return false; 1.1504 + 1.1505 + module_->assignCallSites(masm_.extractCallSites()); 1.1506 + module_->assignHeapAccesses(masm_.extractAsmJSHeapAccesses()); 1.1507 + 1.1508 +#if defined(JS_CODEGEN_ARM) 1.1509 + // Now that compilation has finished, we need to update offsets to 1.1510 + // reflect actual offsets (an ARM distinction). 1.1511 + for (unsigned i = 0; i < module_->numHeapAccesses(); i++) { 1.1512 + AsmJSHeapAccess &a = module_->heapAccess(i); 1.1513 + a.setOffset(masm_.actualOffset(a.offset())); 1.1514 + } 1.1515 + for (unsigned i = 0; i < module_->numExportedFunctions(); i++) 1.1516 + module_->exportedFunction(i).updateCodeOffset(masm_); 1.1517 + for (unsigned i = 0; i < module_->numExits(); i++) 1.1518 + module_->exit(i).updateOffsets(masm_); 1.1519 + for (unsigned i = 0; i < module_->numCallSites(); i++) { 1.1520 + CallSite &c = module_->callSite(i); 1.1521 + c.setReturnAddressOffset(masm_.actualOffset(c.returnAddressOffset())); 1.1522 + } 1.1523 +#endif 1.1524 + 1.1525 + // The returned memory is owned by module_. 1.1526 + if (!module_->allocateAndCopyCode(cx_, masm_)) 1.1527 + return false; 1.1528 + 1.1529 + module_->updateFunctionBytes(masm_); 1.1530 + // c.f. JitCode::copyFrom 1.1531 + JS_ASSERT(masm_.jumpRelocationTableBytes() == 0); 1.1532 + JS_ASSERT(masm_.dataRelocationTableBytes() == 0); 1.1533 + JS_ASSERT(masm_.preBarrierTableBytes() == 0); 1.1534 + JS_ASSERT(!masm_.hasEnteredExitFrame()); 1.1535 + 1.1536 +#if defined(MOZ_VTUNE) || defined(JS_ION_PERF) 1.1537 + // Fix up the code offsets. 1.1538 + for (unsigned i = 0; i < module_->numProfiledFunctions(); i++) { 1.1539 + AsmJSModule::ProfiledFunction &func = module_->profiledFunction(i); 1.1540 + func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset); 1.1541 + func.pod.endCodeOffset = masm_.actualOffset(func.pod.endCodeOffset); 1.1542 + } 1.1543 +#endif 1.1544 + 1.1545 +#ifdef JS_ION_PERF 1.1546 + for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) { 1.1547 + AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i); 1.1548 + func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset); 1.1549 + func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset); 1.1550 + func.pod.endCodeOffset = masm_.actualOffset(func.pod.endCodeOffset); 1.1551 + BasicBlocksVector &basicBlocks = func.blocks; 1.1552 + for (uint32_t i = 0; i < basicBlocks.length(); i++) { 1.1553 + Record &r = basicBlocks[i]; 1.1554 + r.startOffset = masm_.actualOffset(r.startOffset); 1.1555 + r.endOffset = masm_.actualOffset(r.endOffset); 1.1556 + } 1.1557 + } 1.1558 +#endif 1.1559 + 1.1560 + module_->setInterruptOffset(masm_.actualOffset(interruptLabel_.offset())); 1.1561 + 1.1562 + // CodeLabels produced during codegen 1.1563 + for (size_t i = 0; i < masm_.numCodeLabels(); i++) { 1.1564 + CodeLabel src = masm_.codeLabel(i); 1.1565 + int32_t labelOffset = src.dest()->offset(); 1.1566 + int32_t targetOffset = masm_.actualOffset(src.src()->offset()); 1.1567 + // The patched uses of a label embed a linked list where the 1.1568 + // to-be-patched immediate is the offset of the next to-be-patched 1.1569 + // instruction. 1.1570 + while (labelOffset != LabelBase::INVALID_OFFSET) { 1.1571 + size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset); 1.1572 + AsmJSModule::RelativeLink link; 1.1573 + link.patchAtOffset = patchAtOffset; 1.1574 + link.targetOffset = targetOffset; 1.1575 + if (!module_->addRelativeLink(link)) 1.1576 + return false; 1.1577 + labelOffset = *(uintptr_t *)(module_->codeBase() + patchAtOffset); 1.1578 + } 1.1579 + } 1.1580 + 1.1581 + // Function-pointer-table entries 1.1582 + for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) { 1.1583 + FuncPtrTable &table = funcPtrTables_[tableIndex]; 1.1584 + unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); 1.1585 + for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) { 1.1586 + AsmJSModule::RelativeLink link; 1.1587 + link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*); 1.1588 + link.targetOffset = masm_.actualOffset(table.elem(elemIndex).code()->offset()); 1.1589 + if (!module_->addRelativeLink(link)) 1.1590 + return false; 1.1591 + } 1.1592 + } 1.1593 + 1.1594 +#if defined(JS_CODEGEN_X86) 1.1595 + // Global data accesses in x86 need to be patched with the absolute 1.1596 + // address of the global. Globals are allocated sequentially after the 1.1597 + // code section so we can just use an RelativeLink. 1.1598 + for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { 1.1599 + AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); 1.1600 + AsmJSModule::RelativeLink link; 1.1601 + link.patchAtOffset = masm_.labelOffsetToPatchOffset(a.patchAt.offset()); 1.1602 + link.targetOffset = module_->offsetOfGlobalData() + a.globalDataOffset; 1.1603 + if (!module_->addRelativeLink(link)) 1.1604 + return false; 1.1605 + } 1.1606 +#endif 1.1607 + 1.1608 +#if defined(JS_CODEGEN_X64) 1.1609 + // Global data accesses on x64 use rip-relative addressing and thus do 1.1610 + // not need patching after deserialization. 1.1611 + uint8_t *code = module_->codeBase(); 1.1612 + for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { 1.1613 + AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); 1.1614 + masm_.patchAsmJSGlobalAccess(a.patchAt, code, module_->globalData(), a.globalDataOffset); 1.1615 + } 1.1616 +#endif 1.1617 + 1.1618 + // Absolute links 1.1619 + for (size_t i = 0; i < masm_.numAsmJSAbsoluteLinks(); i++) { 1.1620 + AsmJSAbsoluteLink src = masm_.asmJSAbsoluteLink(i); 1.1621 + AsmJSModule::AbsoluteLink link; 1.1622 + link.patchAt = masm_.actualOffset(src.patchAt.offset()); 1.1623 + link.target = src.target; 1.1624 + if (!module_->addAbsoluteLink(link)) 1.1625 + return false; 1.1626 + } 1.1627 + 1.1628 + *module = module_.forget(); 1.1629 + return true; 1.1630 + } 1.1631 +}; 1.1632 + 1.1633 +} /* anonymous namespace */ 1.1634 + 1.1635 +/*****************************************************************************/ 1.1636 +// Numeric literal utilities 1.1637 + 1.1638 +namespace { 1.1639 + 1.1640 +// Represents the type and value of an asm.js numeric literal. 1.1641 +// 1.1642 +// A literal is a double iff the literal contains an exponent or decimal point 1.1643 +// (even if the fractional part is 0). Otherwise, integers may be classified: 1.1644 +// fixnum: [0, 2^31) 1.1645 +// negative int: [-2^31, 0) 1.1646 +// big unsigned: [2^31, 2^32) 1.1647 +// out of range: otherwise 1.1648 +// Lastly, a literal may be a float literal which is any double or integer 1.1649 +// literal coerced with Math.fround. 1.1650 +class NumLit 1.1651 +{ 1.1652 + public: 1.1653 + enum Which { 1.1654 + Fixnum = Type::Fixnum, 1.1655 + NegativeInt = Type::Signed, 1.1656 + BigUnsigned = Type::Unsigned, 1.1657 + Double = Type::Double, 1.1658 + Float = Type::Float, 1.1659 + OutOfRangeInt = -1 1.1660 + }; 1.1661 + 1.1662 + private: 1.1663 + Which which_; 1.1664 + Value v_; 1.1665 + 1.1666 + public: 1.1667 + NumLit() {} 1.1668 + 1.1669 + NumLit(Which w, Value v) 1.1670 + : which_(w), v_(v) 1.1671 + {} 1.1672 + 1.1673 + Which which() const { 1.1674 + return which_; 1.1675 + } 1.1676 + 1.1677 + int32_t toInt32() const { 1.1678 + JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned); 1.1679 + return v_.toInt32(); 1.1680 + } 1.1681 + 1.1682 + double toDouble() const { 1.1683 + JS_ASSERT(which_ == Double); 1.1684 + return v_.toDouble(); 1.1685 + } 1.1686 + 1.1687 + float toFloat() const { 1.1688 + JS_ASSERT(which_ == Float); 1.1689 + return float(v_.toDouble()); 1.1690 + } 1.1691 + 1.1692 + Value value() const { 1.1693 + JS_ASSERT(which_ != OutOfRangeInt); 1.1694 + return v_; 1.1695 + } 1.1696 + 1.1697 + bool hasType() const { 1.1698 + return which_ != OutOfRangeInt; 1.1699 + } 1.1700 + 1.1701 + Type type() const { 1.1702 + JS_ASSERT(hasType()); 1.1703 + return Type::Which(which_); 1.1704 + } 1.1705 + 1.1706 + VarType varType() const { 1.1707 + JS_ASSERT(hasType()); 1.1708 + switch (which_) { 1.1709 + case NumLit::Fixnum: 1.1710 + case NumLit::NegativeInt: 1.1711 + case NumLit::BigUnsigned: 1.1712 + return VarType::Int; 1.1713 + case NumLit::Double: 1.1714 + return VarType::Double; 1.1715 + case NumLit::Float: 1.1716 + return VarType::Float; 1.1717 + case NumLit::OutOfRangeInt:; 1.1718 + } 1.1719 + MOZ_ASSUME_UNREACHABLE("Unexpected NumLit type"); 1.1720 + } 1.1721 +}; 1.1722 + 1.1723 +} /* anonymous namespace */ 1.1724 + 1.1725 +static bool 1.1726 +IsNumericNonFloatLiteral(ParseNode *pn) 1.1727 +{ 1.1728 + // Note: '-' is never rolled into the number; numbers are always positive 1.1729 + // and negations must be applied manually. 1.1730 + return pn->isKind(PNK_NUMBER) || 1.1731 + (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER)); 1.1732 +} 1.1733 + 1.1734 +static bool 1.1735 +IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr) 1.1736 +{ 1.1737 + if (!pn->isKind(PNK_CALL)) 1.1738 + return false; 1.1739 + 1.1740 + ParseNode *callee = CallCallee(pn); 1.1741 + if (!callee->isKind(PNK_NAME)) 1.1742 + return false; 1.1743 + 1.1744 + const ModuleCompiler::Global *global = m.lookupGlobal(callee->name()); 1.1745 + if (!global || 1.1746 + global->which() != ModuleCompiler::Global::MathBuiltinFunction || 1.1747 + global->mathBuiltinFunction() != AsmJSMathBuiltin_fround) 1.1748 + { 1.1749 + return false; 1.1750 + } 1.1751 + 1.1752 + if (CallArgListLength(pn) != 1) 1.1753 + return false; 1.1754 + 1.1755 + if (coercedExpr) 1.1756 + *coercedExpr = CallArgList(pn); 1.1757 + 1.1758 + return true; 1.1759 +} 1.1760 + 1.1761 +static bool 1.1762 +IsNumericFloatLiteral(ModuleCompiler &m, ParseNode *pn) 1.1763 +{ 1.1764 + ParseNode *coercedExpr; 1.1765 + if (!IsFloatCoercion(m, pn, &coercedExpr)) 1.1766 + return false; 1.1767 + 1.1768 + return IsNumericNonFloatLiteral(coercedExpr); 1.1769 +} 1.1770 + 1.1771 +static bool 1.1772 +IsNumericLiteral(ModuleCompiler &m, ParseNode *pn) 1.1773 +{ 1.1774 + return IsNumericNonFloatLiteral(pn) || 1.1775 + IsNumericFloatLiteral(m, pn); 1.1776 +} 1.1777 + 1.1778 +// The JS grammar treats -42 as -(42) (i.e., with separate grammar 1.1779 +// productions) for the unary - and literal 42). However, the asm.js spec 1.1780 +// recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal 1.1781 +// so fold the two potential parse nodes into a single double value. 1.1782 +static double 1.1783 +ExtractNumericNonFloatValue(ParseNode **pn) 1.1784 +{ 1.1785 + JS_ASSERT(IsNumericNonFloatLiteral(*pn)); 1.1786 + 1.1787 + if ((*pn)->isKind(PNK_NEG)) { 1.1788 + *pn = UnaryKid(*pn); 1.1789 + return -NumberNodeValue(*pn); 1.1790 + } 1.1791 + 1.1792 + return NumberNodeValue(*pn); 1.1793 +} 1.1794 + 1.1795 +static NumLit 1.1796 +ExtractNumericLiteral(ModuleCompiler &m, ParseNode *pn) 1.1797 +{ 1.1798 + JS_ASSERT(IsNumericLiteral(m, pn)); 1.1799 + 1.1800 + // Float literals are explicitly coerced and thus the coerced literal may be 1.1801 + // any valid (non-float) numeric literal. 1.1802 + if (pn->isKind(PNK_CALL)) { 1.1803 + pn = CallArgList(pn); 1.1804 + double d = ExtractNumericNonFloatValue(&pn); 1.1805 + return NumLit(NumLit::Float, DoubleValue(d)); 1.1806 + } 1.1807 + 1.1808 + double d = ExtractNumericNonFloatValue(&pn); 1.1809 + 1.1810 + // The asm.js spec syntactically distinguishes any literal containing a 1.1811 + // decimal point or the literal -0 as having double type. 1.1812 + if (NumberNodeHasFrac(pn) || IsNegativeZero(d)) 1.1813 + return NumLit(NumLit::Double, DoubleValue(d)); 1.1814 + 1.1815 + // The syntactic checks above rule out these double values. 1.1816 + JS_ASSERT(!IsNegativeZero(d)); 1.1817 + JS_ASSERT(!IsNaN(d)); 1.1818 + 1.1819 + // Although doubles can only *precisely* represent 53-bit integers, they 1.1820 + // can *imprecisely* represent integers much bigger than an int64_t. 1.1821 + // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t 1.1822 + // is undefined, so test against the integer bounds using doubles. 1.1823 + if (d < double(INT32_MIN) || d > double(UINT32_MAX)) 1.1824 + return NumLit(NumLit::OutOfRangeInt, UndefinedValue()); 1.1825 + 1.1826 + // With the above syntactic and range limitations, d is definitely an 1.1827 + // integer in the range [INT32_MIN, UINT32_MAX] range. 1.1828 + int64_t i64 = int64_t(d); 1.1829 + if (i64 >= 0) { 1.1830 + if (i64 <= INT32_MAX) 1.1831 + return NumLit(NumLit::Fixnum, Int32Value(i64)); 1.1832 + JS_ASSERT(i64 <= UINT32_MAX); 1.1833 + return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64))); 1.1834 + } 1.1835 + JS_ASSERT(i64 >= INT32_MIN); 1.1836 + return NumLit(NumLit::NegativeInt, Int32Value(i64)); 1.1837 +} 1.1838 + 1.1839 +static inline bool 1.1840 +IsLiteralInt(ModuleCompiler &m, ParseNode *pn, uint32_t *u32) 1.1841 +{ 1.1842 + if (!IsNumericLiteral(m, pn)) 1.1843 + return false; 1.1844 + 1.1845 + NumLit literal = ExtractNumericLiteral(m, pn); 1.1846 + switch (literal.which()) { 1.1847 + case NumLit::Fixnum: 1.1848 + case NumLit::BigUnsigned: 1.1849 + case NumLit::NegativeInt: 1.1850 + *u32 = uint32_t(literal.toInt32()); 1.1851 + return true; 1.1852 + case NumLit::Double: 1.1853 + case NumLit::Float: 1.1854 + case NumLit::OutOfRangeInt: 1.1855 + return false; 1.1856 + } 1.1857 + 1.1858 + MOZ_ASSUME_UNREACHABLE("Bad literal type"); 1.1859 +} 1.1860 + 1.1861 +/*****************************************************************************/ 1.1862 + 1.1863 +namespace { 1.1864 + 1.1865 +// Encapsulates the compilation of a single function in an asm.js module. The 1.1866 +// function compiler handles the creation and final backend compilation of the 1.1867 +// MIR graph. Also see ModuleCompiler comment. 1.1868 +class FunctionCompiler 1.1869 +{ 1.1870 + public: 1.1871 + struct Local 1.1872 + { 1.1873 + VarType type; 1.1874 + unsigned slot; 1.1875 + Local(VarType t, unsigned slot) : type(t), slot(slot) {} 1.1876 + }; 1.1877 + 1.1878 + struct TypedValue 1.1879 + { 1.1880 + VarType type; 1.1881 + Value value; 1.1882 + TypedValue(VarType t, const Value &v) : type(t), value(v) {} 1.1883 + }; 1.1884 + 1.1885 + private: 1.1886 + typedef HashMap<PropertyName*, Local> LocalMap; 1.1887 + typedef js::Vector<TypedValue> VarInitializerVector; 1.1888 + typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap; 1.1889 + typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap; 1.1890 + typedef js::Vector<ParseNode*, 4> NodeStack; 1.1891 + 1.1892 + ModuleCompiler & m_; 1.1893 + LifoAlloc & lifo_; 1.1894 + ParseNode * fn_; 1.1895 + uint32_t functionNameIndex_; 1.1896 + 1.1897 + LocalMap locals_; 1.1898 + VarInitializerVector varInitializers_; 1.1899 + Maybe<RetType> alreadyReturned_; 1.1900 + 1.1901 + TempAllocator * alloc_; 1.1902 + MIRGraph * graph_; 1.1903 + CompileInfo * info_; 1.1904 + MIRGenerator * mirGen_; 1.1905 + Maybe<IonContext> ionContext_; 1.1906 + 1.1907 + MBasicBlock * curBlock_; 1.1908 + 1.1909 + NodeStack loopStack_; 1.1910 + NodeStack breakableStack_; 1.1911 + UnlabeledBlockMap unlabeledBreaks_; 1.1912 + UnlabeledBlockMap unlabeledContinues_; 1.1913 + LabeledBlockMap labeledBreaks_; 1.1914 + LabeledBlockMap labeledContinues_; 1.1915 + 1.1916 + static const uint32_t NO_FUNCTION_NAME_INDEX = UINT32_MAX; 1.1917 + JS_STATIC_ASSERT(NO_FUNCTION_NAME_INDEX > CallSiteDesc::FUNCTION_NAME_INDEX_MAX); 1.1918 + 1.1919 + public: 1.1920 + FunctionCompiler(ModuleCompiler &m, ParseNode *fn, LifoAlloc &lifo) 1.1921 + : m_(m), 1.1922 + lifo_(lifo), 1.1923 + fn_(fn), 1.1924 + functionNameIndex_(NO_FUNCTION_NAME_INDEX), 1.1925 + locals_(m.cx()), 1.1926 + varInitializers_(m.cx()), 1.1927 + alloc_(nullptr), 1.1928 + graph_(nullptr), 1.1929 + info_(nullptr), 1.1930 + mirGen_(nullptr), 1.1931 + curBlock_(nullptr), 1.1932 + loopStack_(m.cx()), 1.1933 + breakableStack_(m.cx()), 1.1934 + unlabeledBreaks_(m.cx()), 1.1935 + unlabeledContinues_(m.cx()), 1.1936 + labeledBreaks_(m.cx()), 1.1937 + labeledContinues_(m.cx()) 1.1938 + {} 1.1939 + 1.1940 + ModuleCompiler & m() const { return m_; } 1.1941 + TempAllocator & alloc() const { return *alloc_; } 1.1942 + LifoAlloc & lifo() const { return lifo_; } 1.1943 + ParseNode * fn() const { return fn_; } 1.1944 + ExclusiveContext * cx() const { return m_.cx(); } 1.1945 + const AsmJSModule & module() const { return m_.module(); } 1.1946 + 1.1947 + bool init() 1.1948 + { 1.1949 + return locals_.init() && 1.1950 + unlabeledBreaks_.init() && 1.1951 + unlabeledContinues_.init() && 1.1952 + labeledBreaks_.init() && 1.1953 + labeledContinues_.init(); 1.1954 + } 1.1955 + 1.1956 + bool fail(ParseNode *pn, const char *str) 1.1957 + { 1.1958 + return m_.fail(pn, str); 1.1959 + } 1.1960 + 1.1961 + bool failf(ParseNode *pn, const char *fmt, ...) 1.1962 + { 1.1963 + va_list ap; 1.1964 + va_start(ap, fmt); 1.1965 + m_.failfVA(pn, fmt, ap); 1.1966 + va_end(ap); 1.1967 + return false; 1.1968 + } 1.1969 + 1.1970 + bool failName(ParseNode *pn, const char *fmt, PropertyName *name) 1.1971 + { 1.1972 + return m_.failName(pn, fmt, name); 1.1973 + } 1.1974 + 1.1975 + ~FunctionCompiler() 1.1976 + { 1.1977 +#ifdef DEBUG 1.1978 + if (!m().hasError() && cx()->isJSContext() && !cx()->asJSContext()->isExceptionPending()) { 1.1979 + JS_ASSERT(loopStack_.empty()); 1.1980 + JS_ASSERT(unlabeledBreaks_.empty()); 1.1981 + JS_ASSERT(unlabeledContinues_.empty()); 1.1982 + JS_ASSERT(labeledBreaks_.empty()); 1.1983 + JS_ASSERT(labeledContinues_.empty()); 1.1984 + JS_ASSERT(inDeadCode()); 1.1985 + } 1.1986 +#endif 1.1987 + } 1.1988 + 1.1989 + /***************************************************** Local scope setup */ 1.1990 + 1.1991 + bool addFormal(ParseNode *pn, PropertyName *name, VarType type) 1.1992 + { 1.1993 + LocalMap::AddPtr p = locals_.lookupForAdd(name); 1.1994 + if (p) 1.1995 + return failName(pn, "duplicate local name '%s' not allowed", name); 1.1996 + return locals_.add(p, name, Local(type, locals_.count())); 1.1997 + } 1.1998 + 1.1999 + bool addVariable(ParseNode *pn, PropertyName *name, VarType type, const Value &init) 1.2000 + { 1.2001 + LocalMap::AddPtr p = locals_.lookupForAdd(name); 1.2002 + if (p) 1.2003 + return failName(pn, "duplicate local name '%s' not allowed", name); 1.2004 + if (!locals_.add(p, name, Local(type, locals_.count()))) 1.2005 + return false; 1.2006 + return varInitializers_.append(TypedValue(type, init)); 1.2007 + } 1.2008 + 1.2009 + bool prepareToEmitMIR(const VarTypeVector &argTypes) 1.2010 + { 1.2011 + JS_ASSERT(locals_.count() == argTypes.length() + varInitializers_.length()); 1.2012 + 1.2013 + alloc_ = lifo_.new_<TempAllocator>(&lifo_); 1.2014 + ionContext_.construct(m_.cx(), alloc_); 1.2015 + 1.2016 + graph_ = lifo_.new_<MIRGraph>(alloc_); 1.2017 + info_ = lifo_.new_<CompileInfo>(locals_.count(), SequentialExecution); 1.2018 + const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS); 1.2019 + const JitCompileOptions options; 1.2020 + mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), 1.2021 + options, alloc_, 1.2022 + graph_, info_, optimizationInfo); 1.2023 + 1.2024 + if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_)) 1.2025 + return false; 1.2026 + 1.2027 + for (ABIArgTypeIter i = argTypes; !i.done(); i++) { 1.2028 + MAsmJSParameter *ins = MAsmJSParameter::New(alloc(), *i, i.mirType()); 1.2029 + curBlock_->add(ins); 1.2030 + curBlock_->initSlot(info().localSlot(i.index()), ins); 1.2031 + if (!mirGen_->ensureBallast()) 1.2032 + return false; 1.2033 + } 1.2034 + unsigned firstLocalSlot = argTypes.length(); 1.2035 + for (unsigned i = 0; i < varInitializers_.length(); i++) { 1.2036 + MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value, 1.2037 + varInitializers_[i].type.toMIRType()); 1.2038 + curBlock_->add(ins); 1.2039 + curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins); 1.2040 + if (!mirGen_->ensureBallast()) 1.2041 + return false; 1.2042 + } 1.2043 + return true; 1.2044 + } 1.2045 + 1.2046 + /******************************* For consistency of returns in a function */ 1.2047 + 1.2048 + bool hasAlreadyReturned() const { 1.2049 + return !alreadyReturned_.empty(); 1.2050 + } 1.2051 + 1.2052 + RetType returnedType() const { 1.2053 + return alreadyReturned_.ref(); 1.2054 + } 1.2055 + 1.2056 + void setReturnedType(RetType retType) { 1.2057 + alreadyReturned_.construct(retType); 1.2058 + } 1.2059 + 1.2060 + /************************* Read-only interface (after local scope setup) */ 1.2061 + 1.2062 + MIRGenerator & mirGen() const { JS_ASSERT(mirGen_); return *mirGen_; } 1.2063 + MIRGraph & mirGraph() const { JS_ASSERT(graph_); return *graph_; } 1.2064 + CompileInfo & info() const { JS_ASSERT(info_); return *info_; } 1.2065 + 1.2066 + const Local *lookupLocal(PropertyName *name) const 1.2067 + { 1.2068 + if (LocalMap::Ptr p = locals_.lookup(name)) 1.2069 + return &p->value(); 1.2070 + return nullptr; 1.2071 + } 1.2072 + 1.2073 + MDefinition *getLocalDef(const Local &local) 1.2074 + { 1.2075 + if (inDeadCode()) 1.2076 + return nullptr; 1.2077 + return curBlock_->getSlot(info().localSlot(local.slot)); 1.2078 + } 1.2079 + 1.2080 + const ModuleCompiler::Global *lookupGlobal(PropertyName *name) const 1.2081 + { 1.2082 + if (locals_.has(name)) 1.2083 + return nullptr; 1.2084 + return m_.lookupGlobal(name); 1.2085 + } 1.2086 + 1.2087 + /***************************** Code generation (after local scope setup) */ 1.2088 + 1.2089 + MDefinition *constant(Value v, Type t) 1.2090 + { 1.2091 + if (inDeadCode()) 1.2092 + return nullptr; 1.2093 + MConstant *constant = MConstant::NewAsmJS(alloc(), v, t.toMIRType()); 1.2094 + curBlock_->add(constant); 1.2095 + return constant; 1.2096 + } 1.2097 + 1.2098 + template <class T> 1.2099 + MDefinition *unary(MDefinition *op) 1.2100 + { 1.2101 + if (inDeadCode()) 1.2102 + return nullptr; 1.2103 + T *ins = T::NewAsmJS(alloc(), op); 1.2104 + curBlock_->add(ins); 1.2105 + return ins; 1.2106 + } 1.2107 + 1.2108 + template <class T> 1.2109 + MDefinition *unary(MDefinition *op, MIRType type) 1.2110 + { 1.2111 + if (inDeadCode()) 1.2112 + return nullptr; 1.2113 + T *ins = T::NewAsmJS(alloc(), op, type); 1.2114 + curBlock_->add(ins); 1.2115 + return ins; 1.2116 + } 1.2117 + 1.2118 + template <class T> 1.2119 + MDefinition *binary(MDefinition *lhs, MDefinition *rhs) 1.2120 + { 1.2121 + if (inDeadCode()) 1.2122 + return nullptr; 1.2123 + T *ins = T::New(alloc(), lhs, rhs); 1.2124 + curBlock_->add(ins); 1.2125 + return ins; 1.2126 + } 1.2127 + 1.2128 + template <class T> 1.2129 + MDefinition *binary(MDefinition *lhs, MDefinition *rhs, MIRType type) 1.2130 + { 1.2131 + if (inDeadCode()) 1.2132 + return nullptr; 1.2133 + T *ins = T::NewAsmJS(alloc(), lhs, rhs, type); 1.2134 + curBlock_->add(ins); 1.2135 + return ins; 1.2136 + } 1.2137 + 1.2138 + MDefinition *minMax(MDefinition *lhs, MDefinition *rhs, MIRType type, bool isMax) { 1.2139 + if (inDeadCode()) 1.2140 + return nullptr; 1.2141 + MMinMax *ins = MMinMax::New(alloc(), lhs, rhs, type, isMax); 1.2142 + curBlock_->add(ins); 1.2143 + return ins; 1.2144 + } 1.2145 + 1.2146 + MDefinition *mul(MDefinition *lhs, MDefinition *rhs, MIRType type, MMul::Mode mode) 1.2147 + { 1.2148 + if (inDeadCode()) 1.2149 + return nullptr; 1.2150 + MMul *ins = MMul::New(alloc(), lhs, rhs, type, mode); 1.2151 + curBlock_->add(ins); 1.2152 + return ins; 1.2153 + } 1.2154 + 1.2155 + MDefinition *div(MDefinition *lhs, MDefinition *rhs, MIRType type, bool unsignd) 1.2156 + { 1.2157 + if (inDeadCode()) 1.2158 + return nullptr; 1.2159 + MDiv *ins = MDiv::NewAsmJS(alloc(), lhs, rhs, type, unsignd); 1.2160 + curBlock_->add(ins); 1.2161 + return ins; 1.2162 + } 1.2163 + 1.2164 + MDefinition *mod(MDefinition *lhs, MDefinition *rhs, MIRType type, bool unsignd) 1.2165 + { 1.2166 + if (inDeadCode()) 1.2167 + return nullptr; 1.2168 + MMod *ins = MMod::NewAsmJS(alloc(), lhs, rhs, type, unsignd); 1.2169 + curBlock_->add(ins); 1.2170 + return ins; 1.2171 + } 1.2172 + 1.2173 + template <class T> 1.2174 + MDefinition *bitwise(MDefinition *lhs, MDefinition *rhs) 1.2175 + { 1.2176 + if (inDeadCode()) 1.2177 + return nullptr; 1.2178 + T *ins = T::NewAsmJS(alloc(), lhs, rhs); 1.2179 + curBlock_->add(ins); 1.2180 + return ins; 1.2181 + } 1.2182 + 1.2183 + template <class T> 1.2184 + MDefinition *bitwise(MDefinition *op) 1.2185 + { 1.2186 + if (inDeadCode()) 1.2187 + return nullptr; 1.2188 + T *ins = T::NewAsmJS(alloc(), op); 1.2189 + curBlock_->add(ins); 1.2190 + return ins; 1.2191 + } 1.2192 + 1.2193 + MDefinition *compare(MDefinition *lhs, MDefinition *rhs, JSOp op, MCompare::CompareType type) 1.2194 + { 1.2195 + if (inDeadCode()) 1.2196 + return nullptr; 1.2197 + MCompare *ins = MCompare::NewAsmJS(alloc(), lhs, rhs, op, type); 1.2198 + curBlock_->add(ins); 1.2199 + return ins; 1.2200 + } 1.2201 + 1.2202 + void assign(const Local &local, MDefinition *def) 1.2203 + { 1.2204 + if (inDeadCode()) 1.2205 + return; 1.2206 + curBlock_->setSlot(info().localSlot(local.slot), def); 1.2207 + } 1.2208 + 1.2209 + MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk) 1.2210 + { 1.2211 + if (inDeadCode()) 1.2212 + return nullptr; 1.2213 + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr); 1.2214 + curBlock_->add(load); 1.2215 + if (chk == NO_BOUNDS_CHECK) 1.2216 + load->setSkipBoundsCheck(true); 1.2217 + return load; 1.2218 + } 1.2219 + 1.2220 + void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) 1.2221 + { 1.2222 + if (inDeadCode()) 1.2223 + return; 1.2224 + MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v); 1.2225 + curBlock_->add(store); 1.2226 + if (chk == NO_BOUNDS_CHECK) 1.2227 + store->setSkipBoundsCheck(true); 1.2228 + } 1.2229 + 1.2230 + MDefinition *loadGlobalVar(const ModuleCompiler::Global &global) 1.2231 + { 1.2232 + if (inDeadCode()) 1.2233 + return nullptr; 1.2234 + 1.2235 + uint32_t index = global.varOrConstIndex(); 1.2236 + unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(index); 1.2237 + MIRType type = global.varOrConstType().toMIRType(); 1.2238 + MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(alloc(), type, globalDataOffset, 1.2239 + global.isConst()); 1.2240 + curBlock_->add(load); 1.2241 + return load; 1.2242 + } 1.2243 + 1.2244 + void storeGlobalVar(const ModuleCompiler::Global &global, MDefinition *v) 1.2245 + { 1.2246 + if (inDeadCode()) 1.2247 + return; 1.2248 + JS_ASSERT(!global.isConst()); 1.2249 + unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varOrConstIndex()); 1.2250 + curBlock_->add(MAsmJSStoreGlobalVar::New(alloc(), globalDataOffset, v)); 1.2251 + } 1.2252 + 1.2253 + /***************************************************************** Calls */ 1.2254 + 1.2255 + // The IonMonkey backend maintains a single stack offset (from the stack 1.2256 + // pointer to the base of the frame) by adding the total amount of spill 1.2257 + // space required plus the maximum stack required for argument passing. 1.2258 + // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must 1.2259 + // manually accumulate, for the entire function, the maximum required stack 1.2260 + // space for argument passing. (This is passed to the CodeGenerator via 1.2261 + // MIRGenerator::maxAsmJSStackArgBytes.) Naively, this would just be the 1.2262 + // maximum of the stack space required for each individual call (as 1.2263 + // determined by the call ABI). However, as an optimization, arguments are 1.2264 + // stored to the stack immediately after evaluation (to decrease live 1.2265 + // ranges and reduce spilling). This introduces the complexity that, 1.2266 + // between evaluating an argument and making the call, another argument 1.2267 + // evaluation could perform a call that also needs to store to the stack. 1.2268 + // When this occurs childClobbers_ = true and the parent expression's 1.2269 + // arguments are stored above the maximum depth clobbered by a child 1.2270 + // expression. 1.2271 + 1.2272 + class Call 1.2273 + { 1.2274 + ParseNode *node_; 1.2275 + ABIArgGenerator abi_; 1.2276 + uint32_t prevMaxStackBytes_; 1.2277 + uint32_t maxChildStackBytes_; 1.2278 + uint32_t spIncrement_; 1.2279 + Signature sig_; 1.2280 + MAsmJSCall::Args regArgs_; 1.2281 + js::Vector<MAsmJSPassStackArg*> stackArgs_; 1.2282 + bool childClobbers_; 1.2283 + 1.2284 + friend class FunctionCompiler; 1.2285 + 1.2286 + public: 1.2287 + Call(FunctionCompiler &f, ParseNode *callNode, RetType retType) 1.2288 + : node_(callNode), 1.2289 + prevMaxStackBytes_(0), 1.2290 + maxChildStackBytes_(0), 1.2291 + spIncrement_(0), 1.2292 + sig_(f.m().lifo(), retType), 1.2293 + regArgs_(f.cx()), 1.2294 + stackArgs_(f.cx()), 1.2295 + childClobbers_(false) 1.2296 + { } 1.2297 + Signature &sig() { return sig_; } 1.2298 + const Signature &sig() const { return sig_; } 1.2299 + }; 1.2300 + 1.2301 + void startCallArgs(Call *call) 1.2302 + { 1.2303 + if (inDeadCode()) 1.2304 + return; 1.2305 + call->prevMaxStackBytes_ = mirGen().resetAsmJSMaxStackArgBytes(); 1.2306 + } 1.2307 + 1.2308 + bool passArg(MDefinition *argDef, VarType type, Call *call) 1.2309 + { 1.2310 + if (!call->sig().appendArg(type)) 1.2311 + return false; 1.2312 + 1.2313 + if (inDeadCode()) 1.2314 + return true; 1.2315 + 1.2316 + uint32_t childStackBytes = mirGen().resetAsmJSMaxStackArgBytes(); 1.2317 + call->maxChildStackBytes_ = Max(call->maxChildStackBytes_, childStackBytes); 1.2318 + if (childStackBytes > 0 && !call->stackArgs_.empty()) 1.2319 + call->childClobbers_ = true; 1.2320 + 1.2321 + ABIArg arg = call->abi_.next(type.toMIRType()); 1.2322 + if (arg.kind() == ABIArg::Stack) { 1.2323 + MAsmJSPassStackArg *mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), 1.2324 + argDef); 1.2325 + curBlock_->add(mir); 1.2326 + if (!call->stackArgs_.append(mir)) 1.2327 + return false; 1.2328 + } else { 1.2329 + if (!call->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef))) 1.2330 + return false; 1.2331 + } 1.2332 + return true; 1.2333 + } 1.2334 + 1.2335 + void finishCallArgs(Call *call) 1.2336 + { 1.2337 + if (inDeadCode()) 1.2338 + return; 1.2339 + uint32_t parentStackBytes = call->abi_.stackBytesConsumedSoFar(); 1.2340 + uint32_t newStackBytes; 1.2341 + if (call->childClobbers_) { 1.2342 + call->spIncrement_ = AlignBytes(call->maxChildStackBytes_, StackAlignment); 1.2343 + for (unsigned i = 0; i < call->stackArgs_.length(); i++) 1.2344 + call->stackArgs_[i]->incrementOffset(call->spIncrement_); 1.2345 + newStackBytes = Max(call->prevMaxStackBytes_, 1.2346 + call->spIncrement_ + parentStackBytes); 1.2347 + } else { 1.2348 + call->spIncrement_ = 0; 1.2349 + newStackBytes = Max(call->prevMaxStackBytes_, 1.2350 + Max(call->maxChildStackBytes_, parentStackBytes)); 1.2351 + } 1.2352 + mirGen_->setAsmJSMaxStackArgBytes(newStackBytes); 1.2353 + } 1.2354 + 1.2355 + private: 1.2356 + bool callPrivate(MAsmJSCall::Callee callee, const Call &call, MIRType returnType, MDefinition **def) 1.2357 + { 1.2358 + if (inDeadCode()) { 1.2359 + *def = nullptr; 1.2360 + return true; 1.2361 + } 1.2362 + 1.2363 + uint32_t line, column; 1.2364 + m_.tokenStream().srcCoords.lineNumAndColumnIndex(call.node_->pn_pos.begin, &line, &column); 1.2365 + 1.2366 + if (functionNameIndex_ == NO_FUNCTION_NAME_INDEX) { 1.2367 + if (!m_.addFunctionName(FunctionName(fn_), &functionNameIndex_)) 1.2368 + return false; 1.2369 + } 1.2370 + 1.2371 + CallSiteDesc desc(line, column, functionNameIndex_); 1.2372 + MAsmJSCall *ins = MAsmJSCall::New(alloc(), desc, callee, call.regArgs_, returnType, 1.2373 + call.spIncrement_); 1.2374 + if (!ins) 1.2375 + return false; 1.2376 + 1.2377 + curBlock_->add(ins); 1.2378 + *def = ins; 1.2379 + return true; 1.2380 + } 1.2381 + 1.2382 + public: 1.2383 + bool internalCall(const ModuleCompiler::Func &func, const Call &call, MDefinition **def) 1.2384 + { 1.2385 + MIRType returnType = func.sig().retType().toMIRType(); 1.2386 + return callPrivate(MAsmJSCall::Callee(func.code()), call, returnType, def); 1.2387 + } 1.2388 + 1.2389 + bool funcPtrCall(const ModuleCompiler::FuncPtrTable &table, MDefinition *index, 1.2390 + const Call &call, MDefinition **def) 1.2391 + { 1.2392 + if (inDeadCode()) { 1.2393 + *def = nullptr; 1.2394 + return true; 1.2395 + } 1.2396 + 1.2397 + MConstant *mask = MConstant::New(alloc(), Int32Value(table.mask())); 1.2398 + curBlock_->add(mask); 1.2399 + MBitAnd *maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask); 1.2400 + curBlock_->add(maskedIndex); 1.2401 + MAsmJSLoadFuncPtr *ptrFun = MAsmJSLoadFuncPtr::New(alloc(), table.globalDataOffset(), maskedIndex); 1.2402 + curBlock_->add(ptrFun); 1.2403 + 1.2404 + MIRType returnType = table.sig().retType().toMIRType(); 1.2405 + return callPrivate(MAsmJSCall::Callee(ptrFun), call, returnType, def); 1.2406 + } 1.2407 + 1.2408 + bool ffiCall(unsigned exitIndex, const Call &call, MIRType returnType, MDefinition **def) 1.2409 + { 1.2410 + if (inDeadCode()) { 1.2411 + *def = nullptr; 1.2412 + return true; 1.2413 + } 1.2414 + 1.2415 + JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0); 1.2416 + unsigned globalDataOffset = module().exitIndexToGlobalDataOffset(exitIndex); 1.2417 + 1.2418 + MAsmJSLoadFFIFunc *ptrFun = MAsmJSLoadFFIFunc::New(alloc(), globalDataOffset); 1.2419 + curBlock_->add(ptrFun); 1.2420 + 1.2421 + return callPrivate(MAsmJSCall::Callee(ptrFun), call, returnType, def); 1.2422 + } 1.2423 + 1.2424 + bool builtinCall(AsmJSImmKind builtin, const Call &call, MIRType returnType, MDefinition **def) 1.2425 + { 1.2426 + return callPrivate(MAsmJSCall::Callee(builtin), call, returnType, def); 1.2427 + } 1.2428 + 1.2429 + /*********************************************** Control flow generation */ 1.2430 + 1.2431 + inline bool inDeadCode() const { 1.2432 + return curBlock_ == nullptr; 1.2433 + } 1.2434 + 1.2435 + void returnExpr(MDefinition *expr) 1.2436 + { 1.2437 + if (inDeadCode()) 1.2438 + return; 1.2439 + MAsmJSReturn *ins = MAsmJSReturn::New(alloc(), expr); 1.2440 + curBlock_->end(ins); 1.2441 + curBlock_ = nullptr; 1.2442 + } 1.2443 + 1.2444 + void returnVoid() 1.2445 + { 1.2446 + if (inDeadCode()) 1.2447 + return; 1.2448 + MAsmJSVoidReturn *ins = MAsmJSVoidReturn::New(alloc()); 1.2449 + curBlock_->end(ins); 1.2450 + curBlock_ = nullptr; 1.2451 + } 1.2452 + 1.2453 + bool branchAndStartThen(MDefinition *cond, MBasicBlock **thenBlock, MBasicBlock **elseBlock, 1.2454 + ParseNode *thenPn, ParseNode* elsePn) 1.2455 + { 1.2456 + if (inDeadCode()) 1.2457 + return true; 1.2458 + 1.2459 + bool hasThenBlock = *thenBlock != nullptr; 1.2460 + bool hasElseBlock = *elseBlock != nullptr; 1.2461 + 1.2462 + if (!hasThenBlock && !newBlock(curBlock_, thenBlock, thenPn)) 1.2463 + return false; 1.2464 + if (!hasElseBlock && !newBlock(curBlock_, elseBlock, thenPn)) 1.2465 + return false; 1.2466 + 1.2467 + curBlock_->end(MTest::New(alloc(), cond, *thenBlock, *elseBlock)); 1.2468 + 1.2469 + // Only add as a predecessor if newBlock hasn't been called (as it does it for us) 1.2470 + if (hasThenBlock && !(*thenBlock)->addPredecessor(alloc(), curBlock_)) 1.2471 + return false; 1.2472 + if (hasElseBlock && !(*elseBlock)->addPredecessor(alloc(), curBlock_)) 1.2473 + return false; 1.2474 + 1.2475 + curBlock_ = *thenBlock; 1.2476 + mirGraph().moveBlockToEnd(curBlock_); 1.2477 + return true; 1.2478 + } 1.2479 + 1.2480 + void assertCurrentBlockIs(MBasicBlock *block) { 1.2481 + if (inDeadCode()) 1.2482 + return; 1.2483 + JS_ASSERT(curBlock_ == block); 1.2484 + } 1.2485 + 1.2486 + bool appendThenBlock(BlockVector *thenBlocks) 1.2487 + { 1.2488 + if (inDeadCode()) 1.2489 + return true; 1.2490 + return thenBlocks->append(curBlock_); 1.2491 + } 1.2492 + 1.2493 + bool joinIf(const BlockVector &thenBlocks, MBasicBlock *joinBlock) 1.2494 + { 1.2495 + if (!joinBlock) 1.2496 + return true; 1.2497 + JS_ASSERT_IF(curBlock_, thenBlocks.back() == curBlock_); 1.2498 + for (size_t i = 0; i < thenBlocks.length(); i++) { 1.2499 + thenBlocks[i]->end(MGoto::New(alloc(), joinBlock)); 1.2500 + if (!joinBlock->addPredecessor(alloc(), thenBlocks[i])) 1.2501 + return false; 1.2502 + } 1.2503 + curBlock_ = joinBlock; 1.2504 + mirGraph().moveBlockToEnd(curBlock_); 1.2505 + return true; 1.2506 + } 1.2507 + 1.2508 + void switchToElse(MBasicBlock *elseBlock) 1.2509 + { 1.2510 + if (!elseBlock) 1.2511 + return; 1.2512 + curBlock_ = elseBlock; 1.2513 + mirGraph().moveBlockToEnd(curBlock_); 1.2514 + } 1.2515 + 1.2516 + bool joinIfElse(const BlockVector &thenBlocks, ParseNode *pn) 1.2517 + { 1.2518 + if (inDeadCode() && thenBlocks.empty()) 1.2519 + return true; 1.2520 + MBasicBlock *pred = curBlock_ ? curBlock_ : thenBlocks[0]; 1.2521 + MBasicBlock *join; 1.2522 + if (!newBlock(pred, &join, pn)) 1.2523 + return false; 1.2524 + if (curBlock_) 1.2525 + curBlock_->end(MGoto::New(alloc(), join)); 1.2526 + for (size_t i = 0; i < thenBlocks.length(); i++) { 1.2527 + thenBlocks[i]->end(MGoto::New(alloc(), join)); 1.2528 + if (pred == curBlock_ || i > 0) { 1.2529 + if (!join->addPredecessor(alloc(), thenBlocks[i])) 1.2530 + return false; 1.2531 + } 1.2532 + } 1.2533 + curBlock_ = join; 1.2534 + return true; 1.2535 + } 1.2536 + 1.2537 + void pushPhiInput(MDefinition *def) 1.2538 + { 1.2539 + if (inDeadCode()) 1.2540 + return; 1.2541 + JS_ASSERT(curBlock_->stackDepth() == info().firstStackSlot()); 1.2542 + curBlock_->push(def); 1.2543 + } 1.2544 + 1.2545 + MDefinition *popPhiOutput() 1.2546 + { 1.2547 + if (inDeadCode()) 1.2548 + return nullptr; 1.2549 + JS_ASSERT(curBlock_->stackDepth() == info().firstStackSlot() + 1); 1.2550 + return curBlock_->pop(); 1.2551 + } 1.2552 + 1.2553 + bool startPendingLoop(ParseNode *pn, MBasicBlock **loopEntry, ParseNode *bodyStmt) 1.2554 + { 1.2555 + if (!loopStack_.append(pn) || !breakableStack_.append(pn)) 1.2556 + return false; 1.2557 + JS_ASSERT_IF(curBlock_, curBlock_->loopDepth() == loopStack_.length() - 1); 1.2558 + if (inDeadCode()) { 1.2559 + *loopEntry = nullptr; 1.2560 + return true; 1.2561 + } 1.2562 + *loopEntry = MBasicBlock::NewAsmJS(mirGraph(), info(), curBlock_, 1.2563 + MBasicBlock::PENDING_LOOP_HEADER); 1.2564 + if (!*loopEntry) 1.2565 + return false; 1.2566 + mirGraph().addBlock(*loopEntry); 1.2567 + noteBasicBlockPosition(*loopEntry, bodyStmt); 1.2568 + (*loopEntry)->setLoopDepth(loopStack_.length()); 1.2569 + curBlock_->end(MGoto::New(alloc(), *loopEntry)); 1.2570 + curBlock_ = *loopEntry; 1.2571 + return true; 1.2572 + } 1.2573 + 1.2574 + bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop, ParseNode *bodyPn, ParseNode *afterPn) 1.2575 + { 1.2576 + if (inDeadCode()) { 1.2577 + *afterLoop = nullptr; 1.2578 + return true; 1.2579 + } 1.2580 + JS_ASSERT(curBlock_->loopDepth() > 0); 1.2581 + MBasicBlock *body; 1.2582 + if (!newBlock(curBlock_, &body, bodyPn)) 1.2583 + return false; 1.2584 + if (cond->isConstant() && cond->toConstant()->valueToBoolean()) { 1.2585 + *afterLoop = nullptr; 1.2586 + curBlock_->end(MGoto::New(alloc(), body)); 1.2587 + } else { 1.2588 + if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop, afterPn)) 1.2589 + return false; 1.2590 + curBlock_->end(MTest::New(alloc(), cond, body, *afterLoop)); 1.2591 + } 1.2592 + curBlock_ = body; 1.2593 + return true; 1.2594 + } 1.2595 + 1.2596 + private: 1.2597 + ParseNode *popLoop() 1.2598 + { 1.2599 + ParseNode *pn = loopStack_.popCopy(); 1.2600 + JS_ASSERT(!unlabeledContinues_.has(pn)); 1.2601 + breakableStack_.popBack(); 1.2602 + return pn; 1.2603 + } 1.2604 + 1.2605 + public: 1.2606 + bool closeLoop(MBasicBlock *loopEntry, MBasicBlock *afterLoop) 1.2607 + { 1.2608 + ParseNode *pn = popLoop(); 1.2609 + if (!loopEntry) { 1.2610 + JS_ASSERT(!afterLoop); 1.2611 + JS_ASSERT(inDeadCode()); 1.2612 + JS_ASSERT(!unlabeledBreaks_.has(pn)); 1.2613 + return true; 1.2614 + } 1.2615 + JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); 1.2616 + JS_ASSERT_IF(afterLoop, afterLoop->loopDepth() == loopStack_.length()); 1.2617 + if (curBlock_) { 1.2618 + JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1); 1.2619 + curBlock_->end(MGoto::New(alloc(), loopEntry)); 1.2620 + if (!loopEntry->setBackedgeAsmJS(curBlock_)) 1.2621 + return false; 1.2622 + } 1.2623 + curBlock_ = afterLoop; 1.2624 + if (curBlock_) 1.2625 + mirGraph().moveBlockToEnd(curBlock_); 1.2626 + return bindUnlabeledBreaks(pn); 1.2627 + } 1.2628 + 1.2629 + bool branchAndCloseDoWhileLoop(MDefinition *cond, MBasicBlock *loopEntry, ParseNode *afterLoopStmt) 1.2630 + { 1.2631 + ParseNode *pn = popLoop(); 1.2632 + if (!loopEntry) { 1.2633 + JS_ASSERT(inDeadCode()); 1.2634 + JS_ASSERT(!unlabeledBreaks_.has(pn)); 1.2635 + return true; 1.2636 + } 1.2637 + JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); 1.2638 + if (curBlock_) { 1.2639 + JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1); 1.2640 + if (cond->isConstant()) { 1.2641 + if (cond->toConstant()->valueToBoolean()) { 1.2642 + curBlock_->end(MGoto::New(alloc(), loopEntry)); 1.2643 + if (!loopEntry->setBackedgeAsmJS(curBlock_)) 1.2644 + return false; 1.2645 + curBlock_ = nullptr; 1.2646 + } else { 1.2647 + MBasicBlock *afterLoop; 1.2648 + if (!newBlock(curBlock_, &afterLoop, afterLoopStmt)) 1.2649 + return false; 1.2650 + curBlock_->end(MGoto::New(alloc(), afterLoop)); 1.2651 + curBlock_ = afterLoop; 1.2652 + } 1.2653 + } else { 1.2654 + MBasicBlock *afterLoop; 1.2655 + if (!newBlock(curBlock_, &afterLoop, afterLoopStmt)) 1.2656 + return false; 1.2657 + curBlock_->end(MTest::New(alloc(), cond, loopEntry, afterLoop)); 1.2658 + if (!loopEntry->setBackedgeAsmJS(curBlock_)) 1.2659 + return false; 1.2660 + curBlock_ = afterLoop; 1.2661 + } 1.2662 + } 1.2663 + return bindUnlabeledBreaks(pn); 1.2664 + } 1.2665 + 1.2666 + bool bindContinues(ParseNode *pn, const LabelVector *maybeLabels) 1.2667 + { 1.2668 + bool createdJoinBlock = false; 1.2669 + if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(pn)) { 1.2670 + if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock, pn)) 1.2671 + return false; 1.2672 + unlabeledContinues_.remove(p); 1.2673 + } 1.2674 + return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_, &createdJoinBlock, pn); 1.2675 + } 1.2676 + 1.2677 + bool bindLabeledBreaks(const LabelVector *maybeLabels, ParseNode *pn) 1.2678 + { 1.2679 + bool createdJoinBlock = false; 1.2680 + return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_, &createdJoinBlock, pn); 1.2681 + } 1.2682 + 1.2683 + bool addBreak(PropertyName *maybeLabel) { 1.2684 + if (maybeLabel) 1.2685 + return addBreakOrContinue(maybeLabel, &labeledBreaks_); 1.2686 + return addBreakOrContinue(breakableStack_.back(), &unlabeledBreaks_); 1.2687 + } 1.2688 + 1.2689 + bool addContinue(PropertyName *maybeLabel) { 1.2690 + if (maybeLabel) 1.2691 + return addBreakOrContinue(maybeLabel, &labeledContinues_); 1.2692 + return addBreakOrContinue(loopStack_.back(), &unlabeledContinues_); 1.2693 + } 1.2694 + 1.2695 + bool startSwitch(ParseNode *pn, MDefinition *expr, int32_t low, int32_t high, 1.2696 + MBasicBlock **switchBlock) 1.2697 + { 1.2698 + if (!breakableStack_.append(pn)) 1.2699 + return false; 1.2700 + if (inDeadCode()) { 1.2701 + *switchBlock = nullptr; 1.2702 + return true; 1.2703 + } 1.2704 + curBlock_->end(MTableSwitch::New(alloc(), expr, low, high)); 1.2705 + *switchBlock = curBlock_; 1.2706 + curBlock_ = nullptr; 1.2707 + return true; 1.2708 + } 1.2709 + 1.2710 + bool startSwitchCase(MBasicBlock *switchBlock, MBasicBlock **next, ParseNode *pn) 1.2711 + { 1.2712 + if (!switchBlock) { 1.2713 + *next = nullptr; 1.2714 + return true; 1.2715 + } 1.2716 + if (!newBlock(switchBlock, next, pn)) 1.2717 + return false; 1.2718 + if (curBlock_) { 1.2719 + curBlock_->end(MGoto::New(alloc(), *next)); 1.2720 + if (!(*next)->addPredecessor(alloc(), curBlock_)) 1.2721 + return false; 1.2722 + } 1.2723 + curBlock_ = *next; 1.2724 + return true; 1.2725 + } 1.2726 + 1.2727 + bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock, ParseNode *pn) 1.2728 + { 1.2729 + if (!startSwitchCase(switchBlock, defaultBlock, pn)) 1.2730 + return false; 1.2731 + if (!*defaultBlock) 1.2732 + return true; 1.2733 + mirGraph().moveBlockToEnd(*defaultBlock); 1.2734 + return true; 1.2735 + } 1.2736 + 1.2737 + bool joinSwitch(MBasicBlock *switchBlock, const BlockVector &cases, MBasicBlock *defaultBlock) 1.2738 + { 1.2739 + ParseNode *pn = breakableStack_.popCopy(); 1.2740 + if (!switchBlock) 1.2741 + return true; 1.2742 + MTableSwitch *mir = switchBlock->lastIns()->toTableSwitch(); 1.2743 + size_t defaultIndex = mir->addDefault(defaultBlock); 1.2744 + for (unsigned i = 0; i < cases.length(); i++) { 1.2745 + if (!cases[i]) 1.2746 + mir->addCase(defaultIndex); 1.2747 + else 1.2748 + mir->addCase(mir->addSuccessor(cases[i])); 1.2749 + } 1.2750 + if (curBlock_) { 1.2751 + MBasicBlock *next; 1.2752 + if (!newBlock(curBlock_, &next, pn)) 1.2753 + return false; 1.2754 + curBlock_->end(MGoto::New(alloc(), next)); 1.2755 + curBlock_ = next; 1.2756 + } 1.2757 + return bindUnlabeledBreaks(pn); 1.2758 + } 1.2759 + 1.2760 + /*************************************************************************/ 1.2761 + 1.2762 + MIRGenerator *extractMIR() 1.2763 + { 1.2764 + JS_ASSERT(mirGen_ != nullptr); 1.2765 + MIRGenerator *mirGen = mirGen_; 1.2766 + mirGen_ = nullptr; 1.2767 + return mirGen; 1.2768 + } 1.2769 + 1.2770 + /*************************************************************************/ 1.2771 + private: 1.2772 + void noteBasicBlockPosition(MBasicBlock *blk, ParseNode *pn) 1.2773 + { 1.2774 +#if defined(JS_ION_PERF) 1.2775 + if (pn) { 1.2776 + unsigned line = 0U, column = 0U; 1.2777 + m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column); 1.2778 + blk->setLineno(line); 1.2779 + blk->setColumnIndex(column); 1.2780 + } 1.2781 +#endif 1.2782 + } 1.2783 + 1.2784 + bool newBlockWithDepth(MBasicBlock *pred, unsigned loopDepth, MBasicBlock **block, ParseNode *pn) 1.2785 + { 1.2786 + *block = MBasicBlock::NewAsmJS(mirGraph(), info(), pred, MBasicBlock::NORMAL); 1.2787 + if (!*block) 1.2788 + return false; 1.2789 + noteBasicBlockPosition(*block, pn); 1.2790 + mirGraph().addBlock(*block); 1.2791 + (*block)->setLoopDepth(loopDepth); 1.2792 + return true; 1.2793 + } 1.2794 + 1.2795 + bool newBlock(MBasicBlock *pred, MBasicBlock **block, ParseNode *pn) 1.2796 + { 1.2797 + return newBlockWithDepth(pred, loopStack_.length(), block, pn); 1.2798 + } 1.2799 + 1.2800 + bool bindBreaksOrContinues(BlockVector *preds, bool *createdJoinBlock, ParseNode *pn) 1.2801 + { 1.2802 + for (unsigned i = 0; i < preds->length(); i++) { 1.2803 + MBasicBlock *pred = (*preds)[i]; 1.2804 + if (*createdJoinBlock) { 1.2805 + pred->end(MGoto::New(alloc(), curBlock_)); 1.2806 + if (!curBlock_->addPredecessor(alloc(), pred)) 1.2807 + return false; 1.2808 + } else { 1.2809 + MBasicBlock *next; 1.2810 + if (!newBlock(pred, &next, pn)) 1.2811 + return false; 1.2812 + pred->end(MGoto::New(alloc(), next)); 1.2813 + if (curBlock_) { 1.2814 + curBlock_->end(MGoto::New(alloc(), next)); 1.2815 + if (!next->addPredecessor(alloc(), curBlock_)) 1.2816 + return false; 1.2817 + } 1.2818 + curBlock_ = next; 1.2819 + *createdJoinBlock = true; 1.2820 + } 1.2821 + JS_ASSERT(curBlock_->begin() == curBlock_->end()); 1.2822 + if (!mirGen_->ensureBallast()) 1.2823 + return false; 1.2824 + } 1.2825 + preds->clear(); 1.2826 + return true; 1.2827 + } 1.2828 + 1.2829 + bool bindLabeledBreaksOrContinues(const LabelVector *maybeLabels, LabeledBlockMap *map, 1.2830 + bool *createdJoinBlock, ParseNode *pn) 1.2831 + { 1.2832 + if (!maybeLabels) 1.2833 + return true; 1.2834 + const LabelVector &labels = *maybeLabels; 1.2835 + for (unsigned i = 0; i < labels.length(); i++) { 1.2836 + if (LabeledBlockMap::Ptr p = map->lookup(labels[i])) { 1.2837 + if (!bindBreaksOrContinues(&p->value(), createdJoinBlock, pn)) 1.2838 + return false; 1.2839 + map->remove(p); 1.2840 + } 1.2841 + if (!mirGen_->ensureBallast()) 1.2842 + return false; 1.2843 + } 1.2844 + return true; 1.2845 + } 1.2846 + 1.2847 + template <class Key, class Map> 1.2848 + bool addBreakOrContinue(Key key, Map *map) 1.2849 + { 1.2850 + if (inDeadCode()) 1.2851 + return true; 1.2852 + typename Map::AddPtr p = map->lookupForAdd(key); 1.2853 + if (!p) { 1.2854 + BlockVector empty(m().cx()); 1.2855 + if (!map->add(p, key, Move(empty))) 1.2856 + return false; 1.2857 + } 1.2858 + if (!p->value().append(curBlock_)) 1.2859 + return false; 1.2860 + curBlock_ = nullptr; 1.2861 + return true; 1.2862 + } 1.2863 + 1.2864 + bool bindUnlabeledBreaks(ParseNode *pn) 1.2865 + { 1.2866 + bool createdJoinBlock = false; 1.2867 + if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(pn)) { 1.2868 + if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock, pn)) 1.2869 + return false; 1.2870 + unlabeledBreaks_.remove(p); 1.2871 + } 1.2872 + return true; 1.2873 + } 1.2874 +}; 1.2875 + 1.2876 +} /* anonymous namespace */ 1.2877 + 1.2878 +/*****************************************************************************/ 1.2879 +// asm.js type-checking and code-generation algorithm 1.2880 + 1.2881 +static bool 1.2882 +CheckIdentifier(ModuleCompiler &m, ParseNode *usepn, PropertyName *name) 1.2883 +{ 1.2884 + if (name == m.cx()->names().arguments || name == m.cx()->names().eval) 1.2885 + return m.failName(usepn, "'%s' is not an allowed identifier", name); 1.2886 + return true; 1.2887 +} 1.2888 + 1.2889 +static bool 1.2890 +CheckModuleLevelName(ModuleCompiler &m, ParseNode *usepn, PropertyName *name) 1.2891 +{ 1.2892 + if (!CheckIdentifier(m, usepn, name)) 1.2893 + return false; 1.2894 + 1.2895 + if (name == m.moduleFunctionName() || 1.2896 + name == m.module().globalArgumentName() || 1.2897 + name == m.module().importArgumentName() || 1.2898 + name == m.module().bufferArgumentName() || 1.2899 + m.lookupGlobal(name)) 1.2900 + { 1.2901 + return m.failName(usepn, "duplicate name '%s' not allowed", name); 1.2902 + } 1.2903 + 1.2904 + return true; 1.2905 +} 1.2906 + 1.2907 +static bool 1.2908 +CheckFunctionHead(ModuleCompiler &m, ParseNode *fn) 1.2909 +{ 1.2910 + JSFunction *fun = FunctionObject(fn); 1.2911 + if (fun->hasRest()) 1.2912 + return m.fail(fn, "rest args not allowed"); 1.2913 + if (fun->isExprClosure()) 1.2914 + return m.fail(fn, "expression closures not allowed"); 1.2915 + if (fn->pn_funbox->hasDestructuringArgs) 1.2916 + return m.fail(fn, "destructuring args not allowed"); 1.2917 + return true; 1.2918 +} 1.2919 + 1.2920 +static bool 1.2921 +CheckArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name) 1.2922 +{ 1.2923 + if (!IsDefinition(arg)) 1.2924 + return m.fail(arg, "duplicate argument name not allowed"); 1.2925 + 1.2926 + if (arg->pn_dflags & PND_DEFAULT) 1.2927 + return m.fail(arg, "default arguments not allowed"); 1.2928 + 1.2929 + if (!CheckIdentifier(m, arg, arg->name())) 1.2930 + return false; 1.2931 + 1.2932 + *name = arg->name(); 1.2933 + return true; 1.2934 +} 1.2935 + 1.2936 +static bool 1.2937 +CheckModuleArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name) 1.2938 +{ 1.2939 + if (!CheckArgument(m, arg, name)) 1.2940 + return false; 1.2941 + 1.2942 + if (!CheckModuleLevelName(m, arg, *name)) 1.2943 + return false; 1.2944 + 1.2945 + return true; 1.2946 +} 1.2947 + 1.2948 +static bool 1.2949 +CheckModuleArguments(ModuleCompiler &m, ParseNode *fn) 1.2950 +{ 1.2951 + unsigned numFormals; 1.2952 + ParseNode *arg1 = FunctionArgsList(fn, &numFormals); 1.2953 + ParseNode *arg2 = arg1 ? NextNode(arg1) : nullptr; 1.2954 + ParseNode *arg3 = arg2 ? NextNode(arg2) : nullptr; 1.2955 + 1.2956 + if (numFormals > 3) 1.2957 + return m.fail(fn, "asm.js modules takes at most 3 argument"); 1.2958 + 1.2959 + PropertyName *arg1Name = nullptr; 1.2960 + if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name)) 1.2961 + return false; 1.2962 + m.initGlobalArgumentName(arg1Name); 1.2963 + 1.2964 + PropertyName *arg2Name = nullptr; 1.2965 + if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name)) 1.2966 + return false; 1.2967 + m.initImportArgumentName(arg2Name); 1.2968 + 1.2969 + PropertyName *arg3Name = nullptr; 1.2970 + if (numFormals >= 3 && !CheckModuleArgument(m, arg3, &arg3Name)) 1.2971 + return false; 1.2972 + m.initBufferArgumentName(arg3Name); 1.2973 + 1.2974 + return true; 1.2975 +} 1.2976 + 1.2977 +static bool 1.2978 +CheckPrecedingStatements(ModuleCompiler &m, ParseNode *stmtList) 1.2979 +{ 1.2980 + JS_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); 1.2981 + 1.2982 + if (ListLength(stmtList) != 0) 1.2983 + return m.fail(ListHead(stmtList), "invalid asm.js statement"); 1.2984 + 1.2985 + return true; 1.2986 +} 1.2987 + 1.2988 +static bool 1.2989 +CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode, 1.2990 + bool isConst) 1.2991 +{ 1.2992 + NumLit literal = ExtractNumericLiteral(m, initNode); 1.2993 + if (!literal.hasType()) 1.2994 + return m.fail(initNode, "global initializer is out of representable integer range"); 1.2995 + 1.2996 + return m.addGlobalVarInit(varName, literal.varType(), literal.value(), isConst); 1.2997 +} 1.2998 + 1.2999 +static bool 1.3000 +CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion, 1.3001 + ParseNode **coercedExpr = nullptr) 1.3002 +{ 1.3003 + switch (coercionNode->getKind()) { 1.3004 + case PNK_BITOR: { 1.3005 + ParseNode *rhs = BinaryRight(coercionNode); 1.3006 + uint32_t i; 1.3007 + if (!IsLiteralInt(m, rhs, &i) || i != 0) 1.3008 + return m.fail(rhs, "must use |0 for argument/return coercion"); 1.3009 + *coercion = AsmJS_ToInt32; 1.3010 + if (coercedExpr) 1.3011 + *coercedExpr = BinaryLeft(coercionNode); 1.3012 + return true; 1.3013 + } 1.3014 + case PNK_POS: { 1.3015 + *coercion = AsmJS_ToNumber; 1.3016 + if (coercedExpr) 1.3017 + *coercedExpr = UnaryKid(coercionNode); 1.3018 + return true; 1.3019 + } 1.3020 + case PNK_CALL: { 1.3021 + *coercion = AsmJS_FRound; 1.3022 + if (!IsFloatCoercion(m, coercionNode, coercedExpr)) 1.3023 + return m.fail(coercionNode, "call must be to fround coercion"); 1.3024 + return true; 1.3025 + } 1.3026 + default:; 1.3027 + } 1.3028 + 1.3029 + return m.fail(coercionNode, "must be of the form +x, fround(x) or x|0"); 1.3030 +} 1.3031 + 1.3032 +static bool 1.3033 +CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion, 1.3034 + ParseNode *coercedExpr, bool isConst) 1.3035 +{ 1.3036 + if (!coercedExpr->isKind(PNK_DOT)) 1.3037 + return m.failName(coercedExpr, "invalid import expression for global '%s'", varName); 1.3038 + 1.3039 + ParseNode *base = DotBase(coercedExpr); 1.3040 + PropertyName *field = DotMember(coercedExpr); 1.3041 + 1.3042 + PropertyName *importName = m.module().importArgumentName(); 1.3043 + if (!importName) 1.3044 + return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter"); 1.3045 + if (!IsUseOfName(base, importName)) 1.3046 + return m.failName(coercedExpr, "base of import expression must be '%s'", importName); 1.3047 + 1.3048 + return m.addGlobalVarImport(varName, field, coercion, isConst); 1.3049 +} 1.3050 + 1.3051 +static bool 1.3052 +CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode, 1.3053 + bool isConst) 1.3054 +{ 1.3055 + AsmJSCoercion coercion; 1.3056 + ParseNode *coercedExpr; 1.3057 + if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr)) 1.3058 + return false; 1.3059 + return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst); 1.3060 +} 1.3061 + 1.3062 +static bool 1.3063 +CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr) 1.3064 +{ 1.3065 + ParseNode *ctorExpr = ListHead(newExpr); 1.3066 + if (!ctorExpr->isKind(PNK_DOT)) 1.3067 + return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'"); 1.3068 + 1.3069 + ParseNode *base = DotBase(ctorExpr); 1.3070 + PropertyName *field = DotMember(ctorExpr); 1.3071 + 1.3072 + PropertyName *globalName = m.module().globalArgumentName(); 1.3073 + if (!globalName) 1.3074 + return m.fail(base, "cannot create array view without an asm.js global parameter"); 1.3075 + if (!IsUseOfName(base, globalName)) 1.3076 + return m.failName(base, "expecting '%s.*Array", globalName); 1.3077 + 1.3078 + ParseNode *bufArg = NextNode(ctorExpr); 1.3079 + if (!bufArg || NextNode(bufArg) != nullptr) 1.3080 + return m.fail(ctorExpr, "array view constructor takes exactly one argument"); 1.3081 + 1.3082 + PropertyName *bufferName = m.module().bufferArgumentName(); 1.3083 + if (!bufferName) 1.3084 + return m.fail(bufArg, "cannot create array view without an asm.js heap parameter"); 1.3085 + if (!IsUseOfName(bufArg, bufferName)) 1.3086 + return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName); 1.3087 + 1.3088 + JSAtomState &names = m.cx()->names(); 1.3089 + ArrayBufferView::ViewType type; 1.3090 + if (field == names.Int8Array) 1.3091 + type = ArrayBufferView::TYPE_INT8; 1.3092 + else if (field == names.Uint8Array) 1.3093 + type = ArrayBufferView::TYPE_UINT8; 1.3094 + else if (field == names.Int16Array) 1.3095 + type = ArrayBufferView::TYPE_INT16; 1.3096 + else if (field == names.Uint16Array) 1.3097 + type = ArrayBufferView::TYPE_UINT16; 1.3098 + else if (field == names.Int32Array) 1.3099 + type = ArrayBufferView::TYPE_INT32; 1.3100 + else if (field == names.Uint32Array) 1.3101 + type = ArrayBufferView::TYPE_UINT32; 1.3102 + else if (field == names.Float32Array) 1.3103 + type = ArrayBufferView::TYPE_FLOAT32; 1.3104 + else if (field == names.Float64Array) 1.3105 + type = ArrayBufferView::TYPE_FLOAT64; 1.3106 + else 1.3107 + return m.fail(ctorExpr, "could not match typed array name"); 1.3108 + 1.3109 + return m.addArrayView(varName, type, field); 1.3110 +} 1.3111 + 1.3112 +static bool 1.3113 +CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) 1.3114 +{ 1.3115 + ParseNode *base = DotBase(initNode); 1.3116 + PropertyName *field = DotMember(initNode); 1.3117 + 1.3118 + if (base->isKind(PNK_DOT)) { 1.3119 + ParseNode *global = DotBase(base); 1.3120 + PropertyName *math = DotMember(base); 1.3121 + if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math) 1.3122 + return m.fail(base, "expecting global.Math"); 1.3123 + 1.3124 + ModuleCompiler::MathBuiltin mathBuiltin; 1.3125 + if (!m.lookupStandardLibraryMathName(field, &mathBuiltin)) 1.3126 + return m.failName(initNode, "'%s' is not a standard Math builtin", field); 1.3127 + 1.3128 + switch (mathBuiltin.kind) { 1.3129 + case ModuleCompiler::MathBuiltin::Function: 1.3130 + return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field); 1.3131 + case ModuleCompiler::MathBuiltin::Constant: 1.3132 + return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field); 1.3133 + default: 1.3134 + break; 1.3135 + } 1.3136 + MOZ_ASSUME_UNREACHABLE("unexpected or uninitialized math builtin type"); 1.3137 + } 1.3138 + 1.3139 + if (IsUseOfName(base, m.module().globalArgumentName())) { 1.3140 + if (field == m.cx()->names().NaN) 1.3141 + return m.addGlobalConstant(varName, GenericNaN(), field); 1.3142 + if (field == m.cx()->names().Infinity) 1.3143 + return m.addGlobalConstant(varName, PositiveInfinity<double>(), field); 1.3144 + return m.failName(initNode, "'%s' is not a standard global constant", field); 1.3145 + } 1.3146 + 1.3147 + if (IsUseOfName(base, m.module().importArgumentName())) 1.3148 + return m.addFFI(varName, field); 1.3149 + 1.3150 + return m.fail(initNode, "expecting c.y where c is either the global or foreign parameter"); 1.3151 +} 1.3152 + 1.3153 +static bool 1.3154 +CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst) 1.3155 +{ 1.3156 + if (!IsDefinition(var)) 1.3157 + return m.fail(var, "import variable names must be unique"); 1.3158 + 1.3159 + if (!CheckModuleLevelName(m, var, var->name())) 1.3160 + return false; 1.3161 + 1.3162 + ParseNode *initNode = MaybeDefinitionInitializer(var); 1.3163 + if (!initNode) 1.3164 + return m.fail(var, "module import needs initializer"); 1.3165 + 1.3166 + if (IsNumericLiteral(m, initNode)) 1.3167 + return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst); 1.3168 + 1.3169 + if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS) || initNode->isKind(PNK_CALL)) 1.3170 + return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst); 1.3171 + 1.3172 + if (initNode->isKind(PNK_NEW)) 1.3173 + return CheckNewArrayView(m, var->name(), initNode); 1.3174 + 1.3175 + if (initNode->isKind(PNK_DOT)) 1.3176 + return CheckGlobalDotImport(m, var->name(), initNode); 1.3177 + 1.3178 + return m.fail(initNode, "unsupported import expression"); 1.3179 +} 1.3180 + 1.3181 +static bool 1.3182 +CheckModuleGlobals(ModuleCompiler &m) 1.3183 +{ 1.3184 + while (true) { 1.3185 + ParseNode *varStmt; 1.3186 + if (!ParseVarOrConstStatement(m.parser(), &varStmt)) 1.3187 + return false; 1.3188 + if (!varStmt) 1.3189 + break; 1.3190 + for (ParseNode *var = VarListHead(varStmt); var; var = NextNode(var)) { 1.3191 + if (!CheckModuleGlobal(m, var, varStmt->isKind(PNK_CONST))) 1.3192 + return false; 1.3193 + } 1.3194 + } 1.3195 + 1.3196 + return true; 1.3197 +} 1.3198 + 1.3199 +static bool 1.3200 +ArgFail(FunctionCompiler &f, PropertyName *argName, ParseNode *stmt) 1.3201 +{ 1.3202 + return f.failName(stmt, "expecting argument type declaration for '%s' of the " 1.3203 + "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName); 1.3204 +} 1.3205 + 1.3206 +static bool 1.3207 +CheckArgumentType(FunctionCompiler &f, ParseNode *stmt, PropertyName *name, VarType *type) 1.3208 +{ 1.3209 + if (!stmt || !IsExpressionStatement(stmt)) 1.3210 + return ArgFail(f, name, stmt ? stmt : f.fn()); 1.3211 + 1.3212 + ParseNode *initNode = ExpressionStatementExpr(stmt); 1.3213 + if (!initNode || !initNode->isKind(PNK_ASSIGN)) 1.3214 + return ArgFail(f, name, stmt); 1.3215 + 1.3216 + ParseNode *argNode = BinaryLeft(initNode); 1.3217 + ParseNode *coercionNode = BinaryRight(initNode); 1.3218 + 1.3219 + if (!IsUseOfName(argNode, name)) 1.3220 + return ArgFail(f, name, stmt); 1.3221 + 1.3222 + ParseNode *coercedExpr; 1.3223 + AsmJSCoercion coercion; 1.3224 + if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion, &coercedExpr)) 1.3225 + return false; 1.3226 + 1.3227 + if (!IsUseOfName(coercedExpr, name)) 1.3228 + return ArgFail(f, name, stmt); 1.3229 + 1.3230 + *type = VarType(coercion); 1.3231 + return true; 1.3232 +} 1.3233 + 1.3234 +static bool 1.3235 +CheckArguments(FunctionCompiler &f, ParseNode **stmtIter, VarTypeVector *argTypes) 1.3236 +{ 1.3237 + ParseNode *stmt = *stmtIter; 1.3238 + 1.3239 + unsigned numFormals; 1.3240 + ParseNode *argpn = FunctionArgsList(f.fn(), &numFormals); 1.3241 + 1.3242 + for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) { 1.3243 + PropertyName *name; 1.3244 + if (!CheckArgument(f.m(), argpn, &name)) 1.3245 + return false; 1.3246 + 1.3247 + VarType type; 1.3248 + if (!CheckArgumentType(f, stmt, name, &type)) 1.3249 + return false; 1.3250 + 1.3251 + if (!argTypes->append(type)) 1.3252 + return false; 1.3253 + 1.3254 + if (!f.addFormal(argpn, name, type)) 1.3255 + return false; 1.3256 + } 1.3257 + 1.3258 + *stmtIter = stmt; 1.3259 + return true; 1.3260 +} 1.3261 + 1.3262 +static bool 1.3263 +CheckFinalReturn(FunctionCompiler &f, ParseNode *stmt, RetType *retType) 1.3264 +{ 1.3265 + if (stmt && stmt->isKind(PNK_RETURN)) { 1.3266 + if (ParseNode *coercionNode = UnaryKid(stmt)) { 1.3267 + if (IsNumericLiteral(f.m(), coercionNode)) { 1.3268 + switch (ExtractNumericLiteral(f.m(), coercionNode).which()) { 1.3269 + case NumLit::BigUnsigned: 1.3270 + case NumLit::OutOfRangeInt: 1.3271 + return f.fail(coercionNode, "returned literal is out of integer range"); 1.3272 + case NumLit::Fixnum: 1.3273 + case NumLit::NegativeInt: 1.3274 + *retType = RetType::Signed; 1.3275 + break; 1.3276 + case NumLit::Double: 1.3277 + *retType = RetType::Double; 1.3278 + break; 1.3279 + case NumLit::Float: 1.3280 + *retType = RetType::Float; 1.3281 + break; 1.3282 + } 1.3283 + return true; 1.3284 + } 1.3285 + 1.3286 + AsmJSCoercion coercion; 1.3287 + if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion)) 1.3288 + return false; 1.3289 + 1.3290 + *retType = RetType(coercion); 1.3291 + return true; 1.3292 + } 1.3293 + 1.3294 + *retType = RetType::Void; 1.3295 + return true; 1.3296 + } 1.3297 + 1.3298 + *retType = RetType::Void; 1.3299 + f.returnVoid(); 1.3300 + return true; 1.3301 +} 1.3302 + 1.3303 +static bool 1.3304 +CheckVariable(FunctionCompiler &f, ParseNode *var) 1.3305 +{ 1.3306 + if (!IsDefinition(var)) 1.3307 + return f.fail(var, "local variable names must not restate argument names"); 1.3308 + 1.3309 + PropertyName *name = var->name(); 1.3310 + 1.3311 + if (!CheckIdentifier(f.m(), var, name)) 1.3312 + return false; 1.3313 + 1.3314 + ParseNode *initNode = MaybeDefinitionInitializer(var); 1.3315 + if (!initNode) 1.3316 + return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name); 1.3317 + 1.3318 + if (initNode->isKind(PNK_NAME)) { 1.3319 + PropertyName *initName = initNode->name(); 1.3320 + if (const ModuleCompiler::Global *global = f.lookupGlobal(initName)) { 1.3321 + if (global->which() != ModuleCompiler::Global::ConstantLiteral) 1.3322 + return f.failName(initNode, "'%s' isn't a possible global variable initializer, " 1.3323 + "needs to be a const numeric literal", initName); 1.3324 + return f.addVariable(var, name, global->varOrConstType(), global->constLiteralValue()); 1.3325 + } 1.3326 + return f.failName(initNode, "'%s' needs to be a global name", initName); 1.3327 + } 1.3328 + 1.3329 + if (!IsNumericLiteral(f.m(), initNode)) 1.3330 + return f.failName(initNode, "initializer for '%s' needs to be a numeric literal or a global const literal", name); 1.3331 + 1.3332 + NumLit literal = ExtractNumericLiteral(f.m(), initNode); 1.3333 + if (!literal.hasType()) 1.3334 + return f.failName(initNode, "initializer for '%s' is out of range", name); 1.3335 + 1.3336 + return f.addVariable(var, name, literal.varType(), literal.value()); 1.3337 +} 1.3338 + 1.3339 +static bool 1.3340 +CheckVariables(FunctionCompiler &f, ParseNode **stmtIter) 1.3341 +{ 1.3342 + ParseNode *stmt = *stmtIter; 1.3343 + 1.3344 + for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) { 1.3345 + for (ParseNode *var = VarListHead(stmt); var; var = NextNode(var)) { 1.3346 + if (!CheckVariable(f, var)) 1.3347 + return false; 1.3348 + } 1.3349 + } 1.3350 + 1.3351 + *stmtIter = stmt; 1.3352 + return true; 1.3353 +} 1.3354 + 1.3355 +static bool 1.3356 +CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type); 1.3357 + 1.3358 +static bool 1.3359 +CheckNumericLiteral(FunctionCompiler &f, ParseNode *num, MDefinition **def, Type *type) 1.3360 +{ 1.3361 + NumLit literal = ExtractNumericLiteral(f.m(), num); 1.3362 + if (!literal.hasType()) 1.3363 + return f.fail(num, "numeric literal out of representable integer range"); 1.3364 + 1.3365 + *type = literal.type(); 1.3366 + *def = f.constant(literal.value(), literal.type()); 1.3367 + return true; 1.3368 +} 1.3369 + 1.3370 +static bool 1.3371 +CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *type) 1.3372 +{ 1.3373 + PropertyName *name = varRef->name(); 1.3374 + 1.3375 + if (const FunctionCompiler::Local *local = f.lookupLocal(name)) { 1.3376 + *def = f.getLocalDef(*local); 1.3377 + *type = local->type.toType(); 1.3378 + return true; 1.3379 + } 1.3380 + 1.3381 + if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) { 1.3382 + switch (global->which()) { 1.3383 + case ModuleCompiler::Global::ConstantLiteral: 1.3384 + *def = f.constant(global->constLiteralValue(), global->varOrConstType().toType()); 1.3385 + *type = global->varOrConstType().toType(); 1.3386 + break; 1.3387 + case ModuleCompiler::Global::ConstantImport: 1.3388 + case ModuleCompiler::Global::Variable: 1.3389 + *def = f.loadGlobalVar(*global); 1.3390 + *type = global->varOrConstType().toType(); 1.3391 + break; 1.3392 + case ModuleCompiler::Global::Function: 1.3393 + case ModuleCompiler::Global::FFI: 1.3394 + case ModuleCompiler::Global::MathBuiltinFunction: 1.3395 + case ModuleCompiler::Global::FuncPtrTable: 1.3396 + case ModuleCompiler::Global::ArrayView: 1.3397 + return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name); 1.3398 + } 1.3399 + return true; 1.3400 + } 1.3401 + 1.3402 + return f.failName(varRef, "'%s' not found in local or asm.js module scope", name); 1.3403 +} 1.3404 + 1.3405 +static inline bool 1.3406 +IsLiteralOrConstInt(FunctionCompiler &f, ParseNode *pn, uint32_t *u32) 1.3407 +{ 1.3408 + if (IsLiteralInt(f.m(), pn, u32)) 1.3409 + return true; 1.3410 + 1.3411 + if (pn->getKind() != PNK_NAME) 1.3412 + return false; 1.3413 + 1.3414 + PropertyName *name = pn->name(); 1.3415 + const ModuleCompiler::Global *global = f.lookupGlobal(name); 1.3416 + if (!global || global->which() != ModuleCompiler::Global::ConstantLiteral) 1.3417 + return false; 1.3418 + 1.3419 + const Value &v = global->constLiteralValue(); 1.3420 + if (!v.isInt32()) 1.3421 + return false; 1.3422 + 1.3423 + *u32 = (uint32_t) v.toInt32(); 1.3424 + return true; 1.3425 +} 1.3426 + 1.3427 +static bool 1.3428 +FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask, 1.3429 + NeedsBoundsCheck *needsBoundsCheck) 1.3430 +{ 1.3431 + ParseNode *indexNode = BinaryLeft(*indexExpr); 1.3432 + ParseNode *maskNode = BinaryRight(*indexExpr); 1.3433 + 1.3434 + uint32_t mask2; 1.3435 + if (IsLiteralOrConstInt(f, maskNode, &mask2)) { 1.3436 + // Flag the access to skip the bounds check if the mask ensures that an 'out of 1.3437 + // bounds' access can not occur based on the current heap length constraint. 1.3438 + if (mask2 == 0 || 1.3439 + CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) { 1.3440 + *needsBoundsCheck = NO_BOUNDS_CHECK; 1.3441 + } 1.3442 + *mask &= mask2; 1.3443 + *indexExpr = indexNode; 1.3444 + return true; 1.3445 + } 1.3446 + 1.3447 + return false; 1.3448 +} 1.3449 + 1.3450 +static bool 1.3451 +CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType, 1.3452 + MDefinition **def, NeedsBoundsCheck *needsBoundsCheck) 1.3453 +{ 1.3454 + ParseNode *viewName = ElemBase(elem); 1.3455 + ParseNode *indexExpr = ElemIndex(elem); 1.3456 + *needsBoundsCheck = NEEDS_BOUNDS_CHECK; 1.3457 + 1.3458 + if (!viewName->isKind(PNK_NAME)) 1.3459 + return f.fail(viewName, "base of array access must be a typed array view name"); 1.3460 + 1.3461 + const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name()); 1.3462 + if (!global || global->which() != ModuleCompiler::Global::ArrayView) 1.3463 + return f.fail(viewName, "base of array access must be a typed array view name"); 1.3464 + 1.3465 + *viewType = global->viewType(); 1.3466 + 1.3467 + uint32_t pointer; 1.3468 + if (IsLiteralOrConstInt(f, indexExpr, &pointer)) { 1.3469 + if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType))) 1.3470 + return f.fail(indexExpr, "constant index out of range"); 1.3471 + pointer <<= TypedArrayShift(*viewType); 1.3472 + // It is adequate to note pointer+1 rather than rounding up to the next 1.3473 + // access-size boundary because access is always aligned and the constraint 1.3474 + // will be rounded up to a larger alignment later. 1.3475 + f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1); 1.3476 + *needsBoundsCheck = NO_BOUNDS_CHECK; 1.3477 + *def = f.constant(Int32Value(pointer), Type::Int); 1.3478 + return true; 1.3479 + } 1.3480 + 1.3481 + // Mask off the low bits to account for the clearing effect of a right shift 1.3482 + // followed by the left shift implicit in the array access. E.g., H32[i>>2] 1.3483 + // loses the low two bits. 1.3484 + int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1); 1.3485 + 1.3486 + MDefinition *pointerDef; 1.3487 + if (indexExpr->isKind(PNK_RSH)) { 1.3488 + ParseNode *shiftNode = BinaryRight(indexExpr); 1.3489 + ParseNode *pointerNode = BinaryLeft(indexExpr); 1.3490 + 1.3491 + uint32_t shift; 1.3492 + if (!IsLiteralInt(f.m(), shiftNode, &shift)) 1.3493 + return f.failf(shiftNode, "shift amount must be constant"); 1.3494 + 1.3495 + unsigned requiredShift = TypedArrayShift(*viewType); 1.3496 + if (shift != requiredShift) 1.3497 + return f.failf(shiftNode, "shift amount must be %u", requiredShift); 1.3498 + 1.3499 + if (pointerNode->isKind(PNK_BITAND)) 1.3500 + FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck); 1.3501 + 1.3502 + // Fold a 'literal constant right shifted' now, and skip the bounds check if 1.3503 + // currently possible. This handles the optimization of many of these uses without 1.3504 + // the need for range analysis, and saves the generation of a MBitAnd op. 1.3505 + if (IsLiteralOrConstInt(f, pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) { 1.3506 + // Cases: b[c>>n], and b[(c&m)>>n] 1.3507 + pointer &= mask; 1.3508 + if (pointer < f.m().minHeapLength()) 1.3509 + *needsBoundsCheck = NO_BOUNDS_CHECK; 1.3510 + *def = f.constant(Int32Value(pointer), Type::Int); 1.3511 + return true; 1.3512 + } 1.3513 + 1.3514 + Type pointerType; 1.3515 + if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType)) 1.3516 + return false; 1.3517 + 1.3518 + if (!pointerType.isIntish()) 1.3519 + return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars()); 1.3520 + } else { 1.3521 + if (TypedArrayShift(*viewType) != 0) 1.3522 + return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access"); 1.3523 + 1.3524 + JS_ASSERT(mask == -1); 1.3525 + bool folded = false; 1.3526 + 1.3527 + if (indexExpr->isKind(PNK_BITAND)) 1.3528 + folded = FoldMaskedArrayIndex(f, &indexExpr, &mask, needsBoundsCheck); 1.3529 + 1.3530 + Type pointerType; 1.3531 + if (!CheckExpr(f, indexExpr, &pointerDef, &pointerType)) 1.3532 + return false; 1.3533 + 1.3534 + if (folded) { 1.3535 + if (!pointerType.isIntish()) 1.3536 + return f.failf(indexExpr, "%s is not a subtype of intish", pointerType.toChars()); 1.3537 + } else { 1.3538 + if (!pointerType.isInt()) 1.3539 + return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars()); 1.3540 + } 1.3541 + } 1.3542 + 1.3543 + // Don't generate the mask op if there is no need for it which could happen for 1.3544 + // a shift of zero. 1.3545 + if (mask == -1) 1.3546 + *def = pointerDef; 1.3547 + else 1.3548 + *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask), Type::Int)); 1.3549 + 1.3550 + return true; 1.3551 +} 1.3552 + 1.3553 +static bool 1.3554 +CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type) 1.3555 +{ 1.3556 + ArrayBufferView::ViewType viewType; 1.3557 + MDefinition *pointerDef; 1.3558 + NeedsBoundsCheck needsBoundsCheck; 1.3559 + if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck)) 1.3560 + return false; 1.3561 + 1.3562 + *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck); 1.3563 + *type = TypedArrayLoadType(viewType); 1.3564 + return true; 1.3565 +} 1.3566 + 1.3567 +static bool 1.3568 +CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type) 1.3569 +{ 1.3570 + ArrayBufferView::ViewType viewType; 1.3571 + MDefinition *pointerDef; 1.3572 + NeedsBoundsCheck needsBoundsCheck; 1.3573 + if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck)) 1.3574 + return false; 1.3575 + 1.3576 + MDefinition *rhsDef; 1.3577 + Type rhsType; 1.3578 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.3579 + return false; 1.3580 + 1.3581 + switch (viewType) { 1.3582 + case ArrayBufferView::TYPE_INT8: 1.3583 + case ArrayBufferView::TYPE_INT16: 1.3584 + case ArrayBufferView::TYPE_INT32: 1.3585 + case ArrayBufferView::TYPE_UINT8: 1.3586 + case ArrayBufferView::TYPE_UINT16: 1.3587 + case ArrayBufferView::TYPE_UINT32: 1.3588 + if (!rhsType.isIntish()) 1.3589 + return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars()); 1.3590 + break; 1.3591 + case ArrayBufferView::TYPE_FLOAT32: 1.3592 + if (rhsType.isMaybeDouble()) 1.3593 + rhsDef = f.unary<MToFloat32>(rhsDef); 1.3594 + else if (!rhsType.isFloatish()) 1.3595 + return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars()); 1.3596 + break; 1.3597 + case ArrayBufferView::TYPE_FLOAT64: 1.3598 + if (rhsType.isFloat()) 1.3599 + rhsDef = f.unary<MToDouble>(rhsDef); 1.3600 + else if (!rhsType.isMaybeDouble()) 1.3601 + return f.failf(lhs, "%s is not a subtype of float or double?", rhsType.toChars()); 1.3602 + break; 1.3603 + default: 1.3604 + MOZ_ASSUME_UNREACHABLE("Unexpected view type"); 1.3605 + } 1.3606 + 1.3607 + f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck); 1.3608 + 1.3609 + *def = rhsDef; 1.3610 + *type = rhsType; 1.3611 + return true; 1.3612 +} 1.3613 + 1.3614 +static bool 1.3615 +CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type) 1.3616 +{ 1.3617 + Rooted<PropertyName *> name(f.cx(), lhs->name()); 1.3618 + 1.3619 + MDefinition *rhsDef; 1.3620 + Type rhsType; 1.3621 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.3622 + return false; 1.3623 + 1.3624 + if (const FunctionCompiler::Local *lhsVar = f.lookupLocal(name)) { 1.3625 + if (!(rhsType <= lhsVar->type)) { 1.3626 + return f.failf(lhs, "%s is not a subtype of %s", 1.3627 + rhsType.toChars(), lhsVar->type.toType().toChars()); 1.3628 + } 1.3629 + f.assign(*lhsVar, rhsDef); 1.3630 + } else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) { 1.3631 + if (global->which() != ModuleCompiler::Global::Variable) 1.3632 + return f.failName(lhs, "'%s' is not a mutable variable", name); 1.3633 + if (!(rhsType <= global->varOrConstType())) { 1.3634 + return f.failf(lhs, "%s is not a subtype of %s", 1.3635 + rhsType.toChars(), global->varOrConstType().toType().toChars()); 1.3636 + } 1.3637 + f.storeGlobalVar(*global, rhsDef); 1.3638 + } else { 1.3639 + return f.failName(lhs, "'%s' not found in local or asm.js module scope", name); 1.3640 + } 1.3641 + 1.3642 + *def = rhsDef; 1.3643 + *type = rhsType; 1.3644 + return true; 1.3645 +} 1.3646 + 1.3647 +static bool 1.3648 +CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type) 1.3649 +{ 1.3650 + JS_ASSERT(assign->isKind(PNK_ASSIGN)); 1.3651 + ParseNode *lhs = BinaryLeft(assign); 1.3652 + ParseNode *rhs = BinaryRight(assign); 1.3653 + 1.3654 + if (lhs->getKind() == PNK_ELEM) 1.3655 + return CheckStoreArray(f, lhs, rhs, def, type); 1.3656 + 1.3657 + if (lhs->getKind() == PNK_NAME) 1.3658 + return CheckAssignName(f, lhs, rhs, def, type); 1.3659 + 1.3660 + return f.fail(assign, "left-hand side of assignment must be a variable or array access"); 1.3661 +} 1.3662 + 1.3663 +static bool 1.3664 +CheckMathIMul(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type) 1.3665 +{ 1.3666 + if (CallArgListLength(call) != 2) 1.3667 + return f.fail(call, "Math.imul must be passed 2 arguments"); 1.3668 + 1.3669 + ParseNode *lhs = CallArgList(call); 1.3670 + ParseNode *rhs = NextNode(lhs); 1.3671 + 1.3672 + MDefinition *lhsDef; 1.3673 + Type lhsType; 1.3674 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.3675 + return false; 1.3676 + 1.3677 + MDefinition *rhsDef; 1.3678 + Type rhsType; 1.3679 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.3680 + return false; 1.3681 + 1.3682 + if (!lhsType.isIntish()) 1.3683 + return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars()); 1.3684 + if (!rhsType.isIntish()) 1.3685 + return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars()); 1.3686 + if (retType != RetType::Signed) 1.3687 + return f.failf(call, "return type is signed, used as %s", retType.toType().toChars()); 1.3688 + 1.3689 + *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); 1.3690 + *type = Type::Signed; 1.3691 + return true; 1.3692 +} 1.3693 + 1.3694 +static bool 1.3695 +CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type) 1.3696 +{ 1.3697 + if (CallArgListLength(call) != 1) 1.3698 + return f.fail(call, "Math.abs must be passed 1 argument"); 1.3699 + 1.3700 + ParseNode *arg = CallArgList(call); 1.3701 + 1.3702 + MDefinition *argDef; 1.3703 + Type argType; 1.3704 + if (!CheckExpr(f, arg, &argDef, &argType)) 1.3705 + return false; 1.3706 + 1.3707 + if (argType.isSigned()) { 1.3708 + if (retType != RetType::Signed) 1.3709 + return f.failf(call, "return type is signed, used as %s", retType.toType().toChars()); 1.3710 + *def = f.unary<MAbs>(argDef, MIRType_Int32); 1.3711 + *type = Type::Signed; 1.3712 + return true; 1.3713 + } 1.3714 + 1.3715 + if (argType.isMaybeDouble()) { 1.3716 + if (retType != RetType::Double) 1.3717 + return f.failf(call, "return type is double, used as %s", retType.toType().toChars()); 1.3718 + *def = f.unary<MAbs>(argDef, MIRType_Double); 1.3719 + *type = Type::Double; 1.3720 + return true; 1.3721 + } 1.3722 + 1.3723 + if (argType.isMaybeFloat()) { 1.3724 + if (retType != RetType::Float) 1.3725 + return f.failf(call, "return type is float, used as %s", retType.toType().toChars()); 1.3726 + *def = f.unary<MAbs>(argDef, MIRType_Float32); 1.3727 + *type = Type::Float; 1.3728 + return true; 1.3729 + } 1.3730 + 1.3731 + return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars()); 1.3732 +} 1.3733 + 1.3734 +static bool 1.3735 +CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type) 1.3736 +{ 1.3737 + if (CallArgListLength(call) != 1) 1.3738 + return f.fail(call, "Math.sqrt must be passed 1 argument"); 1.3739 + 1.3740 + ParseNode *arg = CallArgList(call); 1.3741 + 1.3742 + MDefinition *argDef; 1.3743 + Type argType; 1.3744 + if (!CheckExpr(f, arg, &argDef, &argType)) 1.3745 + return false; 1.3746 + 1.3747 + if (argType.isMaybeDouble()) { 1.3748 + if (retType != RetType::Double) 1.3749 + return f.failf(call, "return type is double, used as %s", retType.toType().toChars()); 1.3750 + *def = f.unary<MSqrt>(argDef, MIRType_Double); 1.3751 + *type = Type::Double; 1.3752 + return true; 1.3753 + } 1.3754 + 1.3755 + if (argType.isMaybeFloat()) { 1.3756 + if (retType != RetType::Float) 1.3757 + return f.failf(call, "return type is float, used as %s", retType.toType().toChars()); 1.3758 + *def = f.unary<MSqrt>(argDef, MIRType_Float32); 1.3759 + *type = Type::Float; 1.3760 + return true; 1.3761 + } 1.3762 + 1.3763 + return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars()); 1.3764 +} 1.3765 + 1.3766 +static bool 1.3767 +CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type, bool isMax) 1.3768 +{ 1.3769 + if (CallArgListLength(callNode) < 2) 1.3770 + return f.fail(callNode, "Math.min/max must be passed at least 2 arguments"); 1.3771 + 1.3772 + ParseNode *firstArg = CallArgList(callNode); 1.3773 + MDefinition *firstDef; 1.3774 + Type firstType; 1.3775 + if (!CheckExpr(f, firstArg, &firstDef, &firstType)) 1.3776 + return false; 1.3777 + 1.3778 + bool opIsDouble = firstType.isMaybeDouble(); 1.3779 + bool opIsInteger = firstType.isInt(); 1.3780 + MIRType opType = firstType.toMIRType(); 1.3781 + 1.3782 + if (!opIsDouble && !opIsInteger) 1.3783 + return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars()); 1.3784 + 1.3785 + MDefinition *lastDef = firstDef; 1.3786 + ParseNode *nextArg = NextNode(firstArg); 1.3787 + for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) { 1.3788 + MDefinition *nextDef; 1.3789 + Type nextType; 1.3790 + if (!CheckExpr(f, nextArg, &nextDef, &nextType)) 1.3791 + return false; 1.3792 + 1.3793 + if (opIsDouble && !nextType.isMaybeDouble()) 1.3794 + return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars()); 1.3795 + if (opIsInteger && !nextType.isInt()) 1.3796 + return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars()); 1.3797 + 1.3798 + lastDef = f.minMax(lastDef, nextDef, opType, isMax); 1.3799 + } 1.3800 + 1.3801 + if (opIsDouble && retType != RetType::Double) 1.3802 + return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars()); 1.3803 + if (opIsInteger && retType != RetType::Signed) 1.3804 + return f.failf(callNode, "return type is int, used as %s", retType.toType().toChars()); 1.3805 + 1.3806 + *type = opIsDouble ? Type::Double : Type::Signed; 1.3807 + *def = lastDef; 1.3808 + return true; 1.3809 +} 1.3810 + 1.3811 +typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type); 1.3812 + 1.3813 +static bool 1.3814 +CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, CheckArgType checkArg, 1.3815 + FunctionCompiler::Call *call) 1.3816 +{ 1.3817 + f.startCallArgs(call); 1.3818 + 1.3819 + ParseNode *argNode = CallArgList(callNode); 1.3820 + for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) { 1.3821 + MDefinition *def; 1.3822 + Type type; 1.3823 + if (!CheckExpr(f, argNode, &def, &type)) 1.3824 + return false; 1.3825 + 1.3826 + if (!checkArg(f, argNode, type)) 1.3827 + return false; 1.3828 + 1.3829 + if (!f.passArg(def, VarType::FromCheckedType(type), call)) 1.3830 + return false; 1.3831 + } 1.3832 + 1.3833 + f.finishCallArgs(call); 1.3834 + return true; 1.3835 +} 1.3836 + 1.3837 +static bool 1.3838 +CheckSignatureAgainstExisting(ModuleCompiler &m, ParseNode *usepn, const Signature &sig, 1.3839 + const Signature &existing) 1.3840 +{ 1.3841 + if (sig.args().length() != existing.args().length()) { 1.3842 + return m.failf(usepn, "incompatible number of arguments (%u here vs. %u before)", 1.3843 + sig.args().length(), existing.args().length()); 1.3844 + } 1.3845 + 1.3846 + for (unsigned i = 0; i < sig.args().length(); i++) { 1.3847 + if (sig.arg(i) != existing.arg(i)) { 1.3848 + return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)", 1.3849 + i, sig.arg(i).toType().toChars(), existing.arg(i).toType().toChars()); 1.3850 + } 1.3851 + } 1.3852 + 1.3853 + if (sig.retType() != existing.retType()) { 1.3854 + return m.failf(usepn, "%s incompatible with previous return of type %s", 1.3855 + sig.retType().toType().toChars(), existing.retType().toType().toChars()); 1.3856 + } 1.3857 + 1.3858 + JS_ASSERT(sig == existing); 1.3859 + return true; 1.3860 +} 1.3861 + 1.3862 +static bool 1.3863 +CheckFunctionSignature(ModuleCompiler &m, ParseNode *usepn, Signature &&sig, PropertyName *name, 1.3864 + ModuleCompiler::Func **func) 1.3865 +{ 1.3866 + ModuleCompiler::Func *existing = m.lookupFunction(name); 1.3867 + if (!existing) { 1.3868 + if (!CheckModuleLevelName(m, usepn, name)) 1.3869 + return false; 1.3870 + return m.addFunction(name, Move(sig), func); 1.3871 + } 1.3872 + 1.3873 + if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig())) 1.3874 + return false; 1.3875 + 1.3876 + *func = existing; 1.3877 + return true; 1.3878 +} 1.3879 + 1.3880 +static bool 1.3881 +CheckIsVarType(FunctionCompiler &f, ParseNode *argNode, Type type) 1.3882 +{ 1.3883 + if (!type.isVarType()) 1.3884 + return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars()); 1.3885 + return true; 1.3886 +} 1.3887 + 1.3888 +static bool 1.3889 +CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, PropertyName *calleeName, 1.3890 + RetType retType, MDefinition **def, Type *type) 1.3891 +{ 1.3892 + FunctionCompiler::Call call(f, callNode, retType); 1.3893 + 1.3894 + if (!CheckCallArgs(f, callNode, CheckIsVarType, &call)) 1.3895 + return false; 1.3896 + 1.3897 + ModuleCompiler::Func *callee; 1.3898 + if (!CheckFunctionSignature(f.m(), callNode, Move(call.sig()), calleeName, &callee)) 1.3899 + return false; 1.3900 + 1.3901 + if (!f.internalCall(*callee, call, def)) 1.3902 + return false; 1.3903 + 1.3904 + *type = retType.toType(); 1.3905 + return true; 1.3906 +} 1.3907 + 1.3908 +static bool 1.3909 +CheckFuncPtrTableAgainstExisting(ModuleCompiler &m, ParseNode *usepn, 1.3910 + PropertyName *name, Signature &&sig, unsigned mask, 1.3911 + ModuleCompiler::FuncPtrTable **tableOut) 1.3912 +{ 1.3913 + if (const ModuleCompiler::Global *existing = m.lookupGlobal(name)) { 1.3914 + if (existing->which() != ModuleCompiler::Global::FuncPtrTable) 1.3915 + return m.failName(usepn, "'%s' is not a function-pointer table", name); 1.3916 + 1.3917 + ModuleCompiler::FuncPtrTable &table = m.funcPtrTable(existing->funcPtrTableIndex()); 1.3918 + if (mask != table.mask()) 1.3919 + return m.failf(usepn, "mask does not match previous value (%u)", table.mask()); 1.3920 + 1.3921 + if (!CheckSignatureAgainstExisting(m, usepn, sig, table.sig())) 1.3922 + return false; 1.3923 + 1.3924 + *tableOut = &table; 1.3925 + return true; 1.3926 + } 1.3927 + 1.3928 + if (!CheckModuleLevelName(m, usepn, name)) 1.3929 + return false; 1.3930 + 1.3931 + return m.addFuncPtrTable(name, Move(sig), mask, tableOut); 1.3932 +} 1.3933 + 1.3934 +static bool 1.3935 +CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type) 1.3936 +{ 1.3937 + ParseNode *callee = CallCallee(callNode); 1.3938 + ParseNode *tableNode = ElemBase(callee); 1.3939 + ParseNode *indexExpr = ElemIndex(callee); 1.3940 + 1.3941 + if (!tableNode->isKind(PNK_NAME)) 1.3942 + return f.fail(tableNode, "expecting name of function-pointer array"); 1.3943 + 1.3944 + PropertyName *name = tableNode->name(); 1.3945 + if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) { 1.3946 + if (existing->which() != ModuleCompiler::Global::FuncPtrTable) 1.3947 + return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name); 1.3948 + } 1.3949 + 1.3950 + if (!indexExpr->isKind(PNK_BITAND)) 1.3951 + return f.fail(indexExpr, "function-pointer table index expression needs & mask"); 1.3952 + 1.3953 + ParseNode *indexNode = BinaryLeft(indexExpr); 1.3954 + ParseNode *maskNode = BinaryRight(indexExpr); 1.3955 + 1.3956 + uint32_t mask; 1.3957 + if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1)) 1.3958 + return f.fail(maskNode, "function-pointer table index mask value must be a power of two"); 1.3959 + 1.3960 + MDefinition *indexDef; 1.3961 + Type indexType; 1.3962 + if (!CheckExpr(f, indexNode, &indexDef, &indexType)) 1.3963 + return false; 1.3964 + 1.3965 + if (!indexType.isIntish()) 1.3966 + return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars()); 1.3967 + 1.3968 + FunctionCompiler::Call call(f, callNode, retType); 1.3969 + 1.3970 + if (!CheckCallArgs(f, callNode, CheckIsVarType, &call)) 1.3971 + return false; 1.3972 + 1.3973 + ModuleCompiler::FuncPtrTable *table; 1.3974 + if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(call.sig()), mask, &table)) 1.3975 + return false; 1.3976 + 1.3977 + if (!f.funcPtrCall(*table, indexDef, call, def)) 1.3978 + return false; 1.3979 + 1.3980 + *type = retType.toType(); 1.3981 + return true; 1.3982 +} 1.3983 + 1.3984 +static bool 1.3985 +CheckIsExternType(FunctionCompiler &f, ParseNode *argNode, Type type) 1.3986 +{ 1.3987 + if (!type.isExtern()) 1.3988 + return f.failf(argNode, "%s is not a subtype of extern", type.toChars()); 1.3989 + return true; 1.3990 +} 1.3991 + 1.3992 +static bool 1.3993 +CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetType retType, 1.3994 + MDefinition **def, Type *type) 1.3995 +{ 1.3996 + PropertyName *calleeName = CallCallee(callNode)->name(); 1.3997 + 1.3998 + if (retType == RetType::Float) 1.3999 + return f.fail(callNode, "FFI calls can't return float"); 1.4000 + 1.4001 + FunctionCompiler::Call call(f, callNode, retType); 1.4002 + if (!CheckCallArgs(f, callNode, CheckIsExternType, &call)) 1.4003 + return false; 1.4004 + 1.4005 + unsigned exitIndex; 1.4006 + if (!f.m().addExit(ffiIndex, calleeName, Move(call.sig()), &exitIndex)) 1.4007 + return false; 1.4008 + 1.4009 + if (!f.ffiCall(exitIndex, call, retType.toMIRType(), def)) 1.4010 + return false; 1.4011 + 1.4012 + *type = retType.toType(); 1.4013 + return true; 1.4014 +} 1.4015 + 1.4016 +static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type); 1.4017 + 1.4018 +static bool 1.4019 +CheckFRoundArg(FunctionCompiler &f, ParseNode *arg, MDefinition **def, Type *type) 1.4020 +{ 1.4021 + if (arg->isKind(PNK_CALL)) 1.4022 + return CheckCall(f, arg, RetType::Float, def, type); 1.4023 + 1.4024 + MDefinition *inputDef; 1.4025 + Type inputType; 1.4026 + if (!CheckExpr(f, arg, &inputDef, &inputType)) 1.4027 + return false; 1.4028 + 1.4029 + if (inputType.isMaybeDouble() || inputType.isSigned()) 1.4030 + *def = f.unary<MToFloat32>(inputDef); 1.4031 + else if (inputType.isUnsigned()) 1.4032 + *def = f.unary<MAsmJSUnsignedToFloat32>(inputDef); 1.4033 + else if (inputType.isFloatish()) 1.4034 + *def = inputDef; 1.4035 + else 1.4036 + return f.failf(arg, "%s is not a subtype of signed, unsigned, double? or floatish", inputType.toChars()); 1.4037 + 1.4038 + *type = Type::Float; 1.4039 + return true; 1.4040 +} 1.4041 + 1.4042 +static bool 1.4043 +CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type) 1.4044 +{ 1.4045 + ParseNode *argNode = nullptr; 1.4046 + if (!IsFloatCoercion(f.m(), callNode, &argNode)) 1.4047 + return f.fail(callNode, "invalid call to fround"); 1.4048 + 1.4049 + MDefinition *operand; 1.4050 + Type operandType; 1.4051 + if (!CheckFRoundArg(f, argNode, &operand, &operandType)) 1.4052 + return false; 1.4053 + 1.4054 + JS_ASSERT(operandType == Type::Float); 1.4055 + 1.4056 + switch (retType.which()) { 1.4057 + case RetType::Double: 1.4058 + *def = f.unary<MToDouble>(operand); 1.4059 + *type = Type::Double; 1.4060 + return true; 1.4061 + case RetType::Signed: 1.4062 + *def = f.unary<MTruncateToInt32>(operand); 1.4063 + *type = Type::Signed; 1.4064 + return true; 1.4065 + case RetType::Float: 1.4066 + *def = operand; 1.4067 + *type = Type::Float; 1.4068 + return true; 1.4069 + case RetType::Void: 1.4070 + // definition and return types should be ignored by the caller 1.4071 + return true; 1.4072 + } 1.4073 + 1.4074 + MOZ_ASSUME_UNREACHABLE("return value of fround is ignored"); 1.4075 +} 1.4076 + 1.4077 +static bool 1.4078 +CheckIsMaybeDouble(FunctionCompiler &f, ParseNode *argNode, Type type) 1.4079 +{ 1.4080 + if (!type.isMaybeDouble()) 1.4081 + return f.failf(argNode, "%s is not a subtype of double?", type.toChars()); 1.4082 + return true; 1.4083 +} 1.4084 + 1.4085 +static bool 1.4086 +CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type) 1.4087 +{ 1.4088 + if (!type.isMaybeFloat()) 1.4089 + return f.failf(argNode, "%s is not a subtype of float?", type.toChars()); 1.4090 + return true; 1.4091 +} 1.4092 + 1.4093 +static bool 1.4094 +CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func, 1.4095 + RetType retType, MDefinition **def, Type *type) 1.4096 +{ 1.4097 + unsigned arity = 0; 1.4098 + AsmJSImmKind doubleCallee, floatCallee; 1.4099 + switch (func) { 1.4100 + case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type); 1.4101 + case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type); 1.4102 + case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type); 1.4103 + case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, retType, def, type); 1.4104 + case AsmJSMathBuiltin_min: return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ false); 1.4105 + case AsmJSMathBuiltin_max: return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ true); 1.4106 + case AsmJSMathBuiltin_ceil: arity = 1; doubleCallee = AsmJSImm_CeilD; floatCallee = AsmJSImm_CeilF; break; 1.4107 + case AsmJSMathBuiltin_floor: arity = 1; doubleCallee = AsmJSImm_FloorD; floatCallee = AsmJSImm_FloorF; break; 1.4108 + case AsmJSMathBuiltin_sin: arity = 1; doubleCallee = AsmJSImm_SinD; floatCallee = AsmJSImm_Invalid; break; 1.4109 + case AsmJSMathBuiltin_cos: arity = 1; doubleCallee = AsmJSImm_CosD; floatCallee = AsmJSImm_Invalid; break; 1.4110 + case AsmJSMathBuiltin_tan: arity = 1; doubleCallee = AsmJSImm_TanD; floatCallee = AsmJSImm_Invalid; break; 1.4111 + case AsmJSMathBuiltin_asin: arity = 1; doubleCallee = AsmJSImm_ASinD; floatCallee = AsmJSImm_Invalid; break; 1.4112 + case AsmJSMathBuiltin_acos: arity = 1; doubleCallee = AsmJSImm_ACosD; floatCallee = AsmJSImm_Invalid; break; 1.4113 + case AsmJSMathBuiltin_atan: arity = 1; doubleCallee = AsmJSImm_ATanD; floatCallee = AsmJSImm_Invalid; break; 1.4114 + case AsmJSMathBuiltin_exp: arity = 1; doubleCallee = AsmJSImm_ExpD; floatCallee = AsmJSImm_Invalid; break; 1.4115 + case AsmJSMathBuiltin_log: arity = 1; doubleCallee = AsmJSImm_LogD; floatCallee = AsmJSImm_Invalid; break; 1.4116 + case AsmJSMathBuiltin_pow: arity = 2; doubleCallee = AsmJSImm_PowD; floatCallee = AsmJSImm_Invalid; break; 1.4117 + case AsmJSMathBuiltin_atan2: arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break; 1.4118 + default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin function"); 1.4119 + } 1.4120 + 1.4121 + if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid) 1.4122 + return f.fail(callNode, "math builtin cannot be used as float"); 1.4123 + if (retType != RetType::Double && retType != RetType::Float) 1.4124 + return f.failf(callNode, "return type of math function is double or float, used as %s", retType.toType().toChars()); 1.4125 + 1.4126 + FunctionCompiler::Call call(f, callNode, retType); 1.4127 + if (retType == RetType::Float && !CheckCallArgs(f, callNode, CheckIsMaybeFloat, &call)) 1.4128 + return false; 1.4129 + if (retType == RetType::Double && !CheckCallArgs(f, callNode, CheckIsMaybeDouble, &call)) 1.4130 + return false; 1.4131 + 1.4132 + if (call.sig().args().length() != arity) 1.4133 + return f.failf(callNode, "call passed %u arguments, expected %u", call.sig().args().length(), arity); 1.4134 + 1.4135 + if (retType == RetType::Float && !f.builtinCall(floatCallee, call, retType.toMIRType(), def)) 1.4136 + return false; 1.4137 + if (retType == RetType::Double && !f.builtinCall(doubleCallee, call, retType.toMIRType(), def)) 1.4138 + return false; 1.4139 + 1.4140 + *type = retType.toType(); 1.4141 + return true; 1.4142 +} 1.4143 + 1.4144 +static bool 1.4145 +CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type) 1.4146 +{ 1.4147 + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); 1.4148 + 1.4149 + ParseNode *callee = CallCallee(call); 1.4150 + 1.4151 + if (callee->isKind(PNK_ELEM)) 1.4152 + return CheckFuncPtrCall(f, call, retType, def, type); 1.4153 + 1.4154 + if (!callee->isKind(PNK_NAME)) 1.4155 + return f.fail(callee, "unexpected callee expression type"); 1.4156 + 1.4157 + PropertyName *calleeName = callee->name(); 1.4158 + 1.4159 + if (const ModuleCompiler::Global *global = f.lookupGlobal(calleeName)) { 1.4160 + switch (global->which()) { 1.4161 + case ModuleCompiler::Global::FFI: 1.4162 + return CheckFFICall(f, call, global->ffiIndex(), retType, def, type); 1.4163 + case ModuleCompiler::Global::MathBuiltinFunction: 1.4164 + return CheckMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type); 1.4165 + case ModuleCompiler::Global::ConstantLiteral: 1.4166 + case ModuleCompiler::Global::ConstantImport: 1.4167 + case ModuleCompiler::Global::Variable: 1.4168 + case ModuleCompiler::Global::FuncPtrTable: 1.4169 + case ModuleCompiler::Global::ArrayView: 1.4170 + return f.failName(callee, "'%s' is not callable function", callee->name()); 1.4171 + case ModuleCompiler::Global::Function: 1.4172 + break; 1.4173 + } 1.4174 + } 1.4175 + 1.4176 + return CheckInternalCall(f, call, calleeName, retType, def, type); 1.4177 +} 1.4178 + 1.4179 +static bool 1.4180 +CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type) 1.4181 +{ 1.4182 + JS_ASSERT(pos->isKind(PNK_POS)); 1.4183 + ParseNode *operand = UnaryKid(pos); 1.4184 + 1.4185 + if (operand->isKind(PNK_CALL)) 1.4186 + return CheckCall(f, operand, RetType::Double, def, type); 1.4187 + 1.4188 + MDefinition *operandDef; 1.4189 + Type operandType; 1.4190 + if (!CheckExpr(f, operand, &operandDef, &operandType)) 1.4191 + return false; 1.4192 + 1.4193 + if (operandType.isMaybeFloat() || operandType.isSigned()) 1.4194 + *def = f.unary<MToDouble>(operandDef); 1.4195 + else if (operandType.isUnsigned()) 1.4196 + *def = f.unary<MAsmJSUnsignedToDouble>(operandDef); 1.4197 + else if (operandType.isMaybeDouble()) 1.4198 + *def = operandDef; 1.4199 + else 1.4200 + return f.failf(operand, "%s is not a subtype of signed, unsigned, float or double?", operandType.toChars()); 1.4201 + 1.4202 + *type = Type::Double; 1.4203 + return true; 1.4204 +} 1.4205 + 1.4206 +static bool 1.4207 +CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4208 +{ 1.4209 + JS_ASSERT(expr->isKind(PNK_NOT)); 1.4210 + ParseNode *operand = UnaryKid(expr); 1.4211 + 1.4212 + MDefinition *operandDef; 1.4213 + Type operandType; 1.4214 + if (!CheckExpr(f, operand, &operandDef, &operandType)) 1.4215 + return false; 1.4216 + 1.4217 + if (!operandType.isInt()) 1.4218 + return f.failf(operand, "%s is not a subtype of int", operandType.toChars()); 1.4219 + 1.4220 + *def = f.unary<MNot>(operandDef); 1.4221 + *type = Type::Int; 1.4222 + return true; 1.4223 +} 1.4224 + 1.4225 +static bool 1.4226 +CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4227 +{ 1.4228 + JS_ASSERT(expr->isKind(PNK_NEG)); 1.4229 + ParseNode *operand = UnaryKid(expr); 1.4230 + 1.4231 + MDefinition *operandDef; 1.4232 + Type operandType; 1.4233 + if (!CheckExpr(f, operand, &operandDef, &operandType)) 1.4234 + return false; 1.4235 + 1.4236 + if (operandType.isInt()) { 1.4237 + *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Int32); 1.4238 + *type = Type::Intish; 1.4239 + return true; 1.4240 + } 1.4241 + 1.4242 + if (operandType.isMaybeDouble()) { 1.4243 + *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double); 1.4244 + *type = Type::Double; 1.4245 + return true; 1.4246 + } 1.4247 + 1.4248 + if (operandType.isMaybeFloat()) { 1.4249 + *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Float32); 1.4250 + *type = Type::Floatish; 1.4251 + return true; 1.4252 + } 1.4253 + 1.4254 + return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars()); 1.4255 +} 1.4256 + 1.4257 +static bool 1.4258 +CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4259 +{ 1.4260 + JS_ASSERT(expr->isKind(PNK_BITNOT)); 1.4261 + ParseNode *operand = UnaryKid(expr); 1.4262 + 1.4263 + MDefinition *operandDef; 1.4264 + Type operandType; 1.4265 + if (!CheckExpr(f, operand, &operandDef, &operandType)) 1.4266 + return false; 1.4267 + 1.4268 + if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) { 1.4269 + *def = f.unary<MTruncateToInt32>(operandDef); 1.4270 + *type = Type::Signed; 1.4271 + return true; 1.4272 + } 1.4273 + 1.4274 + if (!operandType.isIntish()) 1.4275 + return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars()); 1.4276 + 1.4277 + *def = operandDef; 1.4278 + *type = Type::Signed; 1.4279 + return true; 1.4280 +} 1.4281 + 1.4282 +static bool 1.4283 +CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type) 1.4284 +{ 1.4285 + JS_ASSERT(neg->isKind(PNK_BITNOT)); 1.4286 + ParseNode *operand = UnaryKid(neg); 1.4287 + 1.4288 + if (operand->isKind(PNK_BITNOT)) 1.4289 + return CheckCoerceToInt(f, operand, def, type); 1.4290 + 1.4291 + MDefinition *operandDef; 1.4292 + Type operandType; 1.4293 + if (!CheckExpr(f, operand, &operandDef, &operandType)) 1.4294 + return false; 1.4295 + 1.4296 + if (!operandType.isIntish()) 1.4297 + return f.failf(operand, "%s is not a subtype of intish", operandType.toChars()); 1.4298 + 1.4299 + *def = f.bitwise<MBitNot>(operandDef); 1.4300 + *type = Type::Signed; 1.4301 + return true; 1.4302 +} 1.4303 + 1.4304 +static bool 1.4305 +CheckComma(FunctionCompiler &f, ParseNode *comma, MDefinition **def, Type *type) 1.4306 +{ 1.4307 + JS_ASSERT(comma->isKind(PNK_COMMA)); 1.4308 + ParseNode *operands = ListHead(comma); 1.4309 + 1.4310 + ParseNode *pn = operands; 1.4311 + for (; NextNode(pn); pn = NextNode(pn)) { 1.4312 + MDefinition *_1; 1.4313 + Type _2; 1.4314 + if (pn->isKind(PNK_CALL)) { 1.4315 + if (!CheckCall(f, pn, RetType::Void, &_1, &_2)) 1.4316 + return false; 1.4317 + } else { 1.4318 + if (!CheckExpr(f, pn, &_1, &_2)) 1.4319 + return false; 1.4320 + } 1.4321 + } 1.4322 + 1.4323 + if (!CheckExpr(f, pn, def, type)) 1.4324 + return false; 1.4325 + 1.4326 + return true; 1.4327 +} 1.4328 + 1.4329 +static bool 1.4330 +CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Type *type) 1.4331 +{ 1.4332 + JS_ASSERT(ternary->isKind(PNK_CONDITIONAL)); 1.4333 + ParseNode *cond = TernaryKid1(ternary); 1.4334 + ParseNode *thenExpr = TernaryKid2(ternary); 1.4335 + ParseNode *elseExpr = TernaryKid3(ternary); 1.4336 + 1.4337 + MDefinition *condDef; 1.4338 + Type condType; 1.4339 + if (!CheckExpr(f, cond, &condDef, &condType)) 1.4340 + return false; 1.4341 + 1.4342 + if (!condType.isInt()) 1.4343 + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); 1.4344 + 1.4345 + MBasicBlock *thenBlock = nullptr, *elseBlock = nullptr; 1.4346 + if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock, thenExpr, elseExpr)) 1.4347 + return false; 1.4348 + 1.4349 + MDefinition *thenDef; 1.4350 + Type thenType; 1.4351 + if (!CheckExpr(f, thenExpr, &thenDef, &thenType)) 1.4352 + return false; 1.4353 + 1.4354 + BlockVector thenBlocks(f.cx()); 1.4355 + if (!f.appendThenBlock(&thenBlocks)) 1.4356 + return false; 1.4357 + 1.4358 + f.pushPhiInput(thenDef); 1.4359 + f.switchToElse(elseBlock); 1.4360 + 1.4361 + MDefinition *elseDef; 1.4362 + Type elseType; 1.4363 + if (!CheckExpr(f, elseExpr, &elseDef, &elseType)) 1.4364 + return false; 1.4365 + 1.4366 + f.pushPhiInput(elseDef); 1.4367 + 1.4368 + if (thenType.isInt() && elseType.isInt()) { 1.4369 + *type = Type::Int; 1.4370 + } else if (thenType.isDouble() && elseType.isDouble()) { 1.4371 + *type = Type::Double; 1.4372 + } else if (thenType.isFloat() && elseType.isFloat()) { 1.4373 + *type = Type::Float; 1.4374 + } else { 1.4375 + return f.failf(ternary, "then/else branches of conditional must both produce int or double, " 1.4376 + "current types are %s and %s", thenType.toChars(), elseType.toChars()); 1.4377 + } 1.4378 + 1.4379 + if (!f.joinIfElse(thenBlocks, elseExpr)) 1.4380 + return false; 1.4381 + 1.4382 + *def = f.popPhiOutput(); 1.4383 + return true; 1.4384 +} 1.4385 + 1.4386 +static bool 1.4387 +IsValidIntMultiplyConstant(ModuleCompiler &m, ParseNode *expr) 1.4388 +{ 1.4389 + if (!IsNumericLiteral(m, expr)) 1.4390 + return false; 1.4391 + 1.4392 + NumLit literal = ExtractNumericLiteral(m, expr); 1.4393 + switch (literal.which()) { 1.4394 + case NumLit::Fixnum: 1.4395 + case NumLit::NegativeInt: 1.4396 + if (abs(literal.toInt32()) < (1<<20)) 1.4397 + return true; 1.4398 + return false; 1.4399 + case NumLit::BigUnsigned: 1.4400 + case NumLit::Double: 1.4401 + case NumLit::Float: 1.4402 + case NumLit::OutOfRangeInt: 1.4403 + return false; 1.4404 + } 1.4405 + 1.4406 + MOZ_ASSUME_UNREACHABLE("Bad literal"); 1.4407 +} 1.4408 + 1.4409 +static bool 1.4410 +CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type) 1.4411 +{ 1.4412 + JS_ASSERT(star->isKind(PNK_STAR)); 1.4413 + ParseNode *lhs = BinaryLeft(star); 1.4414 + ParseNode *rhs = BinaryRight(star); 1.4415 + 1.4416 + MDefinition *lhsDef; 1.4417 + Type lhsType; 1.4418 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.4419 + return false; 1.4420 + 1.4421 + MDefinition *rhsDef; 1.4422 + Type rhsType; 1.4423 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.4424 + return false; 1.4425 + 1.4426 + if (lhsType.isInt() && rhsType.isInt()) { 1.4427 + if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs)) 1.4428 + return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal"); 1.4429 + *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); 1.4430 + *type = Type::Intish; 1.4431 + return true; 1.4432 + } 1.4433 + 1.4434 + if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { 1.4435 + *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); 1.4436 + *type = Type::Double; 1.4437 + return true; 1.4438 + } 1.4439 + 1.4440 + if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { 1.4441 + *def = f.mul(lhsDef, rhsDef, MIRType_Float32, MMul::Normal); 1.4442 + *type = Type::Floatish; 1.4443 + return true; 1.4444 + } 1.4445 + 1.4446 + return f.fail(star, "multiply operands must be both int, both double? or both float?"); 1.4447 +} 1.4448 + 1.4449 +static bool 1.4450 +CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, 1.4451 + unsigned *numAddOrSubOut = nullptr) 1.4452 +{ 1.4453 + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); 1.4454 + 1.4455 + JS_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB)); 1.4456 + ParseNode *lhs = BinaryLeft(expr); 1.4457 + ParseNode *rhs = BinaryRight(expr); 1.4458 + 1.4459 + MDefinition *lhsDef, *rhsDef; 1.4460 + Type lhsType, rhsType; 1.4461 + unsigned lhsNumAddOrSub, rhsNumAddOrSub; 1.4462 + 1.4463 + if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) { 1.4464 + if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub)) 1.4465 + return false; 1.4466 + if (lhsType == Type::Intish) 1.4467 + lhsType = Type::Int; 1.4468 + } else { 1.4469 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.4470 + return false; 1.4471 + lhsNumAddOrSub = 0; 1.4472 + } 1.4473 + 1.4474 + if (rhs->isKind(PNK_ADD) || rhs->isKind(PNK_SUB)) { 1.4475 + if (!CheckAddOrSub(f, rhs, &rhsDef, &rhsType, &rhsNumAddOrSub)) 1.4476 + return false; 1.4477 + if (rhsType == Type::Intish) 1.4478 + rhsType = Type::Int; 1.4479 + } else { 1.4480 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.4481 + return false; 1.4482 + rhsNumAddOrSub = 0; 1.4483 + } 1.4484 + 1.4485 + unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1; 1.4486 + if (numAddOrSub > (1<<20)) 1.4487 + return f.fail(expr, "too many + or - without intervening coercion"); 1.4488 + 1.4489 + if (lhsType.isInt() && rhsType.isInt()) { 1.4490 + *def = expr->isKind(PNK_ADD) 1.4491 + ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32) 1.4492 + : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32); 1.4493 + *type = Type::Intish; 1.4494 + } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { 1.4495 + *def = expr->isKind(PNK_ADD) 1.4496 + ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double) 1.4497 + : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double); 1.4498 + *type = Type::Double; 1.4499 + } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { 1.4500 + *def = expr->isKind(PNK_ADD) 1.4501 + ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Float32) 1.4502 + : f.binary<MSub>(lhsDef, rhsDef, MIRType_Float32); 1.4503 + *type = Type::Floatish; 1.4504 + } else { 1.4505 + return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s", 1.4506 + lhsType.toChars(), rhsType.toChars()); 1.4507 + } 1.4508 + 1.4509 + if (numAddOrSubOut) 1.4510 + *numAddOrSubOut = numAddOrSub; 1.4511 + return true; 1.4512 +} 1.4513 + 1.4514 +static bool 1.4515 +CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4516 +{ 1.4517 + JS_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD)); 1.4518 + ParseNode *lhs = BinaryLeft(expr); 1.4519 + ParseNode *rhs = BinaryRight(expr); 1.4520 + 1.4521 + MDefinition *lhsDef, *rhsDef; 1.4522 + Type lhsType, rhsType; 1.4523 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.4524 + return false; 1.4525 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.4526 + return false; 1.4527 + 1.4528 + if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { 1.4529 + *def = expr->isKind(PNK_DIV) 1.4530 + ? f.div(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false) 1.4531 + : f.mod(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false); 1.4532 + *type = Type::Double; 1.4533 + return true; 1.4534 + } 1.4535 + 1.4536 + if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { 1.4537 + if (expr->isKind(PNK_DIV)) 1.4538 + *def = f.div(lhsDef, rhsDef, MIRType_Float32, /* unsignd = */ false); 1.4539 + else 1.4540 + return f.fail(expr, "modulo cannot receive float arguments"); 1.4541 + *type = Type::Floatish; 1.4542 + return true; 1.4543 + } 1.4544 + 1.4545 + if (lhsType.isSigned() && rhsType.isSigned()) { 1.4546 + if (expr->isKind(PNK_DIV)) 1.4547 + *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false); 1.4548 + else 1.4549 + *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false); 1.4550 + *type = Type::Intish; 1.4551 + return true; 1.4552 + } 1.4553 + 1.4554 + if (lhsType.isUnsigned() && rhsType.isUnsigned()) { 1.4555 + if (expr->isKind(PNK_DIV)) 1.4556 + *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true); 1.4557 + else 1.4558 + *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true); 1.4559 + *type = Type::Intish; 1.4560 + return true; 1.4561 + } 1.4562 + 1.4563 + return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; " 1.4564 + "%s and %s are given", lhsType.toChars(), rhsType.toChars()); 1.4565 +} 1.4566 + 1.4567 +static bool 1.4568 +CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type) 1.4569 +{ 1.4570 + JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) || 1.4571 + comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); 1.4572 + ParseNode *lhs = BinaryLeft(comp); 1.4573 + ParseNode *rhs = BinaryRight(comp); 1.4574 + 1.4575 + MDefinition *lhsDef, *rhsDef; 1.4576 + Type lhsType, rhsType; 1.4577 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.4578 + return false; 1.4579 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.4580 + return false; 1.4581 + 1.4582 + if ((lhsType.isSigned() && rhsType.isSigned()) || (lhsType.isUnsigned() && rhsType.isUnsigned())) { 1.4583 + MCompare::CompareType compareType = (lhsType.isUnsigned() && rhsType.isUnsigned()) 1.4584 + ? MCompare::Compare_UInt32 1.4585 + : MCompare::Compare_Int32; 1.4586 + *def = f.compare(lhsDef, rhsDef, comp->getOp(), compareType); 1.4587 + *type = Type::Int; 1.4588 + return true; 1.4589 + } 1.4590 + 1.4591 + if (lhsType.isDouble() && rhsType.isDouble()) { 1.4592 + *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double); 1.4593 + *type = Type::Int; 1.4594 + return true; 1.4595 + } 1.4596 + 1.4597 + if (lhsType.isFloat() && rhsType.isFloat()) { 1.4598 + *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Float32); 1.4599 + *type = Type::Int; 1.4600 + return true; 1.4601 + } 1.4602 + 1.4603 + return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; " 1.4604 + "%s and %s are given", lhsType.toChars(), rhsType.toChars()); 1.4605 +} 1.4606 + 1.4607 +static bool 1.4608 +CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type) 1.4609 +{ 1.4610 + ParseNode *lhs = BinaryLeft(bitwise); 1.4611 + ParseNode *rhs = BinaryRight(bitwise); 1.4612 + 1.4613 + int32_t identityElement; 1.4614 + bool onlyOnRight; 1.4615 + switch (bitwise->getKind()) { 1.4616 + case PNK_BITOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; 1.4617 + case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break; 1.4618 + case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; 1.4619 + case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break; 1.4620 + case PNK_RSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break; 1.4621 + case PNK_URSH: identityElement = 0; onlyOnRight = true; *type = Type::Unsigned; break; 1.4622 + default: MOZ_ASSUME_UNREACHABLE("not a bitwise op"); 1.4623 + } 1.4624 + 1.4625 + uint32_t i; 1.4626 + if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) && i == uint32_t(identityElement)) { 1.4627 + Type rhsType; 1.4628 + if (!CheckExpr(f, rhs, def, &rhsType)) 1.4629 + return false; 1.4630 + if (!rhsType.isIntish()) 1.4631 + return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars()); 1.4632 + return true; 1.4633 + } 1.4634 + 1.4635 + if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) { 1.4636 + if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL)) 1.4637 + return CheckCall(f, lhs, RetType::Signed, def, type); 1.4638 + 1.4639 + Type lhsType; 1.4640 + if (!CheckExpr(f, lhs, def, &lhsType)) 1.4641 + return false; 1.4642 + if (!lhsType.isIntish()) 1.4643 + return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars()); 1.4644 + return true; 1.4645 + } 1.4646 + 1.4647 + MDefinition *lhsDef; 1.4648 + Type lhsType; 1.4649 + if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) 1.4650 + return false; 1.4651 + 1.4652 + MDefinition *rhsDef; 1.4653 + Type rhsType; 1.4654 + if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) 1.4655 + return false; 1.4656 + 1.4657 + if (!lhsType.isIntish()) 1.4658 + return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars()); 1.4659 + if (!rhsType.isIntish()) 1.4660 + return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars()); 1.4661 + 1.4662 + switch (bitwise->getKind()) { 1.4663 + case PNK_BITOR: *def = f.bitwise<MBitOr>(lhsDef, rhsDef); break; 1.4664 + case PNK_BITAND: *def = f.bitwise<MBitAnd>(lhsDef, rhsDef); break; 1.4665 + case PNK_BITXOR: *def = f.bitwise<MBitXor>(lhsDef, rhsDef); break; 1.4666 + case PNK_LSH: *def = f.bitwise<MLsh>(lhsDef, rhsDef); break; 1.4667 + case PNK_RSH: *def = f.bitwise<MRsh>(lhsDef, rhsDef); break; 1.4668 + case PNK_URSH: *def = f.bitwise<MUrsh>(lhsDef, rhsDef); break; 1.4669 + default: MOZ_ASSUME_UNREACHABLE("not a bitwise op"); 1.4670 + } 1.4671 + 1.4672 + return true; 1.4673 +} 1.4674 + 1.4675 +static bool 1.4676 +CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4677 +{ 1.4678 + JS_ASSERT(expr->isKind(PNK_CALL)); 1.4679 + 1.4680 + ParseNode *arg; 1.4681 + if (!IsFloatCoercion(f.m(), expr, &arg)) { 1.4682 + return f.fail(expr, "all function calls must either be ignored (via f(); or " 1.4683 + "comma-expression), coerced to signed (via f()|0), coerced to float " 1.4684 + "(via fround(f())) or coerced to double (via +f())"); 1.4685 + } 1.4686 + 1.4687 + return CheckFRoundArg(f, arg, def, type); 1.4688 +} 1.4689 + 1.4690 +static bool 1.4691 +CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) 1.4692 +{ 1.4693 + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); 1.4694 + 1.4695 + if (!f.mirGen().ensureBallast()) 1.4696 + return false; 1.4697 + 1.4698 + if (IsNumericLiteral(f.m(), expr)) 1.4699 + return CheckNumericLiteral(f, expr, def, type); 1.4700 + 1.4701 + switch (expr->getKind()) { 1.4702 + case PNK_NAME: return CheckVarRef(f, expr, def, type); 1.4703 + case PNK_ELEM: return CheckLoadArray(f, expr, def, type); 1.4704 + case PNK_ASSIGN: return CheckAssign(f, expr, def, type); 1.4705 + case PNK_POS: return CheckPos(f, expr, def, type); 1.4706 + case PNK_NOT: return CheckNot(f, expr, def, type); 1.4707 + case PNK_NEG: return CheckNeg(f, expr, def, type); 1.4708 + case PNK_BITNOT: return CheckBitNot(f, expr, def, type); 1.4709 + case PNK_COMMA: return CheckComma(f, expr, def, type); 1.4710 + case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type); 1.4711 + case PNK_STAR: return CheckMultiply(f, expr, def, type); 1.4712 + case PNK_CALL: return CheckUncoercedCall(f, expr, def, type); 1.4713 + 1.4714 + case PNK_ADD: 1.4715 + case PNK_SUB: return CheckAddOrSub(f, expr, def, type); 1.4716 + 1.4717 + case PNK_DIV: 1.4718 + case PNK_MOD: return CheckDivOrMod(f, expr, def, type); 1.4719 + 1.4720 + case PNK_LT: 1.4721 + case PNK_LE: 1.4722 + case PNK_GT: 1.4723 + case PNK_GE: 1.4724 + case PNK_EQ: 1.4725 + case PNK_NE: return CheckComparison(f, expr, def, type); 1.4726 + 1.4727 + case PNK_BITOR: 1.4728 + case PNK_BITAND: 1.4729 + case PNK_BITXOR: 1.4730 + case PNK_LSH: 1.4731 + case PNK_RSH: 1.4732 + case PNK_URSH: return CheckBitwise(f, expr, def, type); 1.4733 + 1.4734 + default:; 1.4735 + } 1.4736 + 1.4737 + return f.fail(expr, "unsupported expression"); 1.4738 +} 1.4739 + 1.4740 +static bool 1.4741 +CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels = nullptr); 1.4742 + 1.4743 +static bool 1.4744 +CheckExprStatement(FunctionCompiler &f, ParseNode *exprStmt) 1.4745 +{ 1.4746 + JS_ASSERT(exprStmt->isKind(PNK_SEMI)); 1.4747 + ParseNode *expr = UnaryKid(exprStmt); 1.4748 + 1.4749 + if (!expr) 1.4750 + return true; 1.4751 + 1.4752 + MDefinition *_1; 1.4753 + Type _2; 1.4754 + 1.4755 + if (expr->isKind(PNK_CALL)) 1.4756 + return CheckCall(f, expr, RetType::Void, &_1, &_2); 1.4757 + 1.4758 + return CheckExpr(f, UnaryKid(exprStmt), &_1, &_2); 1.4759 +} 1.4760 + 1.4761 +static bool 1.4762 +CheckWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels) 1.4763 +{ 1.4764 + JS_ASSERT(whileStmt->isKind(PNK_WHILE)); 1.4765 + ParseNode *cond = BinaryLeft(whileStmt); 1.4766 + ParseNode *body = BinaryRight(whileStmt); 1.4767 + 1.4768 + MBasicBlock *loopEntry; 1.4769 + if (!f.startPendingLoop(whileStmt, &loopEntry, body)) 1.4770 + return false; 1.4771 + 1.4772 + MDefinition *condDef; 1.4773 + Type condType; 1.4774 + if (!CheckExpr(f, cond, &condDef, &condType)) 1.4775 + return false; 1.4776 + 1.4777 + if (!condType.isInt()) 1.4778 + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); 1.4779 + 1.4780 + MBasicBlock *afterLoop; 1.4781 + if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(whileStmt))) 1.4782 + return false; 1.4783 + 1.4784 + if (!CheckStatement(f, body)) 1.4785 + return false; 1.4786 + 1.4787 + if (!f.bindContinues(whileStmt, maybeLabels)) 1.4788 + return false; 1.4789 + 1.4790 + return f.closeLoop(loopEntry, afterLoop); 1.4791 +} 1.4792 + 1.4793 +static bool 1.4794 +CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels) 1.4795 +{ 1.4796 + JS_ASSERT(forStmt->isKind(PNK_FOR)); 1.4797 + ParseNode *forHead = BinaryLeft(forStmt); 1.4798 + ParseNode *body = BinaryRight(forStmt); 1.4799 + 1.4800 + if (!forHead->isKind(PNK_FORHEAD)) 1.4801 + return f.fail(forHead, "unsupported for-loop statement"); 1.4802 + 1.4803 + ParseNode *maybeInit = TernaryKid1(forHead); 1.4804 + ParseNode *maybeCond = TernaryKid2(forHead); 1.4805 + ParseNode *maybeInc = TernaryKid3(forHead); 1.4806 + 1.4807 + if (maybeInit) { 1.4808 + MDefinition *_1; 1.4809 + Type _2; 1.4810 + if (!CheckExpr(f, maybeInit, &_1, &_2)) 1.4811 + return false; 1.4812 + } 1.4813 + 1.4814 + MBasicBlock *loopEntry; 1.4815 + if (!f.startPendingLoop(forStmt, &loopEntry, body)) 1.4816 + return false; 1.4817 + 1.4818 + MDefinition *condDef; 1.4819 + if (maybeCond) { 1.4820 + Type condType; 1.4821 + if (!CheckExpr(f, maybeCond, &condDef, &condType)) 1.4822 + return false; 1.4823 + 1.4824 + if (!condType.isInt()) 1.4825 + return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars()); 1.4826 + } else { 1.4827 + condDef = f.constant(Int32Value(1), Type::Int); 1.4828 + } 1.4829 + 1.4830 + MBasicBlock *afterLoop; 1.4831 + if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(forStmt))) 1.4832 + return false; 1.4833 + 1.4834 + if (!CheckStatement(f, body)) 1.4835 + return false; 1.4836 + 1.4837 + if (!f.bindContinues(forStmt, maybeLabels)) 1.4838 + return false; 1.4839 + 1.4840 + if (maybeInc) { 1.4841 + MDefinition *_1; 1.4842 + Type _2; 1.4843 + if (!CheckExpr(f, maybeInc, &_1, &_2)) 1.4844 + return false; 1.4845 + } 1.4846 + 1.4847 + return f.closeLoop(loopEntry, afterLoop); 1.4848 +} 1.4849 + 1.4850 +static bool 1.4851 +CheckDoWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels) 1.4852 +{ 1.4853 + JS_ASSERT(whileStmt->isKind(PNK_DOWHILE)); 1.4854 + ParseNode *body = BinaryLeft(whileStmt); 1.4855 + ParseNode *cond = BinaryRight(whileStmt); 1.4856 + 1.4857 + MBasicBlock *loopEntry; 1.4858 + if (!f.startPendingLoop(whileStmt, &loopEntry, body)) 1.4859 + return false; 1.4860 + 1.4861 + if (!CheckStatement(f, body)) 1.4862 + return false; 1.4863 + 1.4864 + if (!f.bindContinues(whileStmt, maybeLabels)) 1.4865 + return false; 1.4866 + 1.4867 + MDefinition *condDef; 1.4868 + Type condType; 1.4869 + if (!CheckExpr(f, cond, &condDef, &condType)) 1.4870 + return false; 1.4871 + 1.4872 + if (!condType.isInt()) 1.4873 + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); 1.4874 + 1.4875 + return f.branchAndCloseDoWhileLoop(condDef, loopEntry, NextNode(whileStmt)); 1.4876 +} 1.4877 + 1.4878 +static bool 1.4879 +CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels) 1.4880 +{ 1.4881 + JS_ASSERT(labeledStmt->isKind(PNK_LABEL)); 1.4882 + PropertyName *label = LabeledStatementLabel(labeledStmt); 1.4883 + ParseNode *stmt = LabeledStatementStatement(labeledStmt); 1.4884 + 1.4885 + if (maybeLabels) { 1.4886 + if (!maybeLabels->append(label)) 1.4887 + return false; 1.4888 + if (!CheckStatement(f, stmt, maybeLabels)) 1.4889 + return false; 1.4890 + return true; 1.4891 + } 1.4892 + 1.4893 + LabelVector labels(f.cx()); 1.4894 + if (!labels.append(label)) 1.4895 + return false; 1.4896 + 1.4897 + if (!CheckStatement(f, stmt, &labels)) 1.4898 + return false; 1.4899 + 1.4900 + return f.bindLabeledBreaks(&labels, labeledStmt); 1.4901 +} 1.4902 + 1.4903 +static bool 1.4904 +CheckLeafCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt, ParseNode *elseOrJoinStmt, 1.4905 + MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock) 1.4906 +{ 1.4907 + MDefinition *condDef; 1.4908 + Type condType; 1.4909 + if (!CheckExpr(f, cond, &condDef, &condType)) 1.4910 + return false; 1.4911 + if (!condType.isInt()) 1.4912 + return f.failf(cond, "%s is not a subtype of int", condType.toChars()); 1.4913 + 1.4914 + if (!f.branchAndStartThen(condDef, thenBlock, elseOrJoinBlock, thenStmt, elseOrJoinStmt)) 1.4915 + return false; 1.4916 + return true; 1.4917 +} 1.4918 + 1.4919 +static bool 1.4920 +CheckIfCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt, ParseNode *elseOrJoinStmt, 1.4921 + MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock); 1.4922 + 1.4923 +static bool 1.4924 +CheckIfConditional(FunctionCompiler &f, ParseNode *conditional, ParseNode *thenStmt, ParseNode *elseOrJoinStmt, 1.4925 + MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock) 1.4926 +{ 1.4927 + JS_ASSERT(conditional->isKind(PNK_CONDITIONAL)); 1.4928 + 1.4929 + // a ? b : c <=> (a && b) || (!a && c) 1.4930 + // b is always referred to the AND condition, as we need A and B to reach this test, 1.4931 + // c is always referred as the OR condition, as we reach it if we don't have A. 1.4932 + ParseNode *cond = TernaryKid1(conditional); 1.4933 + ParseNode *lhs = TernaryKid2(conditional); 1.4934 + ParseNode *rhs = TernaryKid3(conditional); 1.4935 + 1.4936 + MBasicBlock *maybeAndTest = nullptr, *maybeOrTest = nullptr; 1.4937 + MBasicBlock **ifTrueBlock = &maybeAndTest, **ifFalseBlock = &maybeOrTest; 1.4938 + ParseNode *ifTrueBlockNode = lhs, *ifFalseBlockNode = rhs; 1.4939 + 1.4940 + // Try to spot opportunities for short-circuiting in the AND subpart 1.4941 + uint32_t andTestLiteral = 0; 1.4942 + bool skipAndTest = false; 1.4943 + 1.4944 + if (IsLiteralInt(f.m(), lhs, &andTestLiteral)) { 1.4945 + skipAndTest = true; 1.4946 + if (andTestLiteral == 0) { 1.4947 + // (a ? 0 : b) is equivalent to !a && b 1.4948 + // If a is true, jump to the elseBlock directly 1.4949 + ifTrueBlock = elseOrJoinBlock; 1.4950 + ifTrueBlockNode = elseOrJoinStmt; 1.4951 + } else { 1.4952 + // (a ? 1 : b) is equivalent to a || b 1.4953 + // If a is true, jump to the thenBlock directly 1.4954 + ifTrueBlock = thenBlock; 1.4955 + ifTrueBlockNode = thenStmt; 1.4956 + } 1.4957 + } 1.4958 + 1.4959 + // Try to spot opportunities for short-circuiting in the OR subpart 1.4960 + uint32_t orTestLiteral = 0; 1.4961 + bool skipOrTest = false; 1.4962 + 1.4963 + if (IsLiteralInt(f.m(), rhs, &orTestLiteral)) { 1.4964 + skipOrTest = true; 1.4965 + if (orTestLiteral == 0) { 1.4966 + // (a ? b : 0) is equivalent to a && b 1.4967 + // If a is false, jump to the elseBlock directly 1.4968 + ifFalseBlock = elseOrJoinBlock; 1.4969 + ifFalseBlockNode = elseOrJoinStmt; 1.4970 + } else { 1.4971 + // (a ? b : 1) is equivalent to !a || b 1.4972 + // If a is false, jump to the thenBlock directly 1.4973 + ifFalseBlock = thenBlock; 1.4974 + ifFalseBlockNode = thenStmt; 1.4975 + } 1.4976 + } 1.4977 + 1.4978 + // Pathological cases: a ? 0 : 0 (i.e. false) or a ? 1 : 1 (i.e. true) 1.4979 + // These cases can't be optimized properly at this point: one of the blocks might be 1.4980 + // created and won't ever be executed. Furthermore, it introduces inconsistencies in the 1.4981 + // MIR graph (even if we try to create a block by hand, it will have no predecessor, which 1.4982 + // breaks graph assumptions). The only way we could optimize it is to do it directly in 1.4983 + // CheckIf by removing the control flow entirely. 1.4984 + if (skipOrTest && skipAndTest && (!!orTestLiteral == !!andTestLiteral)) 1.4985 + return CheckLeafCondition(f, conditional, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock); 1.4986 + 1.4987 + if (!CheckIfCondition(f, cond, ifTrueBlockNode, ifFalseBlockNode, ifTrueBlock, ifFalseBlock)) 1.4988 + return false; 1.4989 + f.assertCurrentBlockIs(*ifTrueBlock); 1.4990 + 1.4991 + // Add supplementary tests, if needed 1.4992 + if (!skipAndTest) { 1.4993 + if (!CheckIfCondition(f, lhs, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock)) 1.4994 + return false; 1.4995 + f.assertCurrentBlockIs(*thenBlock); 1.4996 + } 1.4997 + 1.4998 + if (!skipOrTest) { 1.4999 + f.switchToElse(*ifFalseBlock); 1.5000 + if (!CheckIfCondition(f, rhs, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock)) 1.5001 + return false; 1.5002 + f.assertCurrentBlockIs(*thenBlock); 1.5003 + } 1.5004 + 1.5005 + // We might not be on the thenBlock in one case 1.5006 + if (ifTrueBlock == elseOrJoinBlock) { 1.5007 + JS_ASSERT(skipAndTest && andTestLiteral == 0); 1.5008 + f.switchToElse(*thenBlock); 1.5009 + } 1.5010 + 1.5011 + // Check post-conditions 1.5012 + f.assertCurrentBlockIs(*thenBlock); 1.5013 + JS_ASSERT_IF(!f.inDeadCode(), *thenBlock && *elseOrJoinBlock); 1.5014 + return true; 1.5015 +} 1.5016 + 1.5017 +/* 1.5018 + * Recursive function that checks for a complex condition (formed with ternary 1.5019 + * conditionals) and creates the associated short-circuiting control flow graph. 1.5020 + * 1.5021 + * After a call to CheckCondition, the followings are true: 1.5022 + * - if *thenBlock and *elseOrJoinBlock were non-null on entry, their value is 1.5023 + * not changed by this function. 1.5024 + * - *thenBlock and *elseOrJoinBlock are non-null on exit. 1.5025 + * - the current block on exit is the *thenBlock. 1.5026 + */ 1.5027 +static bool 1.5028 +CheckIfCondition(FunctionCompiler &f, ParseNode *cond, ParseNode *thenStmt, 1.5029 + ParseNode *elseOrJoinStmt, MBasicBlock **thenBlock, MBasicBlock **elseOrJoinBlock) 1.5030 +{ 1.5031 + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); 1.5032 + 1.5033 + if (cond->isKind(PNK_CONDITIONAL)) 1.5034 + return CheckIfConditional(f, cond, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock); 1.5035 + 1.5036 + // We've reached a leaf, i.e. an atomic condition 1.5037 + JS_ASSERT(!cond->isKind(PNK_CONDITIONAL)); 1.5038 + if (!CheckLeafCondition(f, cond, thenStmt, elseOrJoinStmt, thenBlock, elseOrJoinBlock)) 1.5039 + return false; 1.5040 + 1.5041 + // Check post-conditions 1.5042 + f.assertCurrentBlockIs(*thenBlock); 1.5043 + JS_ASSERT_IF(!f.inDeadCode(), *thenBlock && *elseOrJoinBlock); 1.5044 + return true; 1.5045 +} 1.5046 + 1.5047 +static bool 1.5048 +CheckIf(FunctionCompiler &f, ParseNode *ifStmt) 1.5049 +{ 1.5050 + // Handle if/else-if chains using iteration instead of recursion. This 1.5051 + // avoids blowing the C stack quota for long if/else-if chains and also 1.5052 + // creates fewer MBasicBlocks at join points (by creating one join block 1.5053 + // for the entire if/else-if chain). 1.5054 + BlockVector thenBlocks(f.cx()); 1.5055 + 1.5056 + ParseNode *nextStmt = NextNode(ifStmt); 1.5057 + recurse: 1.5058 + JS_ASSERT(ifStmt->isKind(PNK_IF)); 1.5059 + ParseNode *cond = TernaryKid1(ifStmt); 1.5060 + ParseNode *thenStmt = TernaryKid2(ifStmt); 1.5061 + ParseNode *elseStmt = TernaryKid3(ifStmt); 1.5062 + 1.5063 + MBasicBlock *thenBlock = nullptr, *elseBlock = nullptr; 1.5064 + ParseNode *elseOrJoinStmt = elseStmt ? elseStmt : nextStmt; 1.5065 + 1.5066 + if (!CheckIfCondition(f, cond, thenStmt, elseOrJoinStmt, &thenBlock, &elseBlock)) 1.5067 + return false; 1.5068 + 1.5069 + if (!CheckStatement(f, thenStmt)) 1.5070 + return false; 1.5071 + 1.5072 + if (!f.appendThenBlock(&thenBlocks)) 1.5073 + return false; 1.5074 + 1.5075 + if (!elseStmt) { 1.5076 + if (!f.joinIf(thenBlocks, elseBlock)) 1.5077 + return false; 1.5078 + } else { 1.5079 + f.switchToElse(elseBlock); 1.5080 + 1.5081 + if (elseStmt->isKind(PNK_IF)) { 1.5082 + ifStmt = elseStmt; 1.5083 + goto recurse; 1.5084 + } 1.5085 + 1.5086 + if (!CheckStatement(f, elseStmt)) 1.5087 + return false; 1.5088 + 1.5089 + if (!f.joinIfElse(thenBlocks, nextStmt)) 1.5090 + return false; 1.5091 + } 1.5092 + 1.5093 + return true; 1.5094 +} 1.5095 + 1.5096 +static bool 1.5097 +CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value) 1.5098 +{ 1.5099 + if (!IsNumericLiteral(f.m(), caseExpr)) 1.5100 + return f.fail(caseExpr, "switch case expression must be an integer literal"); 1.5101 + 1.5102 + NumLit literal = ExtractNumericLiteral(f.m(), caseExpr); 1.5103 + switch (literal.which()) { 1.5104 + case NumLit::Fixnum: 1.5105 + case NumLit::NegativeInt: 1.5106 + *value = literal.toInt32(); 1.5107 + break; 1.5108 + case NumLit::OutOfRangeInt: 1.5109 + case NumLit::BigUnsigned: 1.5110 + return f.fail(caseExpr, "switch case expression out of integer range"); 1.5111 + case NumLit::Double: 1.5112 + case NumLit::Float: 1.5113 + return f.fail(caseExpr, "switch case expression must be an integer literal"); 1.5114 + } 1.5115 + 1.5116 + return true; 1.5117 +} 1.5118 + 1.5119 +static bool 1.5120 +CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt) 1.5121 +{ 1.5122 + for (; stmt; stmt = NextNode(stmt)) { 1.5123 + JS_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT)); 1.5124 + if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != nullptr) 1.5125 + return f.fail(stmt, "default label must be at the end"); 1.5126 + } 1.5127 + 1.5128 + return true; 1.5129 +} 1.5130 + 1.5131 +static bool 1.5132 +CheckSwitchRange(FunctionCompiler &f, ParseNode *stmt, int32_t *low, int32_t *high, 1.5133 + int32_t *tableLength) 1.5134 +{ 1.5135 + if (stmt->isKind(PNK_DEFAULT)) { 1.5136 + *low = 0; 1.5137 + *high = -1; 1.5138 + *tableLength = 0; 1.5139 + return true; 1.5140 + } 1.5141 + 1.5142 + int32_t i = 0; 1.5143 + if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) 1.5144 + return false; 1.5145 + 1.5146 + *low = *high = i; 1.5147 + 1.5148 + ParseNode *initialStmt = stmt; 1.5149 + for (stmt = NextNode(stmt); stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) { 1.5150 + int32_t i = 0; 1.5151 + if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) 1.5152 + return false; 1.5153 + 1.5154 + *low = Min(*low, i); 1.5155 + *high = Max(*high, i); 1.5156 + } 1.5157 + 1.5158 + int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1; 1.5159 + if (i64 > 4*1024*1024) 1.5160 + return f.fail(initialStmt, "all switch statements generate tables; this table would be too big"); 1.5161 + 1.5162 + *tableLength = int32_t(i64); 1.5163 + return true; 1.5164 +} 1.5165 + 1.5166 +static bool 1.5167 +CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt) 1.5168 +{ 1.5169 + JS_ASSERT(switchStmt->isKind(PNK_SWITCH)); 1.5170 + ParseNode *switchExpr = BinaryLeft(switchStmt); 1.5171 + ParseNode *switchBody = BinaryRight(switchStmt); 1.5172 + 1.5173 + if (!switchBody->isKind(PNK_STATEMENTLIST)) 1.5174 + return f.fail(switchBody, "switch body may not contain 'let' declarations"); 1.5175 + 1.5176 + MDefinition *exprDef; 1.5177 + Type exprType; 1.5178 + if (!CheckExpr(f, switchExpr, &exprDef, &exprType)) 1.5179 + return false; 1.5180 + 1.5181 + if (!exprType.isSigned()) 1.5182 + return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars()); 1.5183 + 1.5184 + ParseNode *stmt = ListHead(switchBody); 1.5185 + 1.5186 + if (!CheckDefaultAtEnd(f, stmt)) 1.5187 + return false; 1.5188 + 1.5189 + if (!stmt) 1.5190 + return true; 1.5191 + 1.5192 + int32_t low = 0, high = 0, tableLength = 0; 1.5193 + if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength)) 1.5194 + return false; 1.5195 + 1.5196 + BlockVector cases(f.cx()); 1.5197 + if (!cases.resize(tableLength)) 1.5198 + return false; 1.5199 + 1.5200 + MBasicBlock *switchBlock; 1.5201 + if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock)) 1.5202 + return false; 1.5203 + 1.5204 + for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) { 1.5205 + int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32(); 1.5206 + unsigned caseIndex = caseValue - low; 1.5207 + 1.5208 + if (cases[caseIndex]) 1.5209 + return f.fail(stmt, "no duplicate case labels"); 1.5210 + 1.5211 + if (!f.startSwitchCase(switchBlock, &cases[caseIndex], stmt)) 1.5212 + return false; 1.5213 + 1.5214 + if (!CheckStatement(f, CaseBody(stmt))) 1.5215 + return false; 1.5216 + } 1.5217 + 1.5218 + MBasicBlock *defaultBlock; 1.5219 + if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock, stmt)) 1.5220 + return false; 1.5221 + 1.5222 + if (stmt && stmt->isKind(PNK_DEFAULT)) { 1.5223 + if (!CheckStatement(f, CaseBody(stmt))) 1.5224 + return false; 1.5225 + } 1.5226 + 1.5227 + return f.joinSwitch(switchBlock, cases, defaultBlock); 1.5228 +} 1.5229 + 1.5230 +static bool 1.5231 +CheckReturnType(FunctionCompiler &f, ParseNode *usepn, RetType retType) 1.5232 +{ 1.5233 + if (!f.hasAlreadyReturned()) { 1.5234 + f.setReturnedType(retType); 1.5235 + return true; 1.5236 + } 1.5237 + 1.5238 + if (f.returnedType() != retType) { 1.5239 + return f.failf(usepn, "%s incompatible with previous return of type %s", 1.5240 + retType.toType().toChars(), f.returnedType().toType().toChars()); 1.5241 + } 1.5242 + 1.5243 + return true; 1.5244 +} 1.5245 + 1.5246 +static bool 1.5247 +CheckReturn(FunctionCompiler &f, ParseNode *returnStmt) 1.5248 +{ 1.5249 + ParseNode *expr = ReturnExpr(returnStmt); 1.5250 + 1.5251 + if (!expr) { 1.5252 + if (!CheckReturnType(f, returnStmt, RetType::Void)) 1.5253 + return false; 1.5254 + 1.5255 + f.returnVoid(); 1.5256 + return true; 1.5257 + } 1.5258 + 1.5259 + MDefinition *def; 1.5260 + Type type; 1.5261 + if (!CheckExpr(f, expr, &def, &type)) 1.5262 + return false; 1.5263 + 1.5264 + RetType retType; 1.5265 + if (type.isSigned()) 1.5266 + retType = RetType::Signed; 1.5267 + else if (type.isDouble()) 1.5268 + retType = RetType::Double; 1.5269 + else if (type.isFloat()) 1.5270 + retType = RetType::Float; 1.5271 + else if (type.isVoid()) 1.5272 + retType = RetType::Void; 1.5273 + else 1.5274 + return f.failf(expr, "%s is not a valid return type", type.toChars()); 1.5275 + 1.5276 + if (!CheckReturnType(f, expr, retType)) 1.5277 + return false; 1.5278 + 1.5279 + if (retType == RetType::Void) 1.5280 + f.returnVoid(); 1.5281 + else 1.5282 + f.returnExpr(def); 1.5283 + return true; 1.5284 +} 1.5285 + 1.5286 +static bool 1.5287 +CheckStatementList(FunctionCompiler &f, ParseNode *stmtList) 1.5288 +{ 1.5289 + JS_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); 1.5290 + 1.5291 + for (ParseNode *stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) { 1.5292 + if (!CheckStatement(f, stmt)) 1.5293 + return false; 1.5294 + } 1.5295 + 1.5296 + return true; 1.5297 +} 1.5298 + 1.5299 +static bool 1.5300 +CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels) 1.5301 +{ 1.5302 + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); 1.5303 + 1.5304 + if (!f.mirGen().ensureBallast()) 1.5305 + return false; 1.5306 + 1.5307 + switch (stmt->getKind()) { 1.5308 + case PNK_SEMI: return CheckExprStatement(f, stmt); 1.5309 + case PNK_WHILE: return CheckWhile(f, stmt, maybeLabels); 1.5310 + case PNK_FOR: return CheckFor(f, stmt, maybeLabels); 1.5311 + case PNK_DOWHILE: return CheckDoWhile(f, stmt, maybeLabels); 1.5312 + case PNK_LABEL: return CheckLabel(f, stmt, maybeLabels); 1.5313 + case PNK_IF: return CheckIf(f, stmt); 1.5314 + case PNK_SWITCH: return CheckSwitch(f, stmt); 1.5315 + case PNK_RETURN: return CheckReturn(f, stmt); 1.5316 + case PNK_STATEMENTLIST: return CheckStatementList(f, stmt); 1.5317 + case PNK_BREAK: return f.addBreak(LoopControlMaybeLabel(stmt)); 1.5318 + case PNK_CONTINUE: return f.addContinue(LoopControlMaybeLabel(stmt)); 1.5319 + default:; 1.5320 + } 1.5321 + 1.5322 + return f.fail(stmt, "unexpected statement kind"); 1.5323 +} 1.5324 + 1.5325 +static bool 1.5326 +ParseFunction(ModuleCompiler &m, ParseNode **fnOut) 1.5327 +{ 1.5328 + TokenStream &tokenStream = m.tokenStream(); 1.5329 + 1.5330 + DebugOnly<TokenKind> tk = tokenStream.getToken(); 1.5331 + JS_ASSERT(tk == TOK_FUNCTION); 1.5332 + 1.5333 + RootedPropertyName name(m.cx()); 1.5334 + 1.5335 + TokenKind tt = tokenStream.getToken(); 1.5336 + if (tt == TOK_NAME) { 1.5337 + name = tokenStream.currentName(); 1.5338 + } else if (tt == TOK_YIELD) { 1.5339 + if (!m.parser().checkYieldNameValidity()) 1.5340 + return false; 1.5341 + name = m.cx()->names().yield; 1.5342 + } else { 1.5343 + return false; // The regular parser will throw a SyntaxError, no need to m.fail. 1.5344 + } 1.5345 + 1.5346 + ParseNode *fn = m.parser().handler.newFunctionDefinition(); 1.5347 + if (!fn) 1.5348 + return false; 1.5349 + 1.5350 + // This flows into FunctionBox, so must be tenured. 1.5351 + RootedFunction fun(m.cx(), NewFunction(m.cx(), NullPtr(), nullptr, 0, JSFunction::INTERPRETED, 1.5352 + m.cx()->global(), name, JSFunction::FinalizeKind, 1.5353 + TenuredObject)); 1.5354 + if (!fun) 1.5355 + return false; 1.5356 + 1.5357 + AsmJSParseContext *outerpc = m.parser().pc; 1.5358 + 1.5359 + Directives directives(outerpc); 1.5360 + FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator); 1.5361 + if (!funbox) 1.5362 + return false; 1.5363 + 1.5364 + Directives newDirectives = directives; 1.5365 + AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives, 1.5366 + outerpc->staticLevel + 1, outerpc->blockidGen, 1.5367 + /* blockScopeDepth = */ 0); 1.5368 + if (!funpc.init(tokenStream)) 1.5369 + return false; 1.5370 + 1.5371 + if (!m.parser().functionArgsAndBodyGeneric(fn, fun, Normal, Statement, &newDirectives)) 1.5372 + return false; 1.5373 + 1.5374 + if (tokenStream.hadError() || directives != newDirectives) 1.5375 + return false; 1.5376 + 1.5377 + outerpc->blockidGen = funpc.blockidGen; 1.5378 + fn->pn_blockid = outerpc->blockid(); 1.5379 + 1.5380 + *fnOut = fn; 1.5381 + return true; 1.5382 +} 1.5383 + 1.5384 +static bool 1.5385 +CheckFunction(ModuleCompiler &m, LifoAlloc &lifo, MIRGenerator **mir, ModuleCompiler::Func **funcOut) 1.5386 +{ 1.5387 + int64_t before = PRMJ_Now(); 1.5388 + 1.5389 + // asm.js modules can be quite large when represented as parse trees so pop 1.5390 + // the backing LifoAlloc after parsing/compiling each function. 1.5391 + AsmJSParser::Mark mark = m.parser().mark(); 1.5392 + 1.5393 + ParseNode *fn; 1.5394 + if (!ParseFunction(m, &fn)) 1.5395 + return false; 1.5396 + 1.5397 + if (!CheckFunctionHead(m, fn)) 1.5398 + return false; 1.5399 + 1.5400 + FunctionCompiler f(m, fn, lifo); 1.5401 + if (!f.init()) 1.5402 + return false; 1.5403 + 1.5404 + ParseNode *stmtIter = ListHead(FunctionStatementList(fn)); 1.5405 + 1.5406 + VarTypeVector argTypes(m.lifo()); 1.5407 + if (!CheckArguments(f, &stmtIter, &argTypes)) 1.5408 + return false; 1.5409 + 1.5410 + if (!CheckVariables(f, &stmtIter)) 1.5411 + return false; 1.5412 + 1.5413 + if (!f.prepareToEmitMIR(argTypes)) 1.5414 + return false; 1.5415 + 1.5416 + ParseNode *lastNonEmptyStmt = nullptr; 1.5417 + for (; stmtIter; stmtIter = NextNode(stmtIter)) { 1.5418 + if (!CheckStatement(f, stmtIter)) 1.5419 + return false; 1.5420 + if (!IsEmptyStatement(stmtIter)) 1.5421 + lastNonEmptyStmt = stmtIter; 1.5422 + } 1.5423 + 1.5424 + RetType retType; 1.5425 + if (!CheckFinalReturn(f, lastNonEmptyStmt, &retType)) 1.5426 + return false; 1.5427 + 1.5428 + if (!CheckReturnType(f, lastNonEmptyStmt, retType)) 1.5429 + return false; 1.5430 + 1.5431 + Signature sig(Move(argTypes), retType); 1.5432 + ModuleCompiler::Func *func = nullptr; 1.5433 + if (!CheckFunctionSignature(m, fn, Move(sig), FunctionName(fn), &func)) 1.5434 + return false; 1.5435 + 1.5436 + if (func->defined()) 1.5437 + return m.failName(fn, "function '%s' already defined", FunctionName(fn)); 1.5438 + 1.5439 + uint32_t funcBegin = fn->pn_pos.begin; 1.5440 + uint32_t funcEnd = fn->pn_pos.end; 1.5441 + // The begin/end char range is relative to the beginning of the module, 1.5442 + // hence the assertions. 1.5443 + JS_ASSERT(funcBegin > m.moduleStart()); 1.5444 + JS_ASSERT(funcEnd > m.moduleStart()); 1.5445 + funcBegin -= m.moduleStart(); 1.5446 + funcEnd -= m.moduleStart(); 1.5447 + func->finish(funcBegin, funcEnd); 1.5448 + 1.5449 + func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC); 1.5450 + 1.5451 + m.parser().release(mark); 1.5452 + 1.5453 + // Copy the cumulative minimum heap size constraint to the MIR for use in analysis. The length 1.5454 + // is also constrained to particular lengths, so firstly round up - a larger 'heap required 1.5455 + // length' can help range analysis to prove that bounds checks are not needed. 1.5456 + uint32_t len = js::RoundUpToNextValidAsmJSHeapLength(m.minHeapLength()); 1.5457 + m.requireHeapLengthToBeAtLeast(len); 1.5458 + 1.5459 + *mir = f.extractMIR(); 1.5460 + (*mir)->noteMinAsmJSHeapLength(len); 1.5461 + *funcOut = func; 1.5462 + return true; 1.5463 +} 1.5464 + 1.5465 +static bool 1.5466 +GenerateCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mir, LIRGraph &lir) 1.5467 +{ 1.5468 + int64_t before = PRMJ_Now(); 1.5469 + 1.5470 + // A single MacroAssembler is reused for all function compilations so 1.5471 + // that there is a single linear code segment for each module. To avoid 1.5472 + // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR 1.5473 + // after each function is compiled. This method is responsible for cleaning 1.5474 + // out any dangling pointers that the MacroAssembler may have kept. 1.5475 + m.masm().resetForNewCodeGenerator(mir.alloc()); 1.5476 + 1.5477 + m.masm().bind(func.code()); 1.5478 + 1.5479 + ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm())); 1.5480 + if (!codegen || !codegen->generateAsmJS(&m.stackOverflowLabel())) 1.5481 + return m.fail(nullptr, "internal codegen failure (probably out of memory)"); 1.5482 + 1.5483 +#if defined(MOZ_VTUNE) || defined(JS_ION_PERF) 1.5484 + // Profiling might not be active now, but it may be activated later (perhaps 1.5485 + // after the module has been cached and reloaded from the cache). Function 1.5486 + // profiling info isn't huge, so store it always (in --enable-profiling 1.5487 + // builds, which is only Nightly builds, but default). 1.5488 + if (!m.trackProfiledFunction(func, m.masm().currentOffset())) 1.5489 + return false; 1.5490 +#endif 1.5491 + 1.5492 +#ifdef JS_ION_PERF 1.5493 + // Per-block profiling info uses significantly more memory so only store 1.5494 + // this information if it is actively requested. 1.5495 + if (PerfBlockEnabled()) { 1.5496 + if (!m.trackPerfProfiledBlocks(mir.perfSpewer(), func, m.masm().currentOffset())) 1.5497 + return false; 1.5498 + } 1.5499 +#endif 1.5500 + 1.5501 + // Align internal function headers. 1.5502 + m.masm().align(CodeAlignment); 1.5503 + 1.5504 + func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC); 1.5505 + if (!m.maybeReportCompileTime(func)) 1.5506 + return false; 1.5507 + 1.5508 + // Unlike regular IonMonkey which links and generates a new JitCode for 1.5509 + // every function, we accumulate all the functions in the module in a 1.5510 + // single MacroAssembler and link at end. Linking asm.js doesn't require a 1.5511 + // CodeGenerator so we can destroy it now. 1.5512 + return true; 1.5513 +} 1.5514 + 1.5515 +static bool 1.5516 +CheckAllFunctionsDefined(ModuleCompiler &m) 1.5517 +{ 1.5518 + for (unsigned i = 0; i < m.numFunctions(); i++) { 1.5519 + if (!m.function(i).code()->bound()) 1.5520 + return m.failName(nullptr, "missing definition of function %s", m.function(i).name()); 1.5521 + } 1.5522 + 1.5523 + return true; 1.5524 +} 1.5525 + 1.5526 +static bool 1.5527 +CheckFunctionsSequential(ModuleCompiler &m) 1.5528 +{ 1.5529 + // Use a single LifoAlloc to allocate all the temporary compiler IR. 1.5530 + // All allocated LifoAlloc'd memory is released after compiling each 1.5531 + // function by the LifoAllocScope inside the loop. 1.5532 + LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE); 1.5533 + 1.5534 + while (PeekToken(m.parser()) == TOK_FUNCTION) { 1.5535 + LifoAllocScope scope(&lifo); 1.5536 + 1.5537 + MIRGenerator *mir; 1.5538 + ModuleCompiler::Func *func; 1.5539 + if (!CheckFunction(m, lifo, &mir, &func)) 1.5540 + return false; 1.5541 + 1.5542 + int64_t before = PRMJ_Now(); 1.5543 + 1.5544 + IonContext icx(m.cx(), &mir->alloc()); 1.5545 + 1.5546 + IonSpewNewFunction(&mir->graph(), NullPtr()); 1.5547 + 1.5548 + if (!OptimizeMIR(mir)) 1.5549 + return m.failOffset(func->srcOffset(), "internal compiler failure (probably out of memory)"); 1.5550 + 1.5551 + LIRGraph *lir = GenerateLIR(mir); 1.5552 + if (!lir) 1.5553 + return m.failOffset(func->srcOffset(), "internal compiler failure (probably out of memory)"); 1.5554 + 1.5555 + func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC); 1.5556 + 1.5557 + if (!GenerateCode(m, *func, *mir, *lir)) 1.5558 + return false; 1.5559 + 1.5560 + IonSpewEndFunction(); 1.5561 + } 1.5562 + 1.5563 + if (!CheckAllFunctionsDefined(m)) 1.5564 + return false; 1.5565 + 1.5566 + return true; 1.5567 +} 1.5568 + 1.5569 +#ifdef JS_THREADSAFE 1.5570 + 1.5571 +// Currently, only one asm.js parallel compilation is allowed at a time. 1.5572 +// This RAII class attempts to claim this parallel compilation using atomic ops 1.5573 +// on rt->workerThreadState->asmJSCompilationInProgress. 1.5574 +class ParallelCompilationGuard 1.5575 +{ 1.5576 + bool parallelState_; 1.5577 + public: 1.5578 + ParallelCompilationGuard() : parallelState_(false) {} 1.5579 + ~ParallelCompilationGuard() { 1.5580 + if (parallelState_) { 1.5581 + JS_ASSERT(WorkerThreadState().asmJSCompilationInProgress == true); 1.5582 + WorkerThreadState().asmJSCompilationInProgress = false; 1.5583 + } 1.5584 + } 1.5585 + bool claim() { 1.5586 + JS_ASSERT(!parallelState_); 1.5587 + if (!WorkerThreadState().asmJSCompilationInProgress.compareExchange(false, true)) 1.5588 + return false; 1.5589 + parallelState_ = true; 1.5590 + return true; 1.5591 + } 1.5592 +}; 1.5593 + 1.5594 +static bool 1.5595 +ParallelCompilationEnabled(ExclusiveContext *cx) 1.5596 +{ 1.5597 + // If 'cx' isn't a JSContext, then we are already off the main thread so 1.5598 + // off-thread compilation must be enabled. However, since there are a fixed 1.5599 + // number of worker threads and one is already being consumed by this 1.5600 + // parsing task, ensure that there another free thread to avoid deadlock. 1.5601 + // (Note: there is at most one thread used for parsing so we don't have to 1.5602 + // worry about general dining philosophers.) 1.5603 + if (WorkerThreadState().threadCount <= 1) 1.5604 + return false; 1.5605 + 1.5606 + if (!cx->isJSContext()) 1.5607 + return true; 1.5608 + return cx->asJSContext()->runtime()->canUseParallelIonCompilation(); 1.5609 +} 1.5610 + 1.5611 +// State of compilation as tracked and updated by the main thread. 1.5612 +struct ParallelGroupState 1.5613 +{ 1.5614 + js::Vector<AsmJSParallelTask> &tasks; 1.5615 + int32_t outstandingJobs; // Good work, jobs! 1.5616 + uint32_t compiledJobs; 1.5617 + 1.5618 + ParallelGroupState(js::Vector<AsmJSParallelTask> &tasks) 1.5619 + : tasks(tasks), outstandingJobs(0), compiledJobs(0) 1.5620 + { } 1.5621 +}; 1.5622 + 1.5623 +// Block until a worker-assigned LifoAlloc becomes finished. 1.5624 +static AsmJSParallelTask * 1.5625 +GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group) 1.5626 +{ 1.5627 + AutoLockWorkerThreadState lock; 1.5628 + 1.5629 + while (!WorkerThreadState().asmJSWorkerFailed()) { 1.5630 + if (!WorkerThreadState().asmJSFinishedList().empty()) { 1.5631 + group.outstandingJobs--; 1.5632 + return WorkerThreadState().asmJSFinishedList().popCopy(); 1.5633 + } 1.5634 + WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER); 1.5635 + } 1.5636 + 1.5637 + return nullptr; 1.5638 +} 1.5639 + 1.5640 +static bool 1.5641 +GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSParallelTask **outTask) 1.5642 +{ 1.5643 + // Block until a used LifoAlloc becomes available. 1.5644 + AsmJSParallelTask *task = GetFinishedCompilation(m, group); 1.5645 + if (!task) 1.5646 + return false; 1.5647 + 1.5648 + ModuleCompiler::Func &func = *reinterpret_cast<ModuleCompiler::Func *>(task->func); 1.5649 + func.accumulateCompileTime(task->compileTime); 1.5650 + 1.5651 + { 1.5652 + // Perform code generation on the main thread. 1.5653 + IonContext ionContext(m.cx(), &task->mir->alloc()); 1.5654 + if (!GenerateCode(m, func, *task->mir, *task->lir)) 1.5655 + return false; 1.5656 + } 1.5657 + 1.5658 + group.compiledJobs++; 1.5659 + 1.5660 + // Clear the LifoAlloc for use by another worker. 1.5661 + TempAllocator &tempAlloc = task->mir->alloc(); 1.5662 + tempAlloc.TempAllocator::~TempAllocator(); 1.5663 + task->lifo.releaseAll(); 1.5664 + 1.5665 + *outTask = task; 1.5666 + return true; 1.5667 +} 1.5668 + 1.5669 +static inline bool 1.5670 +GetUnusedTask(ParallelGroupState &group, uint32_t i, AsmJSParallelTask **outTask) 1.5671 +{ 1.5672 + // Since functions are dispatched in order, if fewer than |numLifos| functions 1.5673 + // have been generated, then the |i'th| LifoAlloc must never have been 1.5674 + // assigned to a worker thread. 1.5675 + if (i >= group.tasks.length()) 1.5676 + return false; 1.5677 + *outTask = &group.tasks[i]; 1.5678 + return true; 1.5679 +} 1.5680 + 1.5681 +static bool 1.5682 +CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group) 1.5683 +{ 1.5684 +#ifdef DEBUG 1.5685 + { 1.5686 + AutoLockWorkerThreadState lock; 1.5687 + JS_ASSERT(WorkerThreadState().asmJSWorklist().empty()); 1.5688 + JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty()); 1.5689 + } 1.5690 +#endif 1.5691 + WorkerThreadState().resetAsmJSFailureState(); 1.5692 + 1.5693 + for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) { 1.5694 + // Get exclusive access to an empty LifoAlloc from the thread group's pool. 1.5695 + AsmJSParallelTask *task = nullptr; 1.5696 + if (!GetUnusedTask(group, i, &task) && !GenerateCodeForFinishedJob(m, group, &task)) 1.5697 + return false; 1.5698 + 1.5699 + // Generate MIR into the LifoAlloc on the main thread. 1.5700 + MIRGenerator *mir; 1.5701 + ModuleCompiler::Func *func; 1.5702 + if (!CheckFunction(m, task->lifo, &mir, &func)) 1.5703 + return false; 1.5704 + 1.5705 + // Perform optimizations and LIR generation on a worker thread. 1.5706 + task->init(m.cx()->compartment()->runtimeFromAnyThread(), func, mir); 1.5707 + if (!StartOffThreadAsmJSCompile(m.cx(), task)) 1.5708 + return false; 1.5709 + 1.5710 + group.outstandingJobs++; 1.5711 + } 1.5712 + 1.5713 + // Block for all outstanding workers to complete. 1.5714 + while (group.outstandingJobs > 0) { 1.5715 + AsmJSParallelTask *ignored = nullptr; 1.5716 + if (!GenerateCodeForFinishedJob(m, group, &ignored)) 1.5717 + return false; 1.5718 + } 1.5719 + 1.5720 + if (!CheckAllFunctionsDefined(m)) 1.5721 + return false; 1.5722 + 1.5723 + JS_ASSERT(group.outstandingJobs == 0); 1.5724 + JS_ASSERT(group.compiledJobs == m.numFunctions()); 1.5725 +#ifdef DEBUG 1.5726 + { 1.5727 + AutoLockWorkerThreadState lock; 1.5728 + JS_ASSERT(WorkerThreadState().asmJSWorklist().empty()); 1.5729 + JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty()); 1.5730 + } 1.5731 +#endif 1.5732 + JS_ASSERT(!WorkerThreadState().asmJSWorkerFailed()); 1.5733 + return true; 1.5734 +} 1.5735 + 1.5736 +static void 1.5737 +CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group) 1.5738 +{ 1.5739 + // This is failure-handling code, so it's not allowed to fail. 1.5740 + // The problem is that all memory for compilation is stored in LifoAllocs 1.5741 + // maintained in the scope of CheckFunctionsParallel() -- so in order 1.5742 + // for that function to safely return, and thereby remove the LifoAllocs, 1.5743 + // none of that memory can be in use or reachable by workers. 1.5744 + 1.5745 + JS_ASSERT(group.outstandingJobs >= 0); 1.5746 + if (!group.outstandingJobs) 1.5747 + return; 1.5748 + 1.5749 + AutoLockWorkerThreadState lock; 1.5750 + 1.5751 + // From the compiling tasks, eliminate those waiting for worker assignation. 1.5752 + group.outstandingJobs -= WorkerThreadState().asmJSWorklist().length(); 1.5753 + WorkerThreadState().asmJSWorklist().clear(); 1.5754 + 1.5755 + // From the compiling tasks, eliminate those waiting for codegen. 1.5756 + group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length(); 1.5757 + WorkerThreadState().asmJSFinishedList().clear(); 1.5758 + 1.5759 + // Eliminate tasks that failed without adding to the finished list. 1.5760 + group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs(); 1.5761 + 1.5762 + // Any remaining tasks are therefore undergoing active compilation. 1.5763 + JS_ASSERT(group.outstandingJobs >= 0); 1.5764 + while (group.outstandingJobs > 0) { 1.5765 + WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER); 1.5766 + 1.5767 + group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs(); 1.5768 + group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length(); 1.5769 + WorkerThreadState().asmJSFinishedList().clear(); 1.5770 + } 1.5771 + 1.5772 + JS_ASSERT(group.outstandingJobs == 0); 1.5773 + JS_ASSERT(WorkerThreadState().asmJSWorklist().empty()); 1.5774 + JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty()); 1.5775 +} 1.5776 + 1.5777 +static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12; 1.5778 + 1.5779 +static bool 1.5780 +CheckFunctionsParallel(ModuleCompiler &m) 1.5781 +{ 1.5782 + // If parallel compilation isn't enabled (not enough cores, disabled by 1.5783 + // pref, etc) or another thread is currently compiling asm.js in parallel, 1.5784 + // fall back to sequential compilation. (We could lift the latter 1.5785 + // constraint by hoisting asmJS* state out of WorkerThreadState so multiple 1.5786 + // concurrent asm.js parallel compilations don't race.) 1.5787 + ParallelCompilationGuard g; 1.5788 + if (!ParallelCompilationEnabled(m.cx()) || !g.claim()) 1.5789 + return CheckFunctionsSequential(m); 1.5790 + 1.5791 + IonSpew(IonSpew_Logs, "Can't log asm.js script. (Compiled on background thread.)"); 1.5792 + 1.5793 + // Saturate all worker threads plus the main thread. 1.5794 + size_t numParallelJobs = WorkerThreadState().threadCount + 1; 1.5795 + 1.5796 + // Allocate scoped AsmJSParallelTask objects. Each contains a unique 1.5797 + // LifoAlloc that provides all necessary memory for compilation. 1.5798 + js::Vector<AsmJSParallelTask, 0> tasks(m.cx()); 1.5799 + if (!tasks.initCapacity(numParallelJobs)) 1.5800 + return false; 1.5801 + 1.5802 + for (size_t i = 0; i < numParallelJobs; i++) 1.5803 + tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE); 1.5804 + 1.5805 + // With compilation memory in-scope, dispatch worker threads. 1.5806 + ParallelGroupState group(tasks); 1.5807 + if (!CheckFunctionsParallelImpl(m, group)) { 1.5808 + CancelOutstandingJobs(m, group); 1.5809 + 1.5810 + // If failure was triggered by a worker thread, report error. 1.5811 + if (void *maybeFunc = WorkerThreadState().maybeAsmJSFailedFunction()) { 1.5812 + ModuleCompiler::Func *func = reinterpret_cast<ModuleCompiler::Func *>(maybeFunc); 1.5813 + return m.failOffset(func->srcOffset(), "allocation failure during compilation"); 1.5814 + } 1.5815 + 1.5816 + // Otherwise, the error occurred on the main thread and was already reported. 1.5817 + return false; 1.5818 + } 1.5819 + return true; 1.5820 +} 1.5821 +#endif // JS_THREADSAFE 1.5822 + 1.5823 +static bool 1.5824 +CheckFuncPtrTable(ModuleCompiler &m, ParseNode *var) 1.5825 +{ 1.5826 + if (!IsDefinition(var)) 1.5827 + return m.fail(var, "function-pointer table name must be unique"); 1.5828 + 1.5829 + ParseNode *arrayLiteral = MaybeDefinitionInitializer(var); 1.5830 + if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY)) 1.5831 + return m.fail(var, "function-pointer table's initializer must be an array literal"); 1.5832 + 1.5833 + unsigned length = ListLength(arrayLiteral); 1.5834 + 1.5835 + if (!IsPowerOfTwo(length)) 1.5836 + return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length); 1.5837 + 1.5838 + unsigned mask = length - 1; 1.5839 + 1.5840 + ModuleCompiler::FuncPtrVector elems(m.cx()); 1.5841 + const Signature *firstSig = nullptr; 1.5842 + 1.5843 + for (ParseNode *elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { 1.5844 + if (!elem->isKind(PNK_NAME)) 1.5845 + return m.fail(elem, "function-pointer table's elements must be names of functions"); 1.5846 + 1.5847 + PropertyName *funcName = elem->name(); 1.5848 + const ModuleCompiler::Func *func = m.lookupFunction(funcName); 1.5849 + if (!func) 1.5850 + return m.fail(elem, "function-pointer table's elements must be names of functions"); 1.5851 + 1.5852 + if (firstSig) { 1.5853 + if (*firstSig != func->sig()) 1.5854 + return m.fail(elem, "all functions in table must have same signature"); 1.5855 + } else { 1.5856 + firstSig = &func->sig(); 1.5857 + } 1.5858 + 1.5859 + if (!elems.append(func)) 1.5860 + return false; 1.5861 + } 1.5862 + 1.5863 + Signature sig(m.lifo()); 1.5864 + if (!sig.copy(*firstSig)) 1.5865 + return false; 1.5866 + 1.5867 + ModuleCompiler::FuncPtrTable *table; 1.5868 + if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(sig), mask, &table)) 1.5869 + return false; 1.5870 + 1.5871 + table->initElems(Move(elems)); 1.5872 + return true; 1.5873 +} 1.5874 + 1.5875 +static bool 1.5876 +CheckFuncPtrTables(ModuleCompiler &m) 1.5877 +{ 1.5878 + while (true) { 1.5879 + ParseNode *varStmt; 1.5880 + if (!ParseVarOrConstStatement(m.parser(), &varStmt)) 1.5881 + return false; 1.5882 + if (!varStmt) 1.5883 + break; 1.5884 + for (ParseNode *var = VarListHead(varStmt); var; var = NextNode(var)) { 1.5885 + if (!CheckFuncPtrTable(m, var)) 1.5886 + return false; 1.5887 + } 1.5888 + } 1.5889 + 1.5890 + for (unsigned i = 0; i < m.numFuncPtrTables(); i++) { 1.5891 + if (!m.funcPtrTable(i).initialized()) 1.5892 + return m.fail(nullptr, "expecting function-pointer table"); 1.5893 + } 1.5894 + 1.5895 + return true; 1.5896 +} 1.5897 + 1.5898 +static bool 1.5899 +CheckModuleExportFunction(ModuleCompiler &m, ParseNode *returnExpr) 1.5900 +{ 1.5901 + if (!returnExpr->isKind(PNK_NAME)) 1.5902 + return m.fail(returnExpr, "export statement must be of the form 'return name'"); 1.5903 + 1.5904 + PropertyName *funcName = returnExpr->name(); 1.5905 + 1.5906 + const ModuleCompiler::Func *func = m.lookupFunction(funcName); 1.5907 + if (!func) 1.5908 + return m.failName(returnExpr, "exported function name '%s' not found", funcName); 1.5909 + 1.5910 + return m.addExportedFunction(func, /* maybeFieldName = */ nullptr); 1.5911 +} 1.5912 + 1.5913 +static bool 1.5914 +CheckModuleExportObject(ModuleCompiler &m, ParseNode *object) 1.5915 +{ 1.5916 + JS_ASSERT(object->isKind(PNK_OBJECT)); 1.5917 + 1.5918 + for (ParseNode *pn = ListHead(object); pn; pn = NextNode(pn)) { 1.5919 + if (!IsNormalObjectField(m.cx(), pn)) 1.5920 + return m.fail(pn, "only normal object properties may be used in the export object literal"); 1.5921 + 1.5922 + PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn); 1.5923 + 1.5924 + ParseNode *initNode = ObjectFieldInitializer(pn); 1.5925 + if (!initNode->isKind(PNK_NAME)) 1.5926 + return m.fail(initNode, "initializer of exported object literal must be name of function"); 1.5927 + 1.5928 + PropertyName *funcName = initNode->name(); 1.5929 + 1.5930 + const ModuleCompiler::Func *func = m.lookupFunction(funcName); 1.5931 + if (!func) 1.5932 + return m.failName(initNode, "exported function name '%s' not found", funcName); 1.5933 + 1.5934 + if (!m.addExportedFunction(func, fieldName)) 1.5935 + return false; 1.5936 + } 1.5937 + 1.5938 + return true; 1.5939 +} 1.5940 + 1.5941 +static bool 1.5942 +CheckModuleReturn(ModuleCompiler &m) 1.5943 +{ 1.5944 + if (PeekToken(m.parser()) != TOK_RETURN) { 1.5945 + TokenKind tk = PeekToken(m.parser()); 1.5946 + if (tk == TOK_RC || tk == TOK_EOF) 1.5947 + return m.fail(nullptr, "expecting return statement"); 1.5948 + return m.fail(nullptr, "invalid asm.js statement"); 1.5949 + } 1.5950 + 1.5951 + ParseNode *returnStmt = m.parser().statement(); 1.5952 + if (!returnStmt) 1.5953 + return false; 1.5954 + 1.5955 + ParseNode *returnExpr = ReturnExpr(returnStmt); 1.5956 + if (!returnExpr) 1.5957 + return m.fail(returnStmt, "export statement must return something"); 1.5958 + 1.5959 + if (returnExpr->isKind(PNK_OBJECT)) { 1.5960 + if (!CheckModuleExportObject(m, returnExpr)) 1.5961 + return false; 1.5962 + } else { 1.5963 + if (!CheckModuleExportFunction(m, returnExpr)) 1.5964 + return false; 1.5965 + } 1.5966 + 1.5967 + // Function statements are not added to the lexical scope in ParseContext 1.5968 + // (since cx->tempLifoAlloc is marked/released after each function 1.5969 + // statement) and thus all the identifiers in the return statement will be 1.5970 + // mistaken as free variables and added to lexdeps. Clear these now. 1.5971 + m.parser().pc->lexdeps->clear(); 1.5972 + return true; 1.5973 +} 1.5974 + 1.5975 +// All registers except the stack pointer. 1.5976 +static const RegisterSet AllRegsExceptSP = 1.5977 + RegisterSet(GeneralRegisterSet(Registers::AllMask & 1.5978 + ~(uint32_t(1) << Registers::StackPointer)), 1.5979 + FloatRegisterSet(FloatRegisters::AllMask)); 1.5980 +#if defined(JS_CODEGEN_ARM) 1.5981 +// The ARM system ABI also includes d15 in the non volatile float registers. 1.5982 +static const RegisterSet NonVolatileRegs = 1.5983 + RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask), 1.5984 + FloatRegisterSet(FloatRegisters::NonVolatileMask | (1 << FloatRegisters::d15))); 1.5985 +#else 1.5986 +static const RegisterSet NonVolatileRegs = 1.5987 + RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask), 1.5988 + FloatRegisterSet(FloatRegisters::NonVolatileMask)); 1.5989 +#endif 1.5990 + 1.5991 +static void 1.5992 +LoadAsmJSActivationIntoRegister(MacroAssembler &masm, Register reg) 1.5993 +{ 1.5994 + masm.movePtr(AsmJSImm_Runtime, reg); 1.5995 + size_t offset = offsetof(JSRuntime, mainThread) + 1.5996 + PerThreadData::offsetOfAsmJSActivationStackReadOnly(); 1.5997 + masm.loadPtr(Address(reg, offset), reg); 1.5998 +} 1.5999 + 1.6000 +static void 1.6001 +LoadJSContextFromActivation(MacroAssembler &masm, Register activation, Register dest) 1.6002 +{ 1.6003 + masm.loadPtr(Address(activation, AsmJSActivation::offsetOfContext()), dest); 1.6004 +} 1.6005 + 1.6006 +static void 1.6007 +AssertStackAlignment(MacroAssembler &masm) 1.6008 +{ 1.6009 + JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0); 1.6010 +#ifdef DEBUG 1.6011 + Label ok; 1.6012 + JS_ASSERT(IsPowerOfTwo(StackAlignment)); 1.6013 + masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok); 1.6014 + masm.assumeUnreachable("Stack should be aligned."); 1.6015 + masm.bind(&ok); 1.6016 +#endif 1.6017 +} 1.6018 + 1.6019 +template <class VectorT> 1.6020 +static unsigned 1.6021 +StackArgBytes(const VectorT &argTypes) 1.6022 +{ 1.6023 + ABIArgIter<VectorT> iter(argTypes); 1.6024 + while (!iter.done()) 1.6025 + iter++; 1.6026 + return iter.stackBytesConsumedSoFar(); 1.6027 +} 1.6028 + 1.6029 +static unsigned 1.6030 +StackDecrementForCall(MacroAssembler &masm, unsigned bytesToPush) 1.6031 +{ 1.6032 + // Include extra padding so that, after pushing the bytesToPush, 1.6033 + // the stack is aligned for a call instruction. 1.6034 + unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed(); 1.6035 + return AlignBytes(alreadyPushed + bytesToPush, StackAlignment) - alreadyPushed; 1.6036 +} 1.6037 + 1.6038 +template <class VectorT> 1.6039 +static unsigned 1.6040 +StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned extraBytes = 0) 1.6041 +{ 1.6042 + return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes); 1.6043 +} 1.6044 + 1.6045 +static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) + 1.6046 + NonVolatileRegs.fpus().size() * sizeof(double); 1.6047 + 1.6048 +// On arm, we need to include an extra word of space at the top of the stack so 1.6049 +// we can explicitly store the return address before making the call to C++ or 1.6050 +// Ion. On x86/x64, this isn't necessary since the call instruction pushes the 1.6051 +// return address. 1.6052 +#ifdef JS_CODEGEN_ARM 1.6053 +static const unsigned MaybeRetAddr = sizeof(void*); 1.6054 +#else 1.6055 +static const unsigned MaybeRetAddr = 0; 1.6056 +#endif 1.6057 + 1.6058 +static bool 1.6059 +GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc) 1.6060 +{ 1.6061 + MacroAssembler &masm = m.masm(); 1.6062 + 1.6063 + // In constrast to the system ABI, the Ion convention is that all registers 1.6064 + // are clobbered by calls. Thus, we must save the caller's non-volatile 1.6065 + // registers. 1.6066 + // 1.6067 + // NB: GenerateExits assumes that masm.framePushed() == 0 before 1.6068 + // PushRegsInMask(NonVolatileRegs). 1.6069 + masm.setFramePushed(0); 1.6070 + masm.PushRegsInMask(NonVolatileRegs); 1.6071 + JS_ASSERT(masm.framePushed() == FramePushedAfterSave); 1.6072 + 1.6073 + // Remember the stack pointer in the current AsmJSActivation. This will be 1.6074 + // used by error exit paths to set the stack pointer back to what it was 1.6075 + // right after the (C++) caller's non-volatile registers were saved so that 1.6076 + // they can be restored. 1.6077 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6078 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6079 + masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP())); 1.6080 + 1.6081 + // ARM has a globally-pinned GlobalReg (x64 uses RIP-relative addressing, 1.6082 + // x86 uses immediates in effective addresses) and NaN register (used as 1.6083 + // part of the out-of-bounds handling in heap loads/stores). 1.6084 +#if defined(JS_CODEGEN_ARM) 1.6085 + masm.movePtr(IntArgReg1, GlobalReg); 1.6086 + masm.ma_vimm(GenericNaN(), NANReg); 1.6087 +#endif 1.6088 + 1.6089 + // ARM and x64 have a globally-pinned HeapReg (x86 uses immediates in 1.6090 + // effective addresses). 1.6091 +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) 1.6092 + masm.loadPtr(Address(IntArgReg1, m.module().heapOffset()), HeapReg); 1.6093 +#endif 1.6094 + 1.6095 + // Get 'argv' into a non-arg register and save it on the stack. 1.6096 + Register argv = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6097 + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; 1.6098 +#if defined(JS_CODEGEN_X86) 1.6099 + masm.loadPtr(Address(StackPointer, NativeFrameSize + masm.framePushed()), argv); 1.6100 +#else 1.6101 + masm.movePtr(IntArgReg0, argv); 1.6102 +#endif 1.6103 + masm.Push(argv); 1.6104 + 1.6105 + // Bump the stack for the call. 1.6106 + const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name()); 1.6107 + unsigned stackDec = StackDecrementForCall(masm, func.sig().args()); 1.6108 + masm.reserveStack(stackDec); 1.6109 + 1.6110 + // Copy parameters out of argv and into the registers/stack-slots specified by 1.6111 + // the system ABI. 1.6112 + for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) { 1.6113 + unsigned argOffset = iter.index() * sizeof(uint64_t); 1.6114 + Address src(argv, argOffset); 1.6115 + switch (iter->kind()) { 1.6116 + case ABIArg::GPR: 1.6117 + masm.load32(src, iter->gpr()); 1.6118 + break; 1.6119 + case ABIArg::FPU: 1.6120 + masm.loadDouble(src, iter->fpu()); 1.6121 + break; 1.6122 + case ABIArg::Stack: 1.6123 + if (iter.mirType() == MIRType_Int32) { 1.6124 + masm.load32(src, scratch); 1.6125 + masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase())); 1.6126 + } else { 1.6127 + JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32); 1.6128 + masm.loadDouble(src, ScratchFloatReg); 1.6129 + masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase())); 1.6130 + } 1.6131 + break; 1.6132 + } 1.6133 + } 1.6134 + 1.6135 + // Call into the real function. 1.6136 + AssertStackAlignment(masm); 1.6137 + masm.call(CallSiteDesc::Entry(), func.code()); 1.6138 + 1.6139 + // Pop the stack and recover the original 'argv' argument passed to the 1.6140 + // trampoline (which was pushed on the stack). 1.6141 + masm.freeStack(stackDec); 1.6142 + masm.Pop(argv); 1.6143 + 1.6144 + // Store the return value in argv[0] 1.6145 + switch (func.sig().retType().which()) { 1.6146 + case RetType::Void: 1.6147 + break; 1.6148 + case RetType::Signed: 1.6149 + masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); 1.6150 + break; 1.6151 + case RetType::Float: 1.6152 + masm.convertFloat32ToDouble(ReturnFloatReg, ReturnFloatReg); 1.6153 + // Fall through as ReturnFloatReg now contains a Double 1.6154 + case RetType::Double: 1.6155 + masm.canonicalizeDouble(ReturnFloatReg); 1.6156 + masm.storeDouble(ReturnFloatReg, Address(argv, 0)); 1.6157 + break; 1.6158 + } 1.6159 + 1.6160 + // Restore clobbered non-volatile registers of the caller. 1.6161 + masm.PopRegsInMask(NonVolatileRegs); 1.6162 + 1.6163 + JS_ASSERT(masm.framePushed() == 0); 1.6164 + 1.6165 + masm.move32(Imm32(true), ReturnReg); 1.6166 + masm.abiret(); 1.6167 + return true; 1.6168 +} 1.6169 + 1.6170 +static inline bool 1.6171 +TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex, 1.6172 + int32_t argc, Value *argv) 1.6173 +{ 1.6174 + if (!fun->hasScript()) 1.6175 + return true; 1.6176 + 1.6177 + // Test if the function is Ion compiled 1.6178 + JSScript *script = fun->nonLazyScript(); 1.6179 + if (!script->hasIonScript()) 1.6180 + return true; 1.6181 + 1.6182 + // Currently we can't rectify arguments. Therefore disabling if argc is too low. 1.6183 + if (fun->nargs() > size_t(argc)) 1.6184 + return true; 1.6185 + 1.6186 + // Normally the types should corresond, since we just ran with those types, 1.6187 + // but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only. 1.6188 + if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType())) 1.6189 + return true; 1.6190 + for(uint32_t i = 0; i < fun->nargs(); i++) { 1.6191 + types::StackTypeSet *typeset = types::TypeScript::ArgTypes(script, i); 1.6192 + types::Type type = types::Type::DoubleType(); 1.6193 + if (!argv[i].isDouble()) 1.6194 + type = types::Type::PrimitiveType(argv[i].extractNonDoubleType()); 1.6195 + if (!typeset->hasType(type)) 1.6196 + return true; 1.6197 + } 1.6198 + 1.6199 + // Enable 1.6200 + IonScript *ionScript = script->ionScript(); 1.6201 + if (!ionScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex))) 1.6202 + return false; 1.6203 + 1.6204 + module.exitIndexToGlobalDatum(exitIndex).exit = module.ionExitTrampoline(module.exit(exitIndex)); 1.6205 + return true; 1.6206 +} 1.6207 + 1.6208 +namespace js { 1.6209 + 1.6210 +int32_t 1.6211 +InvokeFromAsmJS_Ignore(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv) 1.6212 +{ 1.6213 + AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module(); 1.6214 + 1.6215 + RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun); 1.6216 + RootedValue fval(cx, ObjectValue(*fun)); 1.6217 + RootedValue rval(cx); 1.6218 + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval)) 1.6219 + return false; 1.6220 + 1.6221 + if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv)) 1.6222 + return false; 1.6223 + 1.6224 + return true; 1.6225 +} 1.6226 + 1.6227 +int32_t 1.6228 +InvokeFromAsmJS_ToInt32(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv) 1.6229 +{ 1.6230 + AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module(); 1.6231 + 1.6232 + RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun); 1.6233 + RootedValue fval(cx, ObjectValue(*fun)); 1.6234 + RootedValue rval(cx); 1.6235 + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval)) 1.6236 + return false; 1.6237 + 1.6238 + if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv)) 1.6239 + return false; 1.6240 + 1.6241 + int32_t i32; 1.6242 + if (!ToInt32(cx, rval, &i32)) 1.6243 + return false; 1.6244 + argv[0] = Int32Value(i32); 1.6245 + 1.6246 + return true; 1.6247 +} 1.6248 + 1.6249 +int32_t 1.6250 +InvokeFromAsmJS_ToNumber(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv) 1.6251 +{ 1.6252 + AsmJSModule &module = cx->mainThread().asmJSActivationStackFromOwnerThread()->module(); 1.6253 + 1.6254 + RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun); 1.6255 + RootedValue fval(cx, ObjectValue(*fun)); 1.6256 + RootedValue rval(cx); 1.6257 + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, &rval)) 1.6258 + return false; 1.6259 + 1.6260 + if (!TryEnablingIon(cx, module, fun, exitIndex, argc, argv)) 1.6261 + return false; 1.6262 + 1.6263 + double dbl; 1.6264 + if (!ToNumber(cx, rval, &dbl)) 1.6265 + return false; 1.6266 + argv[0] = DoubleValue(dbl); 1.6267 + 1.6268 + return true; 1.6269 +} 1.6270 + 1.6271 +} // namespace js 1.6272 + 1.6273 +static void 1.6274 +FillArgumentArray(ModuleCompiler &m, const VarTypeVector &argTypes, 1.6275 + unsigned offsetToArgs, unsigned offsetToCallerStackArgs, 1.6276 + Register scratch) 1.6277 +{ 1.6278 + MacroAssembler &masm = m.masm(); 1.6279 + 1.6280 + for (ABIArgTypeIter i(argTypes); !i.done(); i++) { 1.6281 + Address dstAddr = Address(StackPointer, offsetToArgs + i.index() * sizeof(Value)); 1.6282 + switch (i->kind()) { 1.6283 + case ABIArg::GPR: 1.6284 + masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr); 1.6285 + break; 1.6286 + case ABIArg::FPU: { 1.6287 + masm.canonicalizeDouble(i->fpu()); 1.6288 + masm.storeDouble(i->fpu(), dstAddr); 1.6289 + break; 1.6290 + } 1.6291 + case ABIArg::Stack: 1.6292 + if (i.mirType() == MIRType_Int32) { 1.6293 + Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); 1.6294 +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 1.6295 + masm.load32(src, scratch); 1.6296 + masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr); 1.6297 +#else 1.6298 + masm.memIntToValue(src, dstAddr); 1.6299 +#endif 1.6300 + } else { 1.6301 + JS_ASSERT(i.mirType() == MIRType_Double); 1.6302 + Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); 1.6303 + masm.loadDouble(src, ScratchFloatReg); 1.6304 + masm.canonicalizeDouble(ScratchFloatReg); 1.6305 + masm.storeDouble(ScratchFloatReg, dstAddr); 1.6306 + } 1.6307 + break; 1.6308 + } 1.6309 + } 1.6310 +} 1.6311 + 1.6312 +static void 1.6313 +GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, 1.6314 + unsigned exitIndex, Label *throwLabel) 1.6315 +{ 1.6316 + MacroAssembler &masm = m.masm(); 1.6317 + masm.align(CodeAlignment); 1.6318 + m.setInterpExitOffset(exitIndex); 1.6319 + masm.setFramePushed(0); 1.6320 +#if defined(JS_CODEGEN_ARM) 1.6321 + masm.Push(lr); 1.6322 +#endif 1.6323 + 1.6324 + MIRType typeArray[] = { MIRType_Pointer, // cx 1.6325 + MIRType_Pointer, // exitDatum 1.6326 + MIRType_Int32, // argc 1.6327 + MIRType_Pointer }; // argv 1.6328 + MIRTypeVector invokeArgTypes(m.cx()); 1.6329 + invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray)); 1.6330 + 1.6331 + // The stack layout looks like: 1.6332 + // | return address | stack arguments | array of values | 1.6333 + unsigned arraySize = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value); 1.6334 + unsigned stackDec = StackDecrementForCall(masm, invokeArgTypes, arraySize + MaybeRetAddr); 1.6335 + masm.reserveStack(stackDec); 1.6336 + 1.6337 + // Fill the argument array. 1.6338 + unsigned offsetToCallerStackArgs = AlignmentAtPrologue + masm.framePushed(); 1.6339 + unsigned offsetToArgv = StackArgBytes(invokeArgTypes) + MaybeRetAddr; 1.6340 + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6341 + FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch); 1.6342 + 1.6343 + // Prepare the arguments for the call to InvokeFromAsmJS_*. 1.6344 + ABIArgMIRTypeIter i(invokeArgTypes); 1.6345 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg1; 1.6346 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6347 + 1.6348 + // Record sp in the AsmJSActivation for stack-walking. 1.6349 + masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP())); 1.6350 + 1.6351 + // argument 0: cx 1.6352 + if (i->kind() == ABIArg::GPR) { 1.6353 + LoadJSContextFromActivation(masm, activation, i->gpr()); 1.6354 + } else { 1.6355 + LoadJSContextFromActivation(masm, activation, scratch); 1.6356 + masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); 1.6357 + } 1.6358 + i++; 1.6359 + 1.6360 + // argument 1: exitIndex 1.6361 + if (i->kind() == ABIArg::GPR) 1.6362 + masm.mov(ImmWord(exitIndex), i->gpr()); 1.6363 + else 1.6364 + masm.store32(Imm32(exitIndex), Address(StackPointer, i->offsetFromArgBase())); 1.6365 + i++; 1.6366 + 1.6367 + // argument 2: argc 1.6368 + unsigned argc = exit.sig().args().length(); 1.6369 + if (i->kind() == ABIArg::GPR) 1.6370 + masm.mov(ImmWord(argc), i->gpr()); 1.6371 + else 1.6372 + masm.store32(Imm32(argc), Address(StackPointer, i->offsetFromArgBase())); 1.6373 + i++; 1.6374 + 1.6375 + // argument 3: argv 1.6376 + Address argv(StackPointer, offsetToArgv); 1.6377 + if (i->kind() == ABIArg::GPR) { 1.6378 + masm.computeEffectiveAddress(argv, i->gpr()); 1.6379 + } else { 1.6380 + masm.computeEffectiveAddress(argv, scratch); 1.6381 + masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); 1.6382 + } 1.6383 + i++; 1.6384 + JS_ASSERT(i.done()); 1.6385 + 1.6386 + // Make the call, test whether it succeeded, and extract the return value. 1.6387 + AssertStackAlignment(masm); 1.6388 + switch (exit.sig().retType().which()) { 1.6389 + case RetType::Void: 1.6390 + masm.callExit(AsmJSImm_InvokeFromAsmJS_Ignore, i.stackBytesConsumedSoFar()); 1.6391 + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); 1.6392 + break; 1.6393 + case RetType::Signed: 1.6394 + masm.callExit(AsmJSImm_InvokeFromAsmJS_ToInt32, i.stackBytesConsumedSoFar()); 1.6395 + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); 1.6396 + masm.unboxInt32(argv, ReturnReg); 1.6397 + break; 1.6398 + case RetType::Double: 1.6399 + masm.callExit(AsmJSImm_InvokeFromAsmJS_ToNumber, i.stackBytesConsumedSoFar()); 1.6400 + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); 1.6401 + masm.loadDouble(argv, ReturnFloatReg); 1.6402 + break; 1.6403 + case RetType::Float: 1.6404 + MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI"); 1.6405 + break; 1.6406 + } 1.6407 + 1.6408 + // Note: the caller is IonMonkey code which means there are no non-volatile 1.6409 + // registers to restore. 1.6410 + masm.freeStack(stackDec); 1.6411 + masm.ret(); 1.6412 +} 1.6413 + 1.6414 +static void 1.6415 +GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel) 1.6416 +{ 1.6417 + MacroAssembler &masm = m.masm(); 1.6418 + 1.6419 + MIRType typeArray[] = { MIRType_Pointer, // cx 1.6420 + MIRType_Pointer }; // argv 1.6421 + MIRTypeVector callArgTypes(m.cx()); 1.6422 + callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray)); 1.6423 + 1.6424 + // The stack is assumed to be aligned. The frame is allocated by GenerateFFIIonExit and 1.6425 + // the stack usage here needs to kept in sync with GenerateFFIIonExit. 1.6426 + 1.6427 + // Store value 1.6428 + unsigned offsetToArgv = StackArgBytes(callArgTypes) + MaybeRetAddr; 1.6429 + masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToArgv)); 1.6430 + 1.6431 + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6432 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg1; 1.6433 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6434 + 1.6435 + // Record sp in the AsmJSActivation for stack-walking. 1.6436 + masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP())); 1.6437 + 1.6438 + // Store real arguments 1.6439 + ABIArgMIRTypeIter i(callArgTypes); 1.6440 + 1.6441 + // argument 0: cx 1.6442 + if (i->kind() == ABIArg::GPR) { 1.6443 + LoadJSContextFromActivation(masm, activation, i->gpr()); 1.6444 + } else { 1.6445 + LoadJSContextFromActivation(masm, activation, scratch); 1.6446 + masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); 1.6447 + } 1.6448 + i++; 1.6449 + 1.6450 + // argument 1: argv 1.6451 + Address argv(StackPointer, offsetToArgv); 1.6452 + if (i->kind() == ABIArg::GPR) { 1.6453 + masm.computeEffectiveAddress(argv, i->gpr()); 1.6454 + } else { 1.6455 + masm.computeEffectiveAddress(argv, scratch); 1.6456 + masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); 1.6457 + } 1.6458 + i++; 1.6459 + JS_ASSERT(i.done()); 1.6460 + 1.6461 + // Call 1.6462 + AssertStackAlignment(masm); 1.6463 + switch (retType.which()) { 1.6464 + case RetType::Signed: 1.6465 + masm.callExit(AsmJSImm_CoerceInPlace_ToInt32, i.stackBytesConsumedSoFar()); 1.6466 + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); 1.6467 + masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg); 1.6468 + break; 1.6469 + case RetType::Double: 1.6470 + masm.callExit(AsmJSImm_CoerceInPlace_ToNumber, i.stackBytesConsumedSoFar()); 1.6471 + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); 1.6472 + masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnFloatReg); 1.6473 + break; 1.6474 + default: 1.6475 + MOZ_ASSUME_UNREACHABLE("Unsupported convert type"); 1.6476 + } 1.6477 +} 1.6478 + 1.6479 +static void 1.6480 +GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, 1.6481 + unsigned exitIndex, Label *throwLabel) 1.6482 +{ 1.6483 + MacroAssembler &masm = m.masm(); 1.6484 + masm.align(CodeAlignment); 1.6485 + m.setIonExitOffset(exitIndex); 1.6486 + masm.setFramePushed(0); 1.6487 + 1.6488 +#if defined(JS_CODEGEN_X64) 1.6489 + masm.Push(HeapReg); 1.6490 +#elif defined(JS_CODEGEN_ARM) 1.6491 + // The lr register holds the return address and needs to be saved. The GlobalReg 1.6492 + // (r10) and HeapReg (r11) also need to be restored before returning to asm.js code. 1.6493 + // The NANReg also needs to be restored, but is a constant and is reloaded before 1.6494 + // returning to asm.js code. 1.6495 + masm.PushRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) | 1.6496 + (1<<HeapReg.code()) | 1.6497 + (1<<lr.code())), 1.6498 + FloatRegisterSet(uint32_t(0)))); 1.6499 +#endif 1.6500 + 1.6501 + // The stack frame is used for the call into Ion and also for calls into C for OOL 1.6502 + // conversion of the result. A frame large enough for both is allocated. 1.6503 + // 1.6504 + // Arguments to the Ion function are in the following order on the stack: 1.6505 + // | return address | descriptor | callee | argc | this | arg1 | arg2 | ... 1.6506 + unsigned argBytes = 3 * sizeof(size_t) + (1 + exit.sig().args().length()) * sizeof(Value); 1.6507 + unsigned offsetToArgs = MaybeRetAddr; 1.6508 + unsigned stackDecForIonCall = StackDecrementForCall(masm, argBytes + offsetToArgs); 1.6509 + 1.6510 + // Reserve space for a call to AsmJSImm_CoerceInPlace_* and an array of values used by 1.6511 + // OOLConvert which reuses the same frame. This code needs to be kept in sync with the 1.6512 + // stack usage in GenerateOOLConvert. 1.6513 + MIRType typeArray[] = { MIRType_Pointer, MIRType_Pointer }; // cx, argv 1.6514 + MIRTypeVector callArgTypes(m.cx()); 1.6515 + callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray)); 1.6516 + unsigned oolExtraBytes = sizeof(Value) + MaybeRetAddr; 1.6517 + unsigned stackDecForOOLCall = StackDecrementForCall(masm, callArgTypes, oolExtraBytes); 1.6518 + 1.6519 + // Allocate a frame large enough for both of the above calls. 1.6520 + unsigned stackDec = Max(stackDecForIonCall, stackDecForOOLCall); 1.6521 + 1.6522 + masm.reserveStack(stackDec); 1.6523 + AssertStackAlignment(masm); 1.6524 + 1.6525 + // 1. Descriptor 1.6526 + size_t argOffset = offsetToArgs; 1.6527 + uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_Entry); 1.6528 + masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset)); 1.6529 + argOffset += sizeof(size_t); 1.6530 + 1.6531 + // 2. Callee 1.6532 + Register callee = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6533 + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; 1.6534 + 1.6535 + // 2.1. Get ExitDatum 1.6536 + unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex); 1.6537 +#if defined(JS_CODEGEN_X64) 1.6538 + CodeOffsetLabel label2 = masm.leaRipRelative(callee); 1.6539 + m.masm().append(AsmJSGlobalAccess(label2.offset(), globalDataOffset)); 1.6540 +#elif defined(JS_CODEGEN_X86) 1.6541 + CodeOffsetLabel label2 = masm.movlWithPatch(Imm32(0), callee); 1.6542 + m.masm().append(AsmJSGlobalAccess(label2.offset(), globalDataOffset)); 1.6543 +#else 1.6544 + masm.lea(Operand(GlobalReg, globalDataOffset), callee); 1.6545 +#endif 1.6546 + 1.6547 + // 2.2. Get callee 1.6548 + masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee); 1.6549 + 1.6550 + // 2.3. Save callee 1.6551 + masm.storePtr(callee, Address(StackPointer, argOffset)); 1.6552 + argOffset += sizeof(size_t); 1.6553 + 1.6554 + // 3. Argc 1.6555 + unsigned argc = exit.sig().args().length(); 1.6556 + masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset)); 1.6557 + argOffset += sizeof(size_t); 1.6558 + 1.6559 + // 4. |this| value 1.6560 + masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset)); 1.6561 + argOffset += sizeof(Value); 1.6562 + 1.6563 + // 5. Fill the arguments 1.6564 + unsigned offsetToCallerStackArgs = masm.framePushed(); 1.6565 +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 1.6566 + offsetToCallerStackArgs += NativeFrameSize; 1.6567 +#endif 1.6568 + FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch); 1.6569 + argOffset += exit.sig().args().length() * sizeof(Value); 1.6570 + JS_ASSERT(argOffset == offsetToArgs + argBytes); 1.6571 + 1.6572 + // Get the pointer to the ion code 1.6573 + Label done, oolConvert; 1.6574 + Label *maybeDebugBreakpoint = nullptr; 1.6575 + 1.6576 +#ifdef DEBUG 1.6577 + Label ionFailed; 1.6578 + maybeDebugBreakpoint = &ionFailed; 1.6579 + masm.branchIfFunctionHasNoScript(callee, &ionFailed); 1.6580 +#endif 1.6581 + 1.6582 + masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee); 1.6583 + masm.loadBaselineOrIonNoArgCheck(callee, callee, SequentialExecution, maybeDebugBreakpoint); 1.6584 + 1.6585 + AssertStackAlignment(masm); 1.6586 + 1.6587 + { 1.6588 + // Enable Activation. 1.6589 + // 1.6590 + // This sequence requires four registers, and needs to preserve the 'callee' 1.6591 + // register, so there are five live registers. 1.6592 + JS_ASSERT(callee == AsmJSIonExitRegCallee); 1.6593 + Register reg0 = AsmJSIonExitRegE0; 1.6594 + Register reg1 = AsmJSIonExitRegE1; 1.6595 + Register reg2 = AsmJSIonExitRegE2; 1.6596 + Register reg3 = AsmJSIonExitRegE3; 1.6597 + 1.6598 + LoadAsmJSActivationIntoRegister(masm, reg0); 1.6599 + 1.6600 + // Record sp in the AsmJSActivation for stack-walking. 1.6601 + masm.storePtr(StackPointer, Address(reg0, AsmJSActivation::offsetOfExitSP())); 1.6602 + 1.6603 + // The following is inlined: 1.6604 + // JSContext *cx = activation->cx(); 1.6605 + // Activation *act = cx->mainThread().activation(); 1.6606 + // act.active_ = true; 1.6607 + // act.prevIonTop_ = cx->mainThread().ionTop; 1.6608 + // act.prevJitJSContext_ = cx->mainThread().jitJSContext; 1.6609 + // cx->mainThread().jitJSContext = cx; 1.6610 + // On the ARM store8() uses the secondScratchReg (lr) as a temp. 1.6611 + size_t offsetOfActivation = offsetof(JSRuntime, mainThread) + 1.6612 + PerThreadData::offsetOfActivation(); 1.6613 + size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop); 1.6614 + size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) + 1.6615 + offsetof(PerThreadData, jitJSContext); 1.6616 + masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3); 1.6617 + masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0); 1.6618 + masm.loadPtr(Address(reg0, offsetOfActivation), reg1); 1.6619 + masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8())); 1.6620 + masm.loadPtr(Address(reg0, offsetOfIonTop), reg2); 1.6621 + masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevIonTop())); 1.6622 + masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2); 1.6623 + masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext())); 1.6624 + masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext)); 1.6625 + } 1.6626 + 1.6627 + // 2. Call 1.6628 + AssertStackAlignment(masm); 1.6629 + masm.callIonFromAsmJS(callee); 1.6630 + AssertStackAlignment(masm); 1.6631 + 1.6632 + { 1.6633 + // Disable Activation. 1.6634 + // 1.6635 + // This sequence needs three registers, and must preserve the JSReturnReg_Data and 1.6636 + // JSReturnReg_Type, so there are five live registers. 1.6637 + JS_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData); 1.6638 + JS_ASSERT(JSReturnReg_Type == AsmJSIonExitRegReturnType); 1.6639 + Register reg0 = AsmJSIonExitRegD0; 1.6640 + Register reg1 = AsmJSIonExitRegD1; 1.6641 + Register reg2 = AsmJSIonExitRegD2; 1.6642 + 1.6643 + LoadAsmJSActivationIntoRegister(masm, reg0); 1.6644 + 1.6645 + // The following is inlined: 1.6646 + // JSContext *cx = activation->cx(); 1.6647 + // Activation *act = cx->mainThread().activation(); 1.6648 + // act.active_ = false; 1.6649 + // cx->mainThread().ionTop = prevIonTop_; 1.6650 + // cx->mainThread().jitJSContext = prevJitJSContext_; 1.6651 + // On the ARM store8() uses the secondScratchReg (lr) as a temp. 1.6652 + size_t offsetOfActivation = offsetof(JSRuntime, mainThread) + 1.6653 + PerThreadData::offsetOfActivation(); 1.6654 + size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop); 1.6655 + size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) + 1.6656 + offsetof(PerThreadData, jitJSContext); 1.6657 + masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg0); 1.6658 + masm.loadPtr(Address(reg0, JSContext::offsetOfRuntime()), reg0); 1.6659 + masm.loadPtr(Address(reg0, offsetOfActivation), reg1); 1.6660 + masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8())); 1.6661 + masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevIonTop()), reg2); 1.6662 + masm.storePtr(reg2, Address(reg0, offsetOfIonTop)); 1.6663 + masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2); 1.6664 + masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext)); 1.6665 + } 1.6666 + 1.6667 +#ifdef DEBUG 1.6668 + masm.branchTestMagicValue(Assembler::Equal, JSReturnOperand, JS_ION_ERROR, throwLabel); 1.6669 + masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &ionFailed); 1.6670 +#else 1.6671 + masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel); 1.6672 +#endif 1.6673 + 1.6674 + uint32_t oolConvertFramePushed = masm.framePushed(); 1.6675 + switch (exit.sig().retType().which()) { 1.6676 + case RetType::Void: 1.6677 + break; 1.6678 + case RetType::Signed: 1.6679 + masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert, 1.6680 + /* -0 check */ false); 1.6681 + break; 1.6682 + case RetType::Double: 1.6683 + masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert); 1.6684 + break; 1.6685 + case RetType::Float: 1.6686 + MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI"); 1.6687 + break; 1.6688 + } 1.6689 + 1.6690 + masm.bind(&done); 1.6691 + masm.freeStack(stackDec); 1.6692 +#if defined(JS_CODEGEN_ARM) 1.6693 + masm.ma_vimm(GenericNaN(), NANReg); 1.6694 + masm.PopRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) | 1.6695 + (1<<HeapReg.code()) | 1.6696 + (1<<pc.code())), 1.6697 + FloatRegisterSet(uint32_t(0)))); 1.6698 +#else 1.6699 +# if defined(JS_CODEGEN_X64) 1.6700 + masm.Pop(HeapReg); 1.6701 +# endif 1.6702 + masm.ret(); 1.6703 +#endif 1.6704 + JS_ASSERT(masm.framePushed() == 0); 1.6705 + 1.6706 + // oolConvert 1.6707 + if (oolConvert.used()) { 1.6708 + masm.bind(&oolConvert); 1.6709 + masm.setFramePushed(oolConvertFramePushed); 1.6710 + GenerateOOLConvert(m, exit.sig().retType(), throwLabel); 1.6711 + masm.setFramePushed(0); 1.6712 + masm.jump(&done); 1.6713 + } 1.6714 + 1.6715 +#ifdef DEBUG 1.6716 + masm.bind(&ionFailed); 1.6717 + masm.assumeUnreachable("AsmJS to IonMonkey call failed."); 1.6718 +#endif 1.6719 +} 1.6720 + 1.6721 +// See "asm.js FFI calls" comment above. 1.6722 +static void 1.6723 +GenerateFFIExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex, 1.6724 + Label *throwLabel) 1.6725 +{ 1.6726 + // Generate the slow path through the interpreter 1.6727 + GenerateFFIInterpreterExit(m, exit, exitIndex, throwLabel); 1.6728 + 1.6729 + // Generate the fast path 1.6730 + GenerateFFIIonExit(m, exit, exitIndex, throwLabel); 1.6731 +} 1.6732 + 1.6733 +// The stack-overflow exit is called when the stack limit has definitely been 1.6734 +// exceeded. In this case, we can clobber everything since we are about to pop 1.6735 +// all the frames. 1.6736 +static bool 1.6737 +GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel) 1.6738 +{ 1.6739 + MacroAssembler &masm = m.masm(); 1.6740 + masm.align(CodeAlignment); 1.6741 + masm.bind(&m.stackOverflowLabel()); 1.6742 + 1.6743 + // The overflow check always occurs before the initial function-specific 1.6744 + // stack-size adjustment. See CodeGenerator::generateAsmJSPrologue. 1.6745 + masm.setFramePushed(AlignmentMidPrologue - AlignmentAtPrologue); 1.6746 + 1.6747 + MIRTypeVector argTypes(m.cx()); 1.6748 + argTypes.infallibleAppend(MIRType_Pointer); // cx 1.6749 + 1.6750 + unsigned stackDec = StackDecrementForCall(masm, argTypes, MaybeRetAddr); 1.6751 + masm.reserveStack(stackDec); 1.6752 + 1.6753 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6754 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6755 + 1.6756 + // Record sp in the AsmJSActivation for stack-walking. 1.6757 + masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP())); 1.6758 + 1.6759 + ABIArgMIRTypeIter i(argTypes); 1.6760 + 1.6761 + // argument 0: cx 1.6762 + if (i->kind() == ABIArg::GPR) { 1.6763 + LoadJSContextFromActivation(masm, activation, i->gpr()); 1.6764 + } else { 1.6765 + LoadJSContextFromActivation(masm, activation, activation); 1.6766 + masm.storePtr(activation, Address(StackPointer, i->offsetFromArgBase())); 1.6767 + } 1.6768 + i++; 1.6769 + 1.6770 + JS_ASSERT(i.done()); 1.6771 + 1.6772 + AssertStackAlignment(masm); 1.6773 + masm.callExit(AsmJSImm_ReportOverRecursed, i.stackBytesConsumedSoFar()); 1.6774 + 1.6775 + // Don't worry about restoring the stack; throwLabel will pop everything. 1.6776 + masm.jump(throwLabel); 1.6777 + return !masm.oom(); 1.6778 +} 1.6779 + 1.6780 +// The operation-callback exit is called from arbitrarily-interrupted asm.js 1.6781 +// code. That means we must first save *all* registers and restore *all* 1.6782 +// registers (except the stack pointer) when we resume. The address to resume to 1.6783 +// (assuming that js::HandleExecutionInterrupt doesn't indicate that the 1.6784 +// execution should be aborted) is stored in AsmJSActivation::resumePC_. 1.6785 +// Unfortunately, loading this requires a scratch register which we don't have 1.6786 +// after restoring all registers. To hack around this, push the resumePC on the 1.6787 +// stack so that it can be popped directly into PC. 1.6788 +static bool 1.6789 +GenerateInterruptExit(ModuleCompiler &m, Label *throwLabel) 1.6790 +{ 1.6791 + MacroAssembler &masm = m.masm(); 1.6792 + masm.align(CodeAlignment); 1.6793 + masm.bind(&m.interruptLabel()); 1.6794 + 1.6795 +#ifndef JS_CODEGEN_ARM 1.6796 + // Be very careful here not to perturb the machine state before saving it 1.6797 + // to the stack. In particular, add/sub instructions may set conditions in 1.6798 + // the flags register. 1.6799 + masm.push(Imm32(0)); // space for resumePC 1.6800 + masm.pushFlags(); // after this we are safe to use sub 1.6801 + masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below 1.6802 + masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP) 1.6803 + 1.6804 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6805 + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; 1.6806 + 1.6807 + // Store resumePC into the reserved space. 1.6808 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6809 + masm.loadPtr(Address(activation, AsmJSActivation::offsetOfResumePC()), scratch); 1.6810 + masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*))); 1.6811 + 1.6812 + // We know that StackPointer is word-aligned, but not necessarily 1.6813 + // stack-aligned, so we need to align it dynamically. 1.6814 + masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg); 1.6815 +#if defined(JS_CODEGEN_X86) 1.6816 + // Ensure that at least one slot is pushed for passing 'cx' below. 1.6817 + masm.push(Imm32(0)); 1.6818 +#endif 1.6819 + masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer); 1.6820 + if (ShadowStackSpace) 1.6821 + masm.subPtr(Imm32(ShadowStackSpace), StackPointer); 1.6822 + 1.6823 + // argument 0: cx 1.6824 +#if defined(JS_CODEGEN_X86) 1.6825 + LoadJSContextFromActivation(masm, activation, scratch); 1.6826 + masm.storePtr(scratch, Address(StackPointer, 0)); 1.6827 +#elif defined(JS_CODEGEN_X64) 1.6828 + LoadJSContextFromActivation(masm, activation, IntArgReg0); 1.6829 +#endif 1.6830 + 1.6831 + masm.call(AsmJSImm_HandleExecutionInterrupt); 1.6832 + masm.branchIfFalseBool(ReturnReg, throwLabel); 1.6833 + 1.6834 + // Restore the StackPointer to it's position before the call. 1.6835 + masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer); 1.6836 + 1.6837 + // Restore the machine state to before the interrupt. 1.6838 + masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP) 1.6839 + masm.popFlags(); // after this, nothing that sets conditions 1.6840 + masm.ret(); // pop resumePC into PC 1.6841 +#else 1.6842 + masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below 1.6843 + masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)), FloatRegisterSet(uint32_t(0)))); // save all GP registers,excep sp 1.6844 + 1.6845 + // Save both the APSR and FPSCR in non-volatile registers. 1.6846 + masm.as_mrs(r4); 1.6847 + masm.as_vmrs(r5); 1.6848 + // Save the stack pointer in a non-volatile register. 1.6849 + masm.mov(sp,r6); 1.6850 + // Align the stack. 1.6851 + masm.ma_and(Imm32(~7), sp, sp); 1.6852 + 1.6853 + // Store resumePC into the return PC stack slot. 1.6854 + LoadAsmJSActivationIntoRegister(masm, IntArgReg0); 1.6855 + masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1); 1.6856 + masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*))); 1.6857 + 1.6858 + // argument 0: cx 1.6859 + masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfContext()), IntArgReg0); 1.6860 + 1.6861 + masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllMask))); // save all FP registers 1.6862 + masm.call(AsmJSImm_HandleExecutionInterrupt); 1.6863 + masm.branchIfFalseBool(ReturnReg, throwLabel); 1.6864 + 1.6865 + // Restore the machine state to before the interrupt. this will set the pc! 1.6866 + masm.PopRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllMask))); // restore all FP registers 1.6867 + masm.mov(r6,sp); 1.6868 + masm.as_vmsr(r5); 1.6869 + masm.as_msr(r4); 1.6870 + // Restore all GP registers 1.6871 + masm.startDataTransferM(IsLoad, sp, IA, WriteBack); 1.6872 + masm.transferReg(r0); 1.6873 + masm.transferReg(r1); 1.6874 + masm.transferReg(r2); 1.6875 + masm.transferReg(r3); 1.6876 + masm.transferReg(r4); 1.6877 + masm.transferReg(r5); 1.6878 + masm.transferReg(r6); 1.6879 + masm.transferReg(r7); 1.6880 + masm.transferReg(r8); 1.6881 + masm.transferReg(r9); 1.6882 + masm.transferReg(r10); 1.6883 + masm.transferReg(r11); 1.6884 + masm.transferReg(r12); 1.6885 + masm.transferReg(lr); 1.6886 + masm.finishDataTransfer(); 1.6887 + masm.ret(); 1.6888 + 1.6889 +#endif 1.6890 + 1.6891 + return !masm.oom(); 1.6892 +} 1.6893 + 1.6894 +// If an exception is thrown, simply pop all frames (since asm.js does not 1.6895 +// contain try/catch). To do this: 1.6896 +// 1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry. 1.6897 +// 2. PopRegsInMask to restore the caller's non-volatile registers. 1.6898 +// 3. Return (to CallAsmJS). 1.6899 +static bool 1.6900 +GenerateThrowExit(ModuleCompiler &m, Label *throwLabel) 1.6901 +{ 1.6902 + MacroAssembler &masm = m.masm(); 1.6903 + masm.align(CodeAlignment); 1.6904 + masm.bind(throwLabel); 1.6905 + 1.6906 + Register activation = ABIArgGenerator::NonArgReturnVolatileReg0; 1.6907 + LoadAsmJSActivationIntoRegister(masm, activation); 1.6908 + 1.6909 + masm.setFramePushed(FramePushedAfterSave); 1.6910 + masm.loadPtr(Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer); 1.6911 + 1.6912 + masm.PopRegsInMask(NonVolatileRegs); 1.6913 + JS_ASSERT(masm.framePushed() == 0); 1.6914 + 1.6915 + masm.mov(ImmWord(0), ReturnReg); 1.6916 + masm.abiret(); 1.6917 + 1.6918 + return !masm.oom(); 1.6919 +} 1.6920 + 1.6921 +static bool 1.6922 +GenerateStubs(ModuleCompiler &m) 1.6923 +{ 1.6924 + for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) { 1.6925 + m.setEntryOffset(i); 1.6926 + if (!GenerateEntry(m, m.module().exportedFunction(i))) 1.6927 + return false; 1.6928 + if (m.masm().oom()) 1.6929 + return false; 1.6930 + } 1.6931 + 1.6932 + Label throwLabel; 1.6933 + 1.6934 + // The order of the iterations here is non-deterministic, since 1.6935 + // m.allExits() is a hash keyed by pointer values! 1.6936 + for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) { 1.6937 + GenerateFFIExit(m, r.front().key(), r.front().value(), &throwLabel); 1.6938 + if (m.masm().oom()) 1.6939 + return false; 1.6940 + } 1.6941 + 1.6942 + if (m.stackOverflowLabel().used()) { 1.6943 + if (!GenerateStackOverflowExit(m, &throwLabel)) 1.6944 + return false; 1.6945 + } 1.6946 + 1.6947 + if (!GenerateInterruptExit(m, &throwLabel)) 1.6948 + return false; 1.6949 + 1.6950 + if (!GenerateThrowExit(m, &throwLabel)) 1.6951 + return false; 1.6952 + 1.6953 + return true; 1.6954 +} 1.6955 + 1.6956 +static bool 1.6957 +FinishModule(ModuleCompiler &m, 1.6958 + ScopedJSDeletePtr<AsmJSModule> *module) 1.6959 +{ 1.6960 + LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE); 1.6961 + TempAllocator alloc(&lifo); 1.6962 + IonContext ionContext(m.cx(), &alloc); 1.6963 + 1.6964 + m.masm().resetForNewCodeGenerator(alloc); 1.6965 + 1.6966 + if (!GenerateStubs(m)) 1.6967 + return false; 1.6968 + 1.6969 + return m.finish(module); 1.6970 +} 1.6971 + 1.6972 +static bool 1.6973 +CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList, 1.6974 + ScopedJSDeletePtr<AsmJSModule> *moduleOut, 1.6975 + ScopedJSFreePtr<char> *compilationTimeReport) 1.6976 +{ 1.6977 + if (!LookupAsmJSModuleInCache(cx, parser, moduleOut, compilationTimeReport)) 1.6978 + return false; 1.6979 + if (*moduleOut) 1.6980 + return true; 1.6981 + 1.6982 + ModuleCompiler m(cx, parser); 1.6983 + if (!m.init()) 1.6984 + return false; 1.6985 + 1.6986 + if (PropertyName *moduleFunctionName = FunctionName(m.moduleFunctionNode())) { 1.6987 + if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName)) 1.6988 + return false; 1.6989 + m.initModuleFunctionName(moduleFunctionName); 1.6990 + } 1.6991 + 1.6992 + if (!CheckFunctionHead(m, m.moduleFunctionNode())) 1.6993 + return false; 1.6994 + 1.6995 + if (!CheckModuleArguments(m, m.moduleFunctionNode())) 1.6996 + return false; 1.6997 + 1.6998 + if (!CheckPrecedingStatements(m, stmtList)) 1.6999 + return false; 1.7000 + 1.7001 + if (!CheckModuleGlobals(m)) 1.7002 + return false; 1.7003 + 1.7004 +#ifdef JS_THREADSAFE 1.7005 + if (!CheckFunctionsParallel(m)) 1.7006 + return false; 1.7007 +#else 1.7008 + if (!CheckFunctionsSequential(m)) 1.7009 + return false; 1.7010 +#endif 1.7011 + 1.7012 + m.finishFunctionBodies(); 1.7013 + 1.7014 + if (!CheckFuncPtrTables(m)) 1.7015 + return false; 1.7016 + 1.7017 + if (!CheckModuleReturn(m)) 1.7018 + return false; 1.7019 + 1.7020 + TokenKind tk = PeekToken(m.parser()); 1.7021 + if (tk != TOK_EOF && tk != TOK_RC) 1.7022 + return m.fail(nullptr, "top-level export (return) must be the last statement"); 1.7023 + 1.7024 + // The instruction cache is flushed when dynamically linking, so can inhibit now. 1.7025 + AutoFlushICache afc("CheckModule", /* inhibit= */ true); 1.7026 + 1.7027 + ScopedJSDeletePtr<AsmJSModule> module; 1.7028 + if (!FinishModule(m, &module)) 1.7029 + return false; 1.7030 + 1.7031 + bool storedInCache = StoreAsmJSModuleInCache(parser, *module, cx); 1.7032 + module->staticallyLink(cx); 1.7033 + 1.7034 + m.buildCompilationTimeReport(storedInCache, compilationTimeReport); 1.7035 + *moduleOut = module.forget(); 1.7036 + return true; 1.7037 +} 1.7038 + 1.7039 +static bool 1.7040 +Warn(AsmJSParser &parser, int errorNumber, const char *str) 1.7041 +{ 1.7042 + parser.reportNoOffset(ParseWarning, /* strict = */ false, errorNumber, str ? str : ""); 1.7043 + return false; 1.7044 +} 1.7045 + 1.7046 +static bool 1.7047 +EstablishPreconditions(ExclusiveContext *cx, AsmJSParser &parser) 1.7048 +{ 1.7049 + if (!cx->jitSupportsFloatingPoint()) 1.7050 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support"); 1.7051 + 1.7052 + if (!cx->signalHandlersInstalled()) 1.7053 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support"); 1.7054 + 1.7055 + if (cx->gcSystemPageSize() != AsmJSPageSize) 1.7056 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size"); 1.7057 + 1.7058 + if (!parser.options().asmJSOption) 1.7059 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config"); 1.7060 + 1.7061 + if (!parser.options().compileAndGo) 1.7062 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Temporarily disabled for event-handler and other cloneable scripts"); 1.7063 + 1.7064 + if (cx->compartment()->debugMode()) 1.7065 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger"); 1.7066 + 1.7067 + if (parser.pc->isGenerator()) 1.7068 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context"); 1.7069 + 1.7070 + if (parser.pc->isArrowFunction()) 1.7071 + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context"); 1.7072 + 1.7073 +#ifdef JS_THREADSAFE 1.7074 + if (ParallelCompilationEnabled(cx)) 1.7075 + EnsureWorkerThreadsInitialized(cx); 1.7076 +#endif 1.7077 + 1.7078 + return true; 1.7079 +} 1.7080 + 1.7081 +static bool 1.7082 +NoExceptionPending(ExclusiveContext *cx) 1.7083 +{ 1.7084 + return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending(); 1.7085 +} 1.7086 + 1.7087 +bool 1.7088 +js::CompileAsmJS(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList, bool *validated) 1.7089 +{ 1.7090 + *validated = false; 1.7091 + 1.7092 + if (!EstablishPreconditions(cx, parser)) 1.7093 + return NoExceptionPending(cx); 1.7094 + 1.7095 + ScopedJSFreePtr<char> compilationTimeReport; 1.7096 + ScopedJSDeletePtr<AsmJSModule> module; 1.7097 + if (!CheckModule(cx, parser, stmtList, &module, &compilationTimeReport)) 1.7098 + return NoExceptionPending(cx); 1.7099 + 1.7100 + RootedObject moduleObj(cx, AsmJSModuleObject::create(cx, &module)); 1.7101 + if (!moduleObj) 1.7102 + return false; 1.7103 + 1.7104 + FunctionBox *funbox = parser.pc->maybeFunction->pn_funbox; 1.7105 + RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj)); 1.7106 + if (!moduleFun) 1.7107 + return false; 1.7108 + 1.7109 + JS_ASSERT(funbox->function()->isInterpreted()); 1.7110 + funbox->object = moduleFun; 1.7111 + 1.7112 + *validated = true; 1.7113 + Warn(parser, JSMSG_USE_ASM_TYPE_OK, compilationTimeReport.get()); 1.7114 + return NoExceptionPending(cx); 1.7115 +} 1.7116 + 1.7117 +bool 1.7118 +js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp) 1.7119 +{ 1.7120 + CallArgs args = CallArgsFromVp(argc, vp); 1.7121 + 1.7122 + // See EstablishPreconditions. 1.7123 + bool available = cx->jitSupportsFloatingPoint() && 1.7124 + cx->signalHandlersInstalled() && 1.7125 + cx->gcSystemPageSize() == AsmJSPageSize && 1.7126 + !cx->compartment()->debugMode() && 1.7127 + cx->runtime()->options().asmJS(); 1.7128 + 1.7129 + args.rval().set(BooleanValue(available)); 1.7130 + return true; 1.7131 +}