js/src/jit/AsmJS.cpp

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

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

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

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

mercurial