Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |