1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 *
4 * Copyright 2014 Mozilla Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include "asmjs/AsmJSValidate.h"
20
21 #include "mozilla/Move.h"
22 #include "mozilla/UniquePtr.h"
23
24 #include "jsmath.h"
25 #include "jsprf.h"
26 #include "jsutil.h"
27
28 #include "asmjs/AsmJSLink.h"
29 #include "asmjs/AsmJSModule.h"
30 #include "asmjs/WasmGenerator.h"
31 #include "builtin/SIMD.h"
32 #include "frontend/Parser.h"
33 #include "jit/AtomicOperations.h"
34 #include "jit/MIR.h"
35 #include "vm/Time.h"
36
37 #include "jsobjinlines.h"
38
39 #include "frontend/ParseNode-inl.h"
40 #include "frontend/Parser-inl.h"
41
42 using namespace js;
43 using namespace js::frontend;
44 using namespace js::jit;
45 using namespace js::wasm;
46
47 using mozilla::HashGeneric;
48 using mozilla::IsNaN;
49 using mozilla::IsNegativeZero;
50 using mozilla::Move;
51 using mozilla::PositiveInfinity;
52 using mozilla::UniquePtr;
53 using JS::AsmJSOption;
54 using JS::GenericNaN;
55
56 /*****************************************************************************/
57 // ParseNode utilities
58
59 static inline ParseNode*
NextNode(ParseNode * pn)60 NextNode(ParseNode* pn)
61 {
62 return pn->pn_next;
63 }
64
65 static inline ParseNode*
UnaryKid(ParseNode * pn)66 UnaryKid(ParseNode* pn)
67 {
68 MOZ_ASSERT(pn->isArity(PN_UNARY));
69 return pn->pn_kid;
70 }
71
72 static inline ParseNode*
BinaryRight(ParseNode * pn)73 BinaryRight(ParseNode* pn)
74 {
75 MOZ_ASSERT(pn->isArity(PN_BINARY));
76 return pn->pn_right;
77 }
78
79 static inline ParseNode*
BinaryLeft(ParseNode * pn)80 BinaryLeft(ParseNode* pn)
81 {
82 MOZ_ASSERT(pn->isArity(PN_BINARY));
83 return pn->pn_left;
84 }
85
86 static inline ParseNode*
ReturnExpr(ParseNode * pn)87 ReturnExpr(ParseNode* pn)
88 {
89 MOZ_ASSERT(pn->isKind(PNK_RETURN));
90 return UnaryKid(pn);
91 }
92
93 static inline ParseNode*
TernaryKid1(ParseNode * pn)94 TernaryKid1(ParseNode* pn)
95 {
96 MOZ_ASSERT(pn->isArity(PN_TERNARY));
97 return pn->pn_kid1;
98 }
99
100 static inline ParseNode*
TernaryKid2(ParseNode * pn)101 TernaryKid2(ParseNode* pn)
102 {
103 MOZ_ASSERT(pn->isArity(PN_TERNARY));
104 return pn->pn_kid2;
105 }
106
107 static inline ParseNode*
TernaryKid3(ParseNode * pn)108 TernaryKid3(ParseNode* pn)
109 {
110 MOZ_ASSERT(pn->isArity(PN_TERNARY));
111 return pn->pn_kid3;
112 }
113
114 static inline ParseNode*
ListHead(ParseNode * pn)115 ListHead(ParseNode* pn)
116 {
117 MOZ_ASSERT(pn->isArity(PN_LIST));
118 return pn->pn_head;
119 }
120
121 static inline unsigned
ListLength(ParseNode * pn)122 ListLength(ParseNode* pn)
123 {
124 MOZ_ASSERT(pn->isArity(PN_LIST));
125 return pn->pn_count;
126 }
127
128 static inline ParseNode*
CallCallee(ParseNode * pn)129 CallCallee(ParseNode* pn)
130 {
131 MOZ_ASSERT(pn->isKind(PNK_CALL));
132 return ListHead(pn);
133 }
134
135 static inline unsigned
CallArgListLength(ParseNode * pn)136 CallArgListLength(ParseNode* pn)
137 {
138 MOZ_ASSERT(pn->isKind(PNK_CALL));
139 MOZ_ASSERT(ListLength(pn) >= 1);
140 return ListLength(pn) - 1;
141 }
142
143 static inline ParseNode*
CallArgList(ParseNode * pn)144 CallArgList(ParseNode* pn)
145 {
146 MOZ_ASSERT(pn->isKind(PNK_CALL));
147 return NextNode(ListHead(pn));
148 }
149
150 static inline ParseNode*
VarListHead(ParseNode * pn)151 VarListHead(ParseNode* pn)
152 {
153 MOZ_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST));
154 return ListHead(pn);
155 }
156
157 static inline bool
IsDefaultCase(ParseNode * pn)158 IsDefaultCase(ParseNode* pn)
159 {
160 return pn->as<CaseClause>().isDefault();
161 }
162
163 static inline ParseNode*
CaseExpr(ParseNode * pn)164 CaseExpr(ParseNode* pn)
165 {
166 return pn->as<CaseClause>().caseExpression();
167 }
168
169 static inline ParseNode*
CaseBody(ParseNode * pn)170 CaseBody(ParseNode* pn)
171 {
172 return pn->as<CaseClause>().statementList();
173 }
174
175 static inline ParseNode*
BinaryOpLeft(ParseNode * pn)176 BinaryOpLeft(ParseNode* pn)
177 {
178 MOZ_ASSERT(pn->isBinaryOperation());
179 MOZ_ASSERT(pn->isArity(PN_LIST));
180 MOZ_ASSERT(pn->pn_count == 2);
181 return ListHead(pn);
182 }
183
184 static inline ParseNode*
BinaryOpRight(ParseNode * pn)185 BinaryOpRight(ParseNode* pn)
186 {
187 MOZ_ASSERT(pn->isBinaryOperation());
188 MOZ_ASSERT(pn->isArity(PN_LIST));
189 MOZ_ASSERT(pn->pn_count == 2);
190 return NextNode(ListHead(pn));
191 }
192
193 static inline ParseNode*
BitwiseLeft(ParseNode * pn)194 BitwiseLeft(ParseNode* pn)
195 {
196 return BinaryOpLeft(pn);
197 }
198
199 static inline ParseNode*
BitwiseRight(ParseNode * pn)200 BitwiseRight(ParseNode* pn)
201 {
202 return BinaryOpRight(pn);
203 }
204
205 static inline ParseNode*
MultiplyLeft(ParseNode * pn)206 MultiplyLeft(ParseNode* pn)
207 {
208 MOZ_ASSERT(pn->isKind(PNK_STAR));
209 return BinaryOpLeft(pn);
210 }
211
212 static inline ParseNode*
MultiplyRight(ParseNode * pn)213 MultiplyRight(ParseNode* pn)
214 {
215 MOZ_ASSERT(pn->isKind(PNK_STAR));
216 return BinaryOpRight(pn);
217 }
218
219 static inline ParseNode*
AddSubLeft(ParseNode * pn)220 AddSubLeft(ParseNode* pn)
221 {
222 MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
223 return BinaryOpLeft(pn);
224 }
225
226 static inline ParseNode*
AddSubRight(ParseNode * pn)227 AddSubRight(ParseNode* pn)
228 {
229 MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
230 return BinaryOpRight(pn);
231 }
232
233 static inline ParseNode*
DivOrModLeft(ParseNode * pn)234 DivOrModLeft(ParseNode* pn)
235 {
236 MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
237 return BinaryOpLeft(pn);
238 }
239
240 static inline ParseNode*
DivOrModRight(ParseNode * pn)241 DivOrModRight(ParseNode* pn)
242 {
243 MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
244 return BinaryOpRight(pn);
245 }
246
247 static inline ParseNode*
ComparisonLeft(ParseNode * pn)248 ComparisonLeft(ParseNode* pn)
249 {
250 return BinaryOpLeft(pn);
251 }
252
253 static inline ParseNode*
ComparisonRight(ParseNode * pn)254 ComparisonRight(ParseNode* pn)
255 {
256 return BinaryOpRight(pn);
257 }
258
259 static inline ParseNode*
AndOrLeft(ParseNode * pn)260 AndOrLeft(ParseNode* pn)
261 {
262 return BinaryOpLeft(pn);
263 }
264
265 static inline ParseNode*
AndOrRight(ParseNode * pn)266 AndOrRight(ParseNode* pn)
267 {
268 return BinaryOpRight(pn);
269 }
270
271 static inline ParseNode*
RelationalLeft(ParseNode * pn)272 RelationalLeft(ParseNode* pn)
273 {
274 return BinaryOpLeft(pn);
275 }
276
277 static inline ParseNode*
RelationalRight(ParseNode * pn)278 RelationalRight(ParseNode* pn)
279 {
280 return BinaryOpRight(pn);
281 }
282
283 static inline bool
IsExpressionStatement(ParseNode * pn)284 IsExpressionStatement(ParseNode* pn)
285 {
286 return pn->isKind(PNK_SEMI);
287 }
288
289 static inline ParseNode*
ExpressionStatementExpr(ParseNode * pn)290 ExpressionStatementExpr(ParseNode* pn)
291 {
292 MOZ_ASSERT(pn->isKind(PNK_SEMI));
293 return UnaryKid(pn);
294 }
295
296 static inline PropertyName*
LoopControlMaybeLabel(ParseNode * pn)297 LoopControlMaybeLabel(ParseNode* pn)
298 {
299 MOZ_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE));
300 MOZ_ASSERT(pn->isArity(PN_NULLARY));
301 return pn->as<LoopControlStatement>().label();
302 }
303
304 static inline PropertyName*
LabeledStatementLabel(ParseNode * pn)305 LabeledStatementLabel(ParseNode* pn)
306 {
307 return pn->as<LabeledStatement>().label();
308 }
309
310 static inline ParseNode*
LabeledStatementStatement(ParseNode * pn)311 LabeledStatementStatement(ParseNode* pn)
312 {
313 return pn->as<LabeledStatement>().statement();
314 }
315
316 static double
NumberNodeValue(ParseNode * pn)317 NumberNodeValue(ParseNode* pn)
318 {
319 MOZ_ASSERT(pn->isKind(PNK_NUMBER));
320 return pn->pn_dval;
321 }
322
323 static bool
NumberNodeHasFrac(ParseNode * pn)324 NumberNodeHasFrac(ParseNode* pn)
325 {
326 MOZ_ASSERT(pn->isKind(PNK_NUMBER));
327 return pn->pn_u.number.decimalPoint == HasDecimal;
328 }
329
330 static ParseNode*
DotBase(ParseNode * pn)331 DotBase(ParseNode* pn)
332 {
333 MOZ_ASSERT(pn->isKind(PNK_DOT));
334 MOZ_ASSERT(pn->isArity(PN_NAME));
335 return pn->expr();
336 }
337
338 static PropertyName*
DotMember(ParseNode * pn)339 DotMember(ParseNode* pn)
340 {
341 MOZ_ASSERT(pn->isKind(PNK_DOT));
342 MOZ_ASSERT(pn->isArity(PN_NAME));
343 return pn->pn_atom->asPropertyName();
344 }
345
346 static ParseNode*
ElemBase(ParseNode * pn)347 ElemBase(ParseNode* pn)
348 {
349 MOZ_ASSERT(pn->isKind(PNK_ELEM));
350 return BinaryLeft(pn);
351 }
352
353 static ParseNode*
ElemIndex(ParseNode * pn)354 ElemIndex(ParseNode* pn)
355 {
356 MOZ_ASSERT(pn->isKind(PNK_ELEM));
357 return BinaryRight(pn);
358 }
359
360 static inline JSFunction*
FunctionObject(ParseNode * fn)361 FunctionObject(ParseNode* fn)
362 {
363 MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
364 MOZ_ASSERT(fn->isArity(PN_CODE));
365 return fn->pn_funbox->function();
366 }
367
368 static inline PropertyName*
FunctionName(ParseNode * fn)369 FunctionName(ParseNode* fn)
370 {
371 if (JSAtom* atom = FunctionObject(fn)->atom())
372 return atom->asPropertyName();
373 return nullptr;
374 }
375
376 static inline ParseNode*
FunctionStatementList(ParseNode * fn)377 FunctionStatementList(ParseNode* fn)
378 {
379 MOZ_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
380 ParseNode* last = fn->pn_body->last();
381 MOZ_ASSERT(last->isKind(PNK_STATEMENTLIST));
382 return last;
383 }
384
385 static inline bool
IsNormalObjectField(ExclusiveContext * cx,ParseNode * pn)386 IsNormalObjectField(ExclusiveContext* cx, ParseNode* pn)
387 {
388 return pn->isKind(PNK_COLON) &&
389 pn->getOp() == JSOP_INITPROP &&
390 BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME);
391 }
392
393 static inline PropertyName*
ObjectNormalFieldName(ExclusiveContext * cx,ParseNode * pn)394 ObjectNormalFieldName(ExclusiveContext* cx, ParseNode* pn)
395 {
396 MOZ_ASSERT(IsNormalObjectField(cx, pn));
397 MOZ_ASSERT(BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME));
398 return BinaryLeft(pn)->pn_atom->asPropertyName();
399 }
400
401 static inline ParseNode*
ObjectNormalFieldInitializer(ExclusiveContext * cx,ParseNode * pn)402 ObjectNormalFieldInitializer(ExclusiveContext* cx, ParseNode* pn)
403 {
404 MOZ_ASSERT(IsNormalObjectField(cx, pn));
405 return BinaryRight(pn);
406 }
407
408 static inline bool
IsDefinition(ParseNode * pn)409 IsDefinition(ParseNode* pn)
410 {
411 return pn->isKind(PNK_NAME) && pn->isDefn();
412 }
413
414 static inline ParseNode*
MaybeDefinitionInitializer(ParseNode * pn)415 MaybeDefinitionInitializer(ParseNode* pn)
416 {
417 MOZ_ASSERT(IsDefinition(pn));
418 return pn->expr();
419 }
420
421 static inline bool
IsUseOfName(ParseNode * pn,PropertyName * name)422 IsUseOfName(ParseNode* pn, PropertyName* name)
423 {
424 return pn->isKind(PNK_NAME) && pn->name() == name;
425 }
426
427 static inline bool
IsIgnoredDirectiveName(ExclusiveContext * cx,JSAtom * atom)428 IsIgnoredDirectiveName(ExclusiveContext* cx, JSAtom* atom)
429 {
430 return atom != cx->names().useStrict;
431 }
432
433 static inline bool
IsIgnoredDirective(ExclusiveContext * cx,ParseNode * pn)434 IsIgnoredDirective(ExclusiveContext* cx, ParseNode* pn)
435 {
436 return pn->isKind(PNK_SEMI) &&
437 UnaryKid(pn) &&
438 UnaryKid(pn)->isKind(PNK_STRING) &&
439 IsIgnoredDirectiveName(cx, UnaryKid(pn)->pn_atom);
440 }
441
442 static inline bool
IsEmptyStatement(ParseNode * pn)443 IsEmptyStatement(ParseNode* pn)
444 {
445 return pn->isKind(PNK_SEMI) && !UnaryKid(pn);
446 }
447
448 static inline ParseNode*
SkipEmptyStatements(ParseNode * pn)449 SkipEmptyStatements(ParseNode* pn)
450 {
451 while (pn && IsEmptyStatement(pn))
452 pn = pn->pn_next;
453 return pn;
454 }
455
456 static inline ParseNode*
NextNonEmptyStatement(ParseNode * pn)457 NextNonEmptyStatement(ParseNode* pn)
458 {
459 return SkipEmptyStatements(pn->pn_next);
460 }
461
462 static bool
GetToken(AsmJSParser & parser,TokenKind * tkp)463 GetToken(AsmJSParser& parser, TokenKind* tkp)
464 {
465 TokenStream& ts = parser.tokenStream;
466 TokenKind tk;
467 while (true) {
468 if (!ts.getToken(&tk, TokenStream::Operand))
469 return false;
470 if (tk != TOK_SEMI)
471 break;
472 }
473 *tkp = tk;
474 return true;
475 }
476
477 static bool
PeekToken(AsmJSParser & parser,TokenKind * tkp)478 PeekToken(AsmJSParser& parser, TokenKind* tkp)
479 {
480 TokenStream& ts = parser.tokenStream;
481 TokenKind tk;
482 while (true) {
483 if (!ts.peekToken(&tk, TokenStream::Operand))
484 return false;
485 if (tk != TOK_SEMI)
486 break;
487 ts.consumeKnownToken(TOK_SEMI, TokenStream::Operand);
488 }
489 *tkp = tk;
490 return true;
491 }
492
493 static bool
ParseVarOrConstStatement(AsmJSParser & parser,ParseNode ** var)494 ParseVarOrConstStatement(AsmJSParser& parser, ParseNode** var)
495 {
496 TokenKind tk;
497 if (!PeekToken(parser, &tk))
498 return false;
499 if (tk != TOK_VAR && tk != TOK_CONST) {
500 *var = nullptr;
501 return true;
502 }
503
504 *var = parser.statement(YieldIsName);
505 if (!*var)
506 return false;
507
508 MOZ_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST));
509 return true;
510 }
511
512 /*****************************************************************************/
513
514 // Represents the type and value of an asm.js numeric literal.
515 //
516 // A literal is a double iff the literal contains a decimal point (even if the
517 // fractional part is 0). Otherwise, integers may be classified:
518 // fixnum: [0, 2^31)
519 // negative int: [-2^31, 0)
520 // big unsigned: [2^31, 2^32)
521 // out of range: otherwise
522 // Lastly, a literal may be a float literal which is any double or integer
523 // literal coerced with Math.fround.
524 class NumLit
525 {
526 public:
527 enum Which {
528 Fixnum,
529 NegativeInt,
530 BigUnsigned,
531 Double,
532 Float,
533 Int32x4,
534 Float32x4,
535 OutOfRangeInt = -1
536 };
537
538 private:
539 Which which_;
540 union {
541 Value scalar_;
542 jit::SimdConstant simd_;
543 } u;
544
545 public:
546 NumLit() = default;
547
NumLit(Which w,Value v)548 NumLit(Which w, Value v) : which_(w) {
549 u.scalar_ = v;
550 MOZ_ASSERT(!isSimd());
551 }
552
NumLit(Which w,jit::SimdConstant c)553 NumLit(Which w, jit::SimdConstant c) : which_(w) {
554 u.simd_ = c;
555 MOZ_ASSERT(isSimd());
556 }
557
which() const558 Which which() const {
559 return which_;
560 }
561
toInt32() const562 int32_t toInt32() const {
563 MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
564 return u.scalar_.toInt32();
565 }
566
toUint32() const567 uint32_t toUint32() const {
568 return (uint32_t)toInt32();
569 }
570
toDouble() const571 double toDouble() const {
572 MOZ_ASSERT(which_ == Double);
573 return u.scalar_.toDouble();
574 }
575
toFloat() const576 float toFloat() const {
577 MOZ_ASSERT(which_ == Float);
578 return float(u.scalar_.toDouble());
579 }
580
scalarValue() const581 Value scalarValue() const {
582 MOZ_ASSERT(which_ != OutOfRangeInt);
583 return u.scalar_;
584 }
585
isSimd() const586 bool isSimd() const {
587 return which_ == Int32x4 || which_ == Float32x4;
588 }
589
simdValue() const590 const jit::SimdConstant& simdValue() const {
591 MOZ_ASSERT(isSimd());
592 return u.simd_;
593 }
594
valid() const595 bool valid() const {
596 return which_ != OutOfRangeInt;
597 }
598
type() const599 ValType type() const {
600 switch (which_) {
601 case NumLit::Fixnum:
602 case NumLit::NegativeInt:
603 case NumLit::BigUnsigned:
604 return ValType::I32;
605 case NumLit::Double:
606 return ValType::F64;
607 case NumLit::Float:
608 return ValType::F32;
609 case NumLit::Int32x4:
610 return ValType::I32x4;
611 case NumLit::Float32x4:
612 return ValType::F32x4;
613 case NumLit::OutOfRangeInt:;
614 }
615 MOZ_CRASH("bad literal");
616 }
617
value() const618 Val value() const {
619 switch (which_) {
620 case NumLit::Fixnum:
621 case NumLit::NegativeInt:
622 case NumLit::BigUnsigned:
623 return Val(toUint32());
624 case NumLit::Float:
625 return Val(toFloat());
626 case NumLit::Double:
627 return Val(toDouble());
628 case NumLit::Int32x4:
629 return Val(simdValue().asInt32x4());
630 case NumLit::Float32x4:
631 return Val(simdValue().asFloat32x4());
632 case NumLit::OutOfRangeInt:;
633 }
634 MOZ_CRASH("bad literal");
635 }
636 };
637
638 // Respresents the type of a general asm.js expression.
639 class Type
640 {
641 public:
642 enum Which {
643 Fixnum = NumLit::Fixnum,
644 Signed = NumLit::NegativeInt,
645 Unsigned = NumLit::BigUnsigned,
646 DoubleLit = NumLit::Double,
647 Float = NumLit::Float,
648 Int32x4 = NumLit::Int32x4,
649 Float32x4 = NumLit::Float32x4,
650 Double,
651 MaybeDouble,
652 MaybeFloat,
653 Floatish,
654 Int,
655 Intish,
656 Void
657 };
658
659 private:
660 Which which_;
661
662 public:
663 Type() = default;
Type(Which w)664 MOZ_IMPLICIT Type(Which w) : which_(w) {}
Type(AsmJSSimdType type)665 MOZ_IMPLICIT Type(AsmJSSimdType type) {
666 switch (type) {
667 case AsmJSSimdType_int32x4:
668 which_ = Int32x4;
669 return;
670 case AsmJSSimdType_float32x4:
671 which_ = Float32x4;
672 return;
673 }
674 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad AsmJSSimdType");
675 }
676
var(ValType t)677 static Type var(ValType t) {
678 switch (t) {
679 case ValType::I32: return Int;
680 case ValType::I64: MOZ_CRASH("no int64 in asm.js");
681 case ValType::F32: return Float;
682 case ValType::F64: return Double;
683 case ValType::I32x4: return Int32x4;
684 case ValType::F32x4: return Float32x4;
685 }
686 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
687 }
688
ret(ExprType t)689 static Type ret(ExprType t) {
690 switch (t) {
691 case ExprType::Void: return Type::Void;
692 case ExprType::I32: return Signed;
693 case ExprType::I64: MOZ_CRASH("no int64 in asm.js");
694 case ExprType::F32: return Float;
695 case ExprType::F64: return Double;
696 case ExprType::I32x4: return Int32x4;
697 case ExprType::F32x4: return Float32x4;
698 }
699 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
700 }
701
lit(const NumLit & lit)702 static Type lit(const NumLit& lit) {
703 MOZ_ASSERT(lit.valid());
704 Which which = Type::Which(lit.which());
705 MOZ_ASSERT(which >= Fixnum && which <= Float32x4);
706 Type t;
707 t.which_ = which;
708 return t;
709 }
710
which() const711 Which which() const { return which_; }
712
operator ==(Type rhs) const713 bool operator==(Type rhs) const { return which_ == rhs.which_; }
operator !=(Type rhs) const714 bool operator!=(Type rhs) const { return which_ != rhs.which_; }
715
operator <=(Type rhs) const716 bool operator<=(Type rhs) const {
717 switch (rhs.which_) {
718 case Signed: return isSigned();
719 case Unsigned: return isUnsigned();
720 case DoubleLit: return isDoubleLit();
721 case Double: return isDouble();
722 case Float: return isFloat();
723 case Int32x4: return isInt32x4();
724 case Float32x4: return isFloat32x4();
725 case MaybeDouble: return isMaybeDouble();
726 case MaybeFloat: return isMaybeFloat();
727 case Floatish: return isFloatish();
728 case Int: return isInt();
729 case Intish: return isIntish();
730 case Fixnum: return isFixnum();
731 case Void: return isVoid();
732 }
733 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected rhs type");
734 }
735
operator <=(ValType rhs) const736 bool operator<=(ValType rhs) const {
737 switch (rhs) {
738 case ValType::I32: return isInt();
739 case ValType::I64: MOZ_CRASH("no int64 in asm.js");
740 case ValType::F32: return isFloat();
741 case ValType::F64: return isDouble();
742 case ValType::I32x4: return isInt32x4();
743 case ValType::F32x4: return isFloat32x4();
744 }
745 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected rhs type");
746 }
747
isFixnum() const748 bool isFixnum() const {
749 return which_ == Fixnum;
750 }
751
isSigned() const752 bool isSigned() const {
753 return which_ == Signed || which_ == Fixnum;
754 }
755
isUnsigned() const756 bool isUnsigned() const {
757 return which_ == Unsigned || which_ == Fixnum;
758 }
759
isInt() const760 bool isInt() const {
761 return isSigned() || isUnsigned() || which_ == Int;
762 }
763
isIntish() const764 bool isIntish() const {
765 return isInt() || which_ == Intish;
766 }
767
isDoubleLit() const768 bool isDoubleLit() const {
769 return which_ == DoubleLit;
770 }
771
isDouble() const772 bool isDouble() const {
773 return isDoubleLit() || which_ == Double;
774 }
775
isMaybeDouble() const776 bool isMaybeDouble() const {
777 return isDouble() || which_ == MaybeDouble;
778 }
779
isFloat() const780 bool isFloat() const {
781 return which_ == Float;
782 }
783
isMaybeFloat() const784 bool isMaybeFloat() const {
785 return isFloat() || which_ == MaybeFloat;
786 }
787
isFloatish() const788 bool isFloatish() const {
789 return isMaybeFloat() || which_ == Floatish;
790 }
791
isVoid() const792 bool isVoid() const {
793 return which_ == Void;
794 }
795
isExtern() const796 bool isExtern() const {
797 return isDouble() || isSigned();
798 }
799
isInt32x4() const800 bool isInt32x4() const {
801 return which_ == Int32x4;
802 }
803
isFloat32x4() const804 bool isFloat32x4() const {
805 return which_ == Float32x4;
806 }
807
isSimd() const808 bool isSimd() const {
809 return isInt32x4() || isFloat32x4();
810 }
811
isVarType() const812 bool isVarType() const {
813 return isInt() || isFloat() || isDouble() || isSimd();
814 }
815
checkedValueType() const816 ValType checkedValueType() const {
817 MOZ_ASSERT(isVarType());
818 if (isInt())
819 return ValType::I32;
820 else if (isFloat())
821 return ValType::F32;
822 else if (isDouble())
823 return ValType::F64;
824 else if (isInt32x4())
825 return ValType::I32x4;
826 return ValType::F32x4;
827 }
828
toMIRType() const829 jit::MIRType toMIRType() const {
830 switch (which_) {
831 case Double:
832 case DoubleLit:
833 case MaybeDouble:
834 return jit::MIRType_Double;
835 case Float:
836 case Floatish:
837 case MaybeFloat:
838 return jit::MIRType_Float32;
839 case Fixnum:
840 case Int:
841 case Signed:
842 case Unsigned:
843 case Intish:
844 return jit::MIRType_Int32;
845 case Int32x4:
846 return jit::MIRType_Int32x4;
847 case Float32x4:
848 return jit::MIRType_Float32x4;
849 case Void:
850 return jit::MIRType_None;
851 }
852 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
853 }
854
simdType() const855 AsmJSSimdType simdType() const {
856 MOZ_ASSERT(isSimd());
857 switch (which_) {
858 case Int32x4:
859 return AsmJSSimdType_int32x4;
860 case Float32x4:
861 return AsmJSSimdType_float32x4;
862 // Scalar types
863 case Double:
864 case DoubleLit:
865 case MaybeDouble:
866 case Float:
867 case MaybeFloat:
868 case Floatish:
869 case Fixnum:
870 case Int:
871 case Signed:
872 case Unsigned:
873 case Intish:
874 case Void:
875 break;
876 }
877 MOZ_CRASH("not a SIMD Type");
878 }
879
toChars() const880 const char* toChars() const {
881 switch (which_) {
882 case Double: return "double";
883 case DoubleLit: return "doublelit";
884 case MaybeDouble: return "double?";
885 case Float: return "float";
886 case Floatish: return "floatish";
887 case MaybeFloat: return "float?";
888 case Fixnum: return "fixnum";
889 case Int: return "int";
890 case Signed: return "signed";
891 case Unsigned: return "unsigned";
892 case Intish: return "intish";
893 case Int32x4: return "int32x4";
894 case Float32x4: return "float32x4";
895 case Void: return "void";
896 }
897 MOZ_CRASH("Invalid Type");
898 }
899 };
900
901 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
902
903 namespace {
904
905 // The ModuleValidator encapsulates the entire validation of an asm.js module.
906 // Its lifetime goes from the validation of the top components of an asm.js
907 // module (all the globals), the emission of bytecode for all the functions in
908 // the module and the validation of function's pointer tables. It also finishes
909 // the compilation of all the module's stubs.
910 //
911 // Rooting note: ModuleValidator is a stack class that contains unrooted
912 // PropertyName (JSAtom) pointers. This is safe because it cannot be
913 // constructed without a TokenStream reference. TokenStream is itself a stack
914 // class that cannot be constructed without an AutoKeepAtoms being live on the
915 // stack, which prevents collection of atoms.
916 //
917 // ModuleValidator is marked as rooted in the rooting analysis. Don't add
918 // non-JSAtom pointers, or this will break!
919 class MOZ_STACK_CLASS ModuleValidator
920 {
921 public:
922 class Func
923 {
924 const LifoSig& sig_;
925 PropertyName* name_;
926 uint32_t firstUse_;
927 uint32_t index_;
928 uint32_t srcBegin_;
929 uint32_t srcEnd_;
930 bool defined_;
931
932 public:
Func(PropertyName * name,uint32_t firstUse,const LifoSig & sig,uint32_t index)933 Func(PropertyName* name, uint32_t firstUse, const LifoSig& sig, uint32_t index)
934 : sig_(sig), name_(name), firstUse_(firstUse), index_(index),
935 srcBegin_(0), srcEnd_(0), defined_(false)
936 {}
937
name() const938 PropertyName* name() const { return name_; }
firstUse() const939 uint32_t firstUse() const { return firstUse_; }
defined() const940 bool defined() const { return defined_; }
index() const941 uint32_t index() const { return index_; }
942
define(ParseNode * fn)943 void define(ParseNode* fn) {
944 MOZ_ASSERT(!defined_);
945 defined_ = true;
946 srcBegin_ = fn->pn_pos.begin;
947 srcEnd_ = fn->pn_pos.end;
948 }
949
srcBegin() const950 uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; }
srcEnd() const951 uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
sig() const952 const LifoSig& sig() const { return sig_; }
953 };
954
955 typedef Vector<const Func*> ConstFuncVector;
956 typedef Vector<Func*> FuncVector;
957
958 class FuncPtrTable
959 {
960 const LifoSig& sig_;
961 PropertyName* name_;
962 uint32_t firstUse_;
963 uint32_t mask_;
964 bool defined_;
965
966 FuncPtrTable(FuncPtrTable&& rhs) = delete;
967
968 public:
FuncPtrTable(ExclusiveContext * cx,PropertyName * name,uint32_t firstUse,const LifoSig & sig,uint32_t mask)969 FuncPtrTable(ExclusiveContext* cx, PropertyName* name, uint32_t firstUse,
970 const LifoSig& sig, uint32_t mask)
971 : sig_(sig), name_(name), firstUse_(firstUse), mask_(mask), defined_(false)
972 {}
973
sig() const974 const LifoSig& sig() const { return sig_; }
name() const975 PropertyName* name() const { return name_; }
firstUse() const976 uint32_t firstUse() const { return firstUse_; }
mask() const977 unsigned mask() const { return mask_; }
defined() const978 bool defined() const { return defined_; }
define()979 void define() { MOZ_ASSERT(!defined_); defined_ = true; }
980 };
981
982 typedef Vector<FuncPtrTable*> FuncPtrTableVector;
983
984 class Global
985 {
986 public:
987 enum Which {
988 Variable,
989 ConstantLiteral,
990 ConstantImport,
991 Function,
992 FuncPtrTable,
993 FFI,
994 ArrayView,
995 ArrayViewCtor,
996 MathBuiltinFunction,
997 AtomicsBuiltinFunction,
998 SimdCtor,
999 SimdOperation,
1000 ByteLength,
1001 ChangeHeap
1002 };
1003
1004 private:
1005 Which which_;
1006 union {
1007 struct {
1008 Type::Which type_;
1009 uint32_t globalDataOffset_;
1010 NumLit literalValue_;
1011 } varOrConst;
1012 uint32_t funcIndex_;
1013 uint32_t funcPtrTableIndex_;
1014 uint32_t ffiIndex_;
1015 struct {
1016 Scalar::Type viewType_;
1017 } viewInfo;
1018 AsmJSMathBuiltinFunction mathBuiltinFunc_;
1019 AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
1020 AsmJSSimdType simdCtorType_;
1021 struct {
1022 AsmJSSimdType type_;
1023 AsmJSSimdOperation which_;
1024 } simdOp;
1025 struct {
1026 uint32_t srcBegin_;
1027 uint32_t srcEnd_;
1028 } changeHeap;
1029 } u;
1030
1031 friend class ModuleValidator;
1032 friend class js::LifoAlloc;
1033
Global(Which which)1034 explicit Global(Which which) : which_(which) {}
1035
1036 public:
which() const1037 Which which() const {
1038 return which_;
1039 }
varOrConstType() const1040 Type varOrConstType() const {
1041 MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
1042 return u.varOrConst.type_;
1043 }
varOrConstGlobalDataOffset() const1044 uint32_t varOrConstGlobalDataOffset() const {
1045 MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
1046 return u.varOrConst.globalDataOffset_;
1047 }
isConst() const1048 bool isConst() const {
1049 return which_ == ConstantLiteral || which_ == ConstantImport;
1050 }
constLiteralValue() const1051 NumLit constLiteralValue() const {
1052 MOZ_ASSERT(which_ == ConstantLiteral);
1053 return u.varOrConst.literalValue_;
1054 }
funcIndex() const1055 uint32_t funcIndex() const {
1056 MOZ_ASSERT(which_ == Function);
1057 return u.funcIndex_;
1058 }
funcPtrTableIndex() const1059 uint32_t funcPtrTableIndex() const {
1060 MOZ_ASSERT(which_ == FuncPtrTable);
1061 return u.funcPtrTableIndex_;
1062 }
ffiIndex() const1063 unsigned ffiIndex() const {
1064 MOZ_ASSERT(which_ == FFI);
1065 return u.ffiIndex_;
1066 }
isAnyArrayView() const1067 bool isAnyArrayView() const {
1068 return which_ == ArrayView || which_ == ArrayViewCtor;
1069 }
viewType() const1070 Scalar::Type viewType() const {
1071 MOZ_ASSERT(isAnyArrayView());
1072 return u.viewInfo.viewType_;
1073 }
isMathFunction() const1074 bool isMathFunction() const {
1075 return which_ == MathBuiltinFunction;
1076 }
mathBuiltinFunction() const1077 AsmJSMathBuiltinFunction mathBuiltinFunction() const {
1078 MOZ_ASSERT(which_ == MathBuiltinFunction);
1079 return u.mathBuiltinFunc_;
1080 }
isAtomicsFunction() const1081 bool isAtomicsFunction() const {
1082 return which_ == AtomicsBuiltinFunction;
1083 }
atomicsBuiltinFunction() const1084 AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
1085 MOZ_ASSERT(which_ == AtomicsBuiltinFunction);
1086 return u.atomicsBuiltinFunc_;
1087 }
isSimdCtor() const1088 bool isSimdCtor() const {
1089 return which_ == SimdCtor;
1090 }
simdCtorType() const1091 AsmJSSimdType simdCtorType() const {
1092 MOZ_ASSERT(which_ == SimdCtor);
1093 return u.simdCtorType_;
1094 }
isSimdOperation() const1095 bool isSimdOperation() const {
1096 return which_ == SimdOperation;
1097 }
simdOperation() const1098 AsmJSSimdOperation simdOperation() const {
1099 MOZ_ASSERT(which_ == SimdOperation);
1100 return u.simdOp.which_;
1101 }
simdOperationType() const1102 AsmJSSimdType simdOperationType() const {
1103 MOZ_ASSERT(which_ == SimdOperation);
1104 return u.simdOp.type_;
1105 }
changeHeapSrcBegin() const1106 uint32_t changeHeapSrcBegin() const {
1107 MOZ_ASSERT(which_ == ChangeHeap);
1108 return u.changeHeap.srcBegin_;
1109 }
changeHeapSrcEnd() const1110 uint32_t changeHeapSrcEnd() const {
1111 MOZ_ASSERT(which_ == ChangeHeap);
1112 return u.changeHeap.srcEnd_;
1113 }
1114 };
1115
1116 struct MathBuiltin
1117 {
1118 enum Kind { Function, Constant };
1119 Kind kind;
1120
1121 union {
1122 double cst;
1123 AsmJSMathBuiltinFunction func;
1124 } u;
1125
MathBuiltin__anonffd339ad0211::ModuleValidator::MathBuiltin1126 MathBuiltin() : kind(Kind(-1)) {}
MathBuiltin__anonffd339ad0211::ModuleValidator::MathBuiltin1127 explicit MathBuiltin(double cst) : kind(Constant) {
1128 u.cst = cst;
1129 }
MathBuiltin__anonffd339ad0211::ModuleValidator::MathBuiltin1130 explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
1131 u.func = func;
1132 }
1133 };
1134
1135 struct ArrayView
1136 {
ArrayView__anonffd339ad0211::ModuleValidator::ArrayView1137 ArrayView(PropertyName* name, Scalar::Type type)
1138 : name(name), type(type)
1139 {}
1140
1141 PropertyName* name;
1142 Scalar::Type type;
1143 };
1144
1145 class ExitDescriptor
1146 {
1147 PropertyName* name_;
1148 const LifoSig* sig_;
1149
1150 public:
ExitDescriptor(PropertyName * name,const LifoSig & sig)1151 ExitDescriptor(PropertyName* name, const LifoSig& sig)
1152 : name_(name), sig_(&sig)
1153 {}
1154
name() const1155 PropertyName* name() const {
1156 return name_;
1157 }
sig() const1158 const LifoSig& sig() const {
1159 return *sig_;
1160 }
1161
1162 struct Lookup { // implements HashPolicy
1163 PropertyName* name_;
1164 const MallocSig& sig_;
Lookup__anonffd339ad0211::ModuleValidator::ExitDescriptor::Lookup1165 Lookup(PropertyName* name, const MallocSig& sig) : name_(name), sig_(sig) {}
1166 };
hash(const Lookup & l)1167 static HashNumber hash(const Lookup& l) {
1168 return HashGeneric(l.name_, l.sig_.hash());
1169 }
match(const ExitDescriptor & lhs,const Lookup & rhs)1170 static bool match(const ExitDescriptor& lhs, const Lookup& rhs) {
1171 return lhs.name_ == rhs.name_ && *lhs.sig_ == rhs.sig_;
1172 }
1173 };
1174
1175 private:
1176 typedef HashMap<PropertyName*, Global*> GlobalMap;
1177 typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
1178 typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
1179 typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
1180 typedef Vector<ArrayView> ArrayViewVector;
1181
1182 public:
1183 typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
1184
1185 private:
1186 ExclusiveContext* cx_;
1187 AsmJSParser& parser_;
1188
1189 ModuleGenerator mg_;
1190
1191 LifoAlloc validationLifo_;
1192 FuncVector functions_;
1193 FuncPtrTableVector funcPtrTables_;
1194 GlobalMap globals_;
1195 ArrayViewVector arrayViews_;
1196 ExitMap exits_;
1197
1198 MathNameMap standardLibraryMathNames_;
1199 AtomicsNameMap standardLibraryAtomicsNames_;
1200 SimdOperationNameMap standardLibrarySimdOpNames_;
1201
1202 ParseNode* moduleFunctionNode_;
1203 PropertyName* moduleFunctionName_;
1204
1205 UniquePtr<char[], JS::FreePolicy> errorString_;
1206 uint32_t errorOffset_;
1207 bool errorOverRecursed_;
1208
1209 bool canValidateChangeHeap_;
1210 bool hasChangeHeap_;
1211 bool supportsSimd_;
1212 bool atomicsPresent_;
1213
1214 public:
ModuleValidator(ExclusiveContext * cx,AsmJSParser & parser)1215 ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
1216 : cx_(cx),
1217 parser_(parser),
1218 mg_(cx),
1219 validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
1220 functions_(cx),
1221 funcPtrTables_(cx),
1222 globals_(cx),
1223 arrayViews_(cx),
1224 exits_(cx),
1225 standardLibraryMathNames_(cx),
1226 standardLibraryAtomicsNames_(cx),
1227 standardLibrarySimdOpNames_(cx),
1228 moduleFunctionNode_(parser.pc->maybeFunction),
1229 moduleFunctionName_(nullptr),
1230 errorString_(nullptr),
1231 errorOffset_(UINT32_MAX),
1232 errorOverRecursed_(false),
1233 canValidateChangeHeap_(false),
1234 hasChangeHeap_(false),
1235 supportsSimd_(cx->jitSupportsSimd()),
1236 atomicsPresent_(false)
1237 {
1238 MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
1239 }
1240
~ModuleValidator()1241 ~ModuleValidator() {
1242 if (errorString_) {
1243 MOZ_ASSERT(errorOffset_ != UINT32_MAX);
1244 tokenStream().reportAsmJSError(errorOffset_,
1245 JSMSG_USE_ASM_TYPE_FAIL,
1246 errorString_.get());
1247 }
1248 if (errorOverRecursed_)
1249 ReportOverRecursed(cx_);
1250 }
1251
1252 private:
1253
1254 // Helpers
addStandardLibraryMathName(const char * name,AsmJSMathBuiltinFunction func)1255 bool addStandardLibraryMathName(const char* name, AsmJSMathBuiltinFunction func) {
1256 JSAtom* atom = Atomize(cx_, name, strlen(name));
1257 if (!atom)
1258 return false;
1259 MathBuiltin builtin(func);
1260 return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
1261 }
addStandardLibraryMathName(const char * name,double cst)1262 bool addStandardLibraryMathName(const char* name, double cst) {
1263 JSAtom* atom = Atomize(cx_, name, strlen(name));
1264 if (!atom)
1265 return false;
1266 MathBuiltin builtin(cst);
1267 return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
1268 }
addStandardLibraryAtomicsName(const char * name,AsmJSAtomicsBuiltinFunction func)1269 bool addStandardLibraryAtomicsName(const char* name, AsmJSAtomicsBuiltinFunction func) {
1270 JSAtom* atom = Atomize(cx_, name, strlen(name));
1271 if (!atom)
1272 return false;
1273 return standardLibraryAtomicsNames_.putNew(atom->asPropertyName(), func);
1274 }
addStandardLibrarySimdOpName(const char * name,AsmJSSimdOperation op)1275 bool addStandardLibrarySimdOpName(const char* name, AsmJSSimdOperation op) {
1276 JSAtom* atom = Atomize(cx_, name, strlen(name));
1277 if (!atom)
1278 return false;
1279 return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
1280 }
1281
1282 public:
1283
init()1284 bool init() {
1285 if (!globals_.init() || !exits_.init())
1286 return false;
1287
1288 if (!standardLibraryMathNames_.init() ||
1289 !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
1290 !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
1291 !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
1292 !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
1293 !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
1294 !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) ||
1295 !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
1296 !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
1297 !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
1298 !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
1299 !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
1300 !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
1301 !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
1302 !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
1303 !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
1304 !addStandardLibraryMathName("clz32", AsmJSMathBuiltin_clz32) ||
1305 !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
1306 !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) ||
1307 !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) ||
1308 !addStandardLibraryMathName("E", M_E) ||
1309 !addStandardLibraryMathName("LN10", M_LN10) ||
1310 !addStandardLibraryMathName("LN2", M_LN2) ||
1311 !addStandardLibraryMathName("LOG2E", M_LOG2E) ||
1312 !addStandardLibraryMathName("LOG10E", M_LOG10E) ||
1313 !addStandardLibraryMathName("PI", M_PI) ||
1314 !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
1315 !addStandardLibraryMathName("SQRT2", M_SQRT2))
1316 {
1317 return false;
1318 }
1319
1320 if (!standardLibraryAtomicsNames_.init() ||
1321 !addStandardLibraryAtomicsName("compareExchange", AsmJSAtomicsBuiltin_compareExchange) ||
1322 !addStandardLibraryAtomicsName("exchange", AsmJSAtomicsBuiltin_exchange) ||
1323 !addStandardLibraryAtomicsName("load", AsmJSAtomicsBuiltin_load) ||
1324 !addStandardLibraryAtomicsName("store", AsmJSAtomicsBuiltin_store) ||
1325 !addStandardLibraryAtomicsName("fence", AsmJSAtomicsBuiltin_fence) ||
1326 !addStandardLibraryAtomicsName("add", AsmJSAtomicsBuiltin_add) ||
1327 !addStandardLibraryAtomicsName("sub", AsmJSAtomicsBuiltin_sub) ||
1328 !addStandardLibraryAtomicsName("and", AsmJSAtomicsBuiltin_and) ||
1329 !addStandardLibraryAtomicsName("or", AsmJSAtomicsBuiltin_or) ||
1330 !addStandardLibraryAtomicsName("xor", AsmJSAtomicsBuiltin_xor) ||
1331 !addStandardLibraryAtomicsName("isLockFree", AsmJSAtomicsBuiltin_isLockFree))
1332 {
1333 return false;
1334 }
1335
1336 #define ADDSTDLIBSIMDOPNAME(op) || !addStandardLibrarySimdOpName(#op, AsmJSSimdOperation_##op)
1337 if (!standardLibrarySimdOpNames_.init()
1338 FORALL_SIMD_OP(ADDSTDLIBSIMDOPNAME))
1339 {
1340 return false;
1341 }
1342 #undef ADDSTDLIBSIMDOPNAME
1343
1344 uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
1345 uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
1346
1347 // "use strict" should be added to the source if we are in an implicit
1348 // strict context, see also comment above addUseStrict in
1349 // js::FunctionToString.
1350 bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
1351
1352 return mg_.init(parser_.ss, srcStart, srcBodyStart, strict);
1353 }
1354
finish(ScopedJSDeletePtr<AsmJSModule> * module,SlowFunctionVector * slowFuncs)1355 bool finish(ScopedJSDeletePtr<AsmJSModule>* module, SlowFunctionVector* slowFuncs) {
1356 return mg_.finish(parser_.tokenStream, module, slowFuncs);
1357 }
1358
1359 // Mutable interface.
initModuleFunctionName(PropertyName * name)1360 void initModuleFunctionName(PropertyName* name) { moduleFunctionName_ = name; }
initGlobalArgumentName(PropertyName * n)1361 void initGlobalArgumentName(PropertyName* n) { module().initGlobalArgumentName(n); }
initImportArgumentName(PropertyName * n)1362 void initImportArgumentName(PropertyName* n) { module().initImportArgumentName(n); }
initBufferArgumentName(PropertyName * n)1363 void initBufferArgumentName(PropertyName* n) { module().initBufferArgumentName(n); }
1364
addGlobalVarInit(PropertyName * varName,const NumLit & lit,bool isConst)1365 bool addGlobalVarInit(PropertyName* varName, const NumLit& lit, bool isConst) {
1366 // The type of a const is the exact type of the literal (since its value
1367 // cannot change) which is more precise than the corresponding vartype.
1368 Type type = isConst ? Type::lit(lit) : Type::var(lit.type());
1369 uint32_t globalDataOffset;
1370 if (!module().addGlobalVarInit(lit.value(), &globalDataOffset))
1371 return false;
1372 Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
1373 Global* global = validationLifo_.new_<Global>(which);
1374 if (!global)
1375 return false;
1376 global->u.varOrConst.globalDataOffset_ = globalDataOffset;
1377 global->u.varOrConst.type_ = type.which();
1378 if (isConst)
1379 global->u.varOrConst.literalValue_ = lit;
1380 return globals_.putNew(varName, global);
1381 }
addGlobalVarImport(PropertyName * varName,PropertyName * fieldName,ValType importType,bool isConst)1382 bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType,
1383 bool isConst)
1384 {
1385 uint32_t globalDataOffset;
1386 if (!module().addGlobalVarImport(fieldName, importType, &globalDataOffset))
1387 return false;
1388 Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
1389 Global* global = validationLifo_.new_<Global>(which);
1390 if (!global)
1391 return false;
1392 global->u.varOrConst.globalDataOffset_ = globalDataOffset;
1393 global->u.varOrConst.type_ = Type::var(importType).which();
1394 return globals_.putNew(varName, global);
1395 }
addArrayView(PropertyName * varName,Scalar::Type vt,PropertyName * maybeField)1396 bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField)
1397 {
1398 if (!arrayViews_.append(ArrayView(varName, vt)))
1399 return false;
1400 Global* global = validationLifo_.new_<Global>(Global::ArrayView);
1401 if (!global)
1402 return false;
1403 if (!module().addArrayView(vt, maybeField))
1404 return false;
1405 global->u.viewInfo.viewType_ = vt;
1406 return globals_.putNew(varName, global);
1407 }
addMathBuiltinFunction(PropertyName * varName,AsmJSMathBuiltinFunction func,PropertyName * fieldName)1408 bool addMathBuiltinFunction(PropertyName* varName, AsmJSMathBuiltinFunction func,
1409 PropertyName* fieldName)
1410 {
1411 if (!module().addMathBuiltinFunction(func, fieldName))
1412 return false;
1413 Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
1414 if (!global)
1415 return false;
1416 global->u.mathBuiltinFunc_ = func;
1417 return globals_.putNew(varName, global);
1418 }
1419 private:
addGlobalDoubleConstant(PropertyName * varName,double constant)1420 bool addGlobalDoubleConstant(PropertyName* varName, double constant) {
1421 Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
1422 if (!global)
1423 return false;
1424 global->u.varOrConst.type_ = Type::Double;
1425 global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant));
1426 return globals_.putNew(varName, global);
1427 }
1428 public:
addMathBuiltinConstant(PropertyName * varName,double constant,PropertyName * fieldName)1429 bool addMathBuiltinConstant(PropertyName* varName, double constant, PropertyName* fieldName) {
1430 if (!module().addMathBuiltinConstant(constant, fieldName))
1431 return false;
1432 return addGlobalDoubleConstant(varName, constant);
1433 }
addGlobalConstant(PropertyName * varName,double constant,PropertyName * fieldName)1434 bool addGlobalConstant(PropertyName* varName, double constant, PropertyName* fieldName) {
1435 if (!module().addGlobalConstant(constant, fieldName))
1436 return false;
1437 return addGlobalDoubleConstant(varName, constant);
1438 }
addAtomicsBuiltinFunction(PropertyName * varName,AsmJSAtomicsBuiltinFunction func,PropertyName * fieldName)1439 bool addAtomicsBuiltinFunction(PropertyName* varName, AsmJSAtomicsBuiltinFunction func,
1440 PropertyName* fieldName)
1441 {
1442 if (!module().addAtomicsBuiltinFunction(func, fieldName))
1443 return false;
1444 Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
1445 if (!global)
1446 return false;
1447 atomicsPresent_ = true;
1448 global->u.atomicsBuiltinFunc_ = func;
1449 return globals_.putNew(varName, global);
1450 }
addSimdCtor(PropertyName * varName,AsmJSSimdType type,PropertyName * fieldName)1451 bool addSimdCtor(PropertyName* varName, AsmJSSimdType type, PropertyName* fieldName) {
1452 if (!module().addSimdCtor(type, fieldName))
1453 return false;
1454 Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
1455 if (!global)
1456 return false;
1457 global->u.simdCtorType_ = type;
1458 return globals_.putNew(varName, global);
1459 }
addSimdOperation(PropertyName * varName,AsmJSSimdType type,AsmJSSimdOperation op,PropertyName * typeVarName,PropertyName * opName)1460 bool addSimdOperation(PropertyName* varName, AsmJSSimdType type, AsmJSSimdOperation op,
1461 PropertyName* typeVarName, PropertyName* opName)
1462 {
1463 if (!module().addSimdOperation(type, op, opName))
1464 return false;
1465 Global* global = validationLifo_.new_<Global>(Global::SimdOperation);
1466 if (!global)
1467 return false;
1468 global->u.simdOp.type_ = type;
1469 global->u.simdOp.which_ = op;
1470 return globals_.putNew(varName, global);
1471 }
addByteLength(PropertyName * name)1472 bool addByteLength(PropertyName* name) {
1473 canValidateChangeHeap_ = true;
1474 if (!module().addByteLength())
1475 return false;
1476 Global* global = validationLifo_.new_<Global>(Global::ByteLength);
1477 return global && globals_.putNew(name, global);
1478 }
addChangeHeap(PropertyName * name,ParseNode * fn,uint32_t mask,uint32_t min,uint32_t max)1479 bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) {
1480 hasChangeHeap_ = true;
1481 module().addChangeHeap(mask, min, max);
1482 Global* global = validationLifo_.new_<Global>(Global::ChangeHeap);
1483 if (!global)
1484 return false;
1485 global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
1486 global->u.changeHeap.srcEnd_ = fn->pn_pos.end;
1487 return globals_.putNew(name, global);
1488 }
addArrayViewCtor(PropertyName * varName,Scalar::Type vt,PropertyName * fieldName)1489 bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName) {
1490 Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
1491 if (!global)
1492 return false;
1493 if (!module().addArrayViewCtor(vt, fieldName))
1494 return false;
1495 global->u.viewInfo.viewType_ = vt;
1496 return globals_.putNew(varName, global);
1497 }
addFFI(PropertyName * varName,PropertyName * field)1498 bool addFFI(PropertyName* varName, PropertyName* field) {
1499 Global* global = validationLifo_.new_<Global>(Global::FFI);
1500 if (!global)
1501 return false;
1502 uint32_t index;
1503 if (!module().addFFI(field, &index))
1504 return false;
1505 global->u.ffiIndex_ = index;
1506 return globals_.putNew(varName, global);
1507 }
addExportedFunction(const Func & func,PropertyName * maybeFieldName)1508 bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) {
1509 MallocSig::ArgVector args;
1510 if (!args.appendAll(func.sig().args()))
1511 return false;
1512 MallocSig sig(Move(args), func.sig().ret());
1513 return module().addExportedFunction(func.name(), func.index(), func.srcBegin(),
1514 func.srcEnd(), maybeFieldName, Move(sig));
1515 }
addExportedChangeHeap(PropertyName * name,const Global & g,PropertyName * maybeFieldName)1516 bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
1517 return module().addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(),
1518 maybeFieldName);
1519 }
1520 private:
getLifoSig(const LifoSig & sig)1521 const LifoSig* getLifoSig(const LifoSig& sig) {
1522 return &sig;
1523 }
getLifoSig(const MallocSig & sig)1524 const LifoSig* getLifoSig(const MallocSig& sig) {
1525 return mg_.newLifoSig(sig);
1526 }
1527 public:
addFunction(PropertyName * name,uint32_t firstUse,const MallocSig & sig,Func ** func)1528 bool addFunction(PropertyName* name, uint32_t firstUse, const MallocSig& sig, Func** func) {
1529 uint32_t funcIndex = numFunctions();
1530 Global* global = validationLifo_.new_<Global>(Global::Function);
1531 if (!global)
1532 return false;
1533 global->u.funcIndex_ = funcIndex;
1534 if (!globals_.putNew(name, global))
1535 return false;
1536 const LifoSig* lifoSig = getLifoSig(sig);
1537 if (!lifoSig)
1538 return false;
1539 *func = validationLifo_.new_<Func>(name, firstUse, *lifoSig, funcIndex);
1540 if (!*func)
1541 return false;
1542 return functions_.append(*func);
1543 }
1544 template <class SigT>
declareFuncPtrTable(PropertyName * name,uint32_t firstUse,SigT & sig,uint32_t mask,uint32_t * index)1545 bool declareFuncPtrTable(PropertyName* name, uint32_t firstUse, SigT& sig, uint32_t mask,
1546 uint32_t* index)
1547 {
1548 if (!mg_.declareFuncPtrTable(/* numElems = */ mask + 1, index))
1549 return false;
1550 MOZ_ASSERT(*index == numFuncPtrTables());
1551 Global* global = validationLifo_.new_<Global>(Global::FuncPtrTable);
1552 if (!global)
1553 return false;
1554 global->u.funcPtrTableIndex_ = *index;
1555 if (!globals_.putNew(name, global))
1556 return false;
1557 const LifoSig* lifoSig = getLifoSig(sig);
1558 if (!lifoSig)
1559 return false;
1560 FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(cx_, name, firstUse, *lifoSig, mask);
1561 return t && funcPtrTables_.append(t);
1562 }
defineFuncPtrTable(uint32_t funcPtrTableIndex,ModuleGenerator::FuncIndexVector && elems)1563 bool defineFuncPtrTable(uint32_t funcPtrTableIndex, ModuleGenerator::FuncIndexVector&& elems) {
1564 FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex];
1565 if (table.defined())
1566 return false;
1567 table.define();
1568 return mg_.defineFuncPtrTable(funcPtrTableIndex, Move(elems));
1569 }
addExit(PropertyName * name,MallocSig && sig,unsigned ffiIndex,unsigned * exitIndex,const LifoSig ** lifoSig)1570 bool addExit(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* exitIndex,
1571 const LifoSig** lifoSig)
1572 {
1573 ExitDescriptor::Lookup lookup(name, sig);
1574 ExitMap::AddPtr p = exits_.lookupForAdd(lookup);
1575 if (p) {
1576 *lifoSig = &p->key().sig();
1577 *exitIndex = p->value();
1578 return true;
1579 }
1580 *lifoSig = getLifoSig(sig);
1581 if (!*lifoSig)
1582 return false;
1583 if (!module().addExit(Move(sig), ffiIndex, exitIndex))
1584 return false;
1585 return exits_.add(p, ExitDescriptor(name, **lifoSig), *exitIndex);
1586 }
1587
tryOnceToValidateChangeHeap()1588 bool tryOnceToValidateChangeHeap() {
1589 bool ret = canValidateChangeHeap_;
1590 canValidateChangeHeap_ = false;
1591 return ret;
1592 }
hasChangeHeap() const1593 bool hasChangeHeap() const {
1594 return hasChangeHeap_;
1595 }
tryRequireHeapLengthToBeAtLeast(uint32_t len)1596 bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
1597 return module().tryRequireHeapLengthToBeAtLeast(len);
1598 }
minHeapLength() const1599 uint32_t minHeapLength() const {
1600 return module().minHeapLength();
1601 }
1602
usesSharedMemory() const1603 bool usesSharedMemory() const {
1604 return atomicsPresent_;
1605 }
1606
1607 // Error handling.
hasAlreadyFailed() const1608 bool hasAlreadyFailed() const {
1609 return !!errorString_;
1610 }
1611
failOffset(uint32_t offset,const char * str)1612 bool failOffset(uint32_t offset, const char* str) {
1613 MOZ_ASSERT(!hasAlreadyFailed());
1614 MOZ_ASSERT(errorOffset_ == UINT32_MAX);
1615 MOZ_ASSERT(str);
1616 errorOffset_ = offset;
1617 errorString_ = DuplicateString(cx_, str);
1618 return false;
1619 }
1620
fail(ParseNode * pn,const char * str)1621 bool fail(ParseNode* pn, const char* str) {
1622 return failOffset(pn->pn_pos.begin, str);
1623 }
1624
failfVAOffset(uint32_t offset,const char * fmt,va_list ap)1625 bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) {
1626 MOZ_ASSERT(!hasAlreadyFailed());
1627 MOZ_ASSERT(errorOffset_ == UINT32_MAX);
1628 MOZ_ASSERT(fmt);
1629 errorOffset_ = offset;
1630 errorString_.reset(JS_vsmprintf(fmt, ap));
1631 return false;
1632 }
1633
failfOffset(uint32_t offset,const char * fmt,...)1634 bool failfOffset(uint32_t offset, const char* fmt, ...) {
1635 va_list ap;
1636 va_start(ap, fmt);
1637 failfVAOffset(offset, fmt, ap);
1638 va_end(ap);
1639 return false;
1640 }
1641
failf(ParseNode * pn,const char * fmt,...)1642 bool failf(ParseNode* pn, const char* fmt, ...) {
1643 va_list ap;
1644 va_start(ap, fmt);
1645 failfVAOffset(pn->pn_pos.begin, fmt, ap);
1646 va_end(ap);
1647 return false;
1648 }
1649
failNameOffset(uint32_t offset,const char * fmt,PropertyName * name)1650 bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
1651 // This function is invoked without the caller properly rooting its locals.
1652 gc::AutoSuppressGC suppress(cx_);
1653 JSAutoByteString bytes;
1654 if (AtomToPrintableString(cx_, name, &bytes))
1655 failfOffset(offset, fmt, bytes.ptr());
1656 return false;
1657 }
1658
failName(ParseNode * pn,const char * fmt,PropertyName * name)1659 bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
1660 return failNameOffset(pn->pn_pos.begin, fmt, name);
1661 }
1662
failOverRecursed()1663 bool failOverRecursed() {
1664 errorOverRecursed_ = true;
1665 return false;
1666 }
1667
1668 // Read-only interface
cx() const1669 ExclusiveContext* cx() const { return cx_; }
moduleFunctionNode() const1670 ParseNode* moduleFunctionNode() const { return moduleFunctionNode_; }
moduleFunctionName() const1671 PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
mg()1672 ModuleGenerator& mg() { return mg_; }
module() const1673 AsmJSModule& module() const { return mg_.module(); }
parser() const1674 AsmJSParser& parser() const { return parser_; }
tokenStream() const1675 TokenStream& tokenStream() const { return parser_.tokenStream; }
supportsSimd() const1676 bool supportsSimd() const { return supportsSimd_; }
1677
numArrayViews() const1678 unsigned numArrayViews() const {
1679 return arrayViews_.length();
1680 }
arrayView(unsigned i) const1681 const ArrayView& arrayView(unsigned i) const {
1682 return arrayViews_[i];
1683 }
numFunctions() const1684 unsigned numFunctions() const {
1685 return functions_.length();
1686 }
function(unsigned i) const1687 Func& function(unsigned i) const {
1688 return *functions_[i];
1689 }
numFuncPtrTables() const1690 unsigned numFuncPtrTables() const {
1691 return funcPtrTables_.length();
1692 }
funcPtrTable(unsigned i) const1693 FuncPtrTable& funcPtrTable(unsigned i) const {
1694 return *funcPtrTables_[i];
1695 }
1696
lookupGlobal(PropertyName * name) const1697 const Global* lookupGlobal(PropertyName* name) const {
1698 if (GlobalMap::Ptr p = globals_.lookup(name))
1699 return p->value();
1700 return nullptr;
1701 }
1702
lookupFunction(PropertyName * name)1703 Func* lookupFunction(PropertyName* name) {
1704 if (GlobalMap::Ptr p = globals_.lookup(name)) {
1705 Global* value = p->value();
1706 if (value->which() == Global::Function)
1707 return functions_[value->funcIndex()];
1708 }
1709 return nullptr;
1710 }
1711
lookupStandardLibraryMathName(PropertyName * name,MathBuiltin * mathBuiltin) const1712 bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const {
1713 if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
1714 *mathBuiltin = p->value();
1715 return true;
1716 }
1717 return false;
1718 }
lookupStandardLibraryAtomicsName(PropertyName * name,AsmJSAtomicsBuiltinFunction * atomicsBuiltin) const1719 bool lookupStandardLibraryAtomicsName(PropertyName* name, AsmJSAtomicsBuiltinFunction* atomicsBuiltin) const {
1720 if (AtomicsNameMap::Ptr p = standardLibraryAtomicsNames_.lookup(name)) {
1721 *atomicsBuiltin = p->value();
1722 return true;
1723 }
1724 return false;
1725 }
lookupStandardSimdOpName(PropertyName * name,AsmJSSimdOperation * op) const1726 bool lookupStandardSimdOpName(PropertyName* name, AsmJSSimdOperation* op) const {
1727 if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) {
1728 *op = p->value();
1729 return true;
1730 }
1731 return false;
1732 }
1733
startFunctionBodies()1734 void startFunctionBodies() {
1735 if (atomicsPresent_)
1736 module().setViewsAreShared();
1737 }
1738 };
1739
1740 } // namespace
1741
1742 /*****************************************************************************/
1743 // Numeric literal utilities
1744
1745 static bool
IsNumericNonFloatLiteral(ParseNode * pn)1746 IsNumericNonFloatLiteral(ParseNode* pn)
1747 {
1748 // Note: '-' is never rolled into the number; numbers are always positive
1749 // and negations must be applied manually.
1750 return pn->isKind(PNK_NUMBER) ||
1751 (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
1752 }
1753
1754 static bool
IsCallToGlobal(ModuleValidator & m,ParseNode * pn,const ModuleValidator::Global ** global)1755 IsCallToGlobal(ModuleValidator& m, ParseNode* pn, const ModuleValidator::Global** global)
1756 {
1757 if (!pn->isKind(PNK_CALL))
1758 return false;
1759
1760 ParseNode* callee = CallCallee(pn);
1761 if (!callee->isKind(PNK_NAME))
1762 return false;
1763
1764 *global = m.lookupGlobal(callee->name());
1765 return !!*global;
1766 }
1767
1768 static bool
IsCoercionCall(ModuleValidator & m,ParseNode * pn,ValType * coerceTo,ParseNode ** coercedExpr)1769 IsCoercionCall(ModuleValidator& m, ParseNode* pn, ValType* coerceTo, ParseNode** coercedExpr)
1770 {
1771 const ModuleValidator::Global* global;
1772 if (!IsCallToGlobal(m, pn, &global))
1773 return false;
1774
1775 if (CallArgListLength(pn) != 1)
1776 return false;
1777
1778 if (coercedExpr)
1779 *coercedExpr = CallArgList(pn);
1780
1781 if (global->isMathFunction() && global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) {
1782 *coerceTo = ValType::F32;
1783 return true;
1784 }
1785
1786 if (global->isSimdOperation() && global->simdOperation() == AsmJSSimdOperation_check) {
1787 switch (global->simdOperationType()) {
1788 case AsmJSSimdType_int32x4:
1789 *coerceTo = ValType::I32x4;
1790 return true;
1791 case AsmJSSimdType_float32x4:
1792 *coerceTo = ValType::F32x4;
1793 return true;
1794 }
1795 }
1796
1797 return false;
1798 }
1799
1800 static bool
IsFloatLiteral(ModuleValidator & m,ParseNode * pn)1801 IsFloatLiteral(ModuleValidator& m, ParseNode* pn)
1802 {
1803 ParseNode* coercedExpr;
1804 ValType coerceTo;
1805 if (!IsCoercionCall(m, pn, &coerceTo, &coercedExpr))
1806 return false;
1807 // Don't fold into || to avoid clang/memcheck bug (bug 1077031).
1808 if (coerceTo != ValType::F32)
1809 return false;
1810 return IsNumericNonFloatLiteral(coercedExpr);
1811 }
1812
1813 static unsigned
SimdTypeToLength(AsmJSSimdType type)1814 SimdTypeToLength(AsmJSSimdType type)
1815 {
1816 switch (type) {
1817 case AsmJSSimdType_float32x4:
1818 case AsmJSSimdType_int32x4:
1819 return 4;
1820 }
1821 MOZ_CRASH("unexpected SIMD type");
1822 }
1823
1824 static bool
IsSimdTuple(ModuleValidator & m,ParseNode * pn,AsmJSSimdType * type)1825 IsSimdTuple(ModuleValidator& m, ParseNode* pn, AsmJSSimdType* type)
1826 {
1827 const ModuleValidator::Global* global;
1828 if (!IsCallToGlobal(m, pn, &global))
1829 return false;
1830
1831 if (!global->isSimdCtor())
1832 return false;
1833
1834 if (CallArgListLength(pn) != SimdTypeToLength(global->simdCtorType()))
1835 return false;
1836
1837 *type = global->simdCtorType();
1838 return true;
1839 }
1840
1841 static bool
1842 IsNumericLiteral(ModuleValidator& m, ParseNode* pn);
1843
1844 static NumLit
1845 ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
1846
1847 static inline bool
1848 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32);
1849
1850 static bool
IsSimdLiteral(ModuleValidator & m,ParseNode * pn)1851 IsSimdLiteral(ModuleValidator& m, ParseNode* pn)
1852 {
1853 AsmJSSimdType type;
1854 if (!IsSimdTuple(m, pn, &type))
1855 return false;
1856
1857 ParseNode* arg = CallArgList(pn);
1858 unsigned length = SimdTypeToLength(type);
1859 for (unsigned i = 0; i < length; i++) {
1860 if (!IsNumericLiteral(m, arg))
1861 return false;
1862
1863 uint32_t _;
1864 switch (type) {
1865 case AsmJSSimdType_int32x4:
1866 if (!IsLiteralInt(m, arg, &_))
1867 return false;
1868 case AsmJSSimdType_float32x4:
1869 if (!IsNumericNonFloatLiteral(arg))
1870 return false;
1871 }
1872
1873 arg = NextNode(arg);
1874 }
1875
1876 MOZ_ASSERT(arg == nullptr);
1877 return true;
1878 }
1879
1880 static bool
IsNumericLiteral(ModuleValidator & m,ParseNode * pn)1881 IsNumericLiteral(ModuleValidator& m, ParseNode* pn)
1882 {
1883 return IsNumericNonFloatLiteral(pn) ||
1884 IsFloatLiteral(m, pn) ||
1885 IsSimdLiteral(m, pn);
1886 }
1887
1888 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
1889 // productions) for the unary - and literal 42). However, the asm.js spec
1890 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
1891 // so fold the two potential parse nodes into a single double value.
1892 static double
ExtractNumericNonFloatValue(ParseNode * pn,ParseNode ** out=nullptr)1893 ExtractNumericNonFloatValue(ParseNode* pn, ParseNode** out = nullptr)
1894 {
1895 MOZ_ASSERT(IsNumericNonFloatLiteral(pn));
1896
1897 if (pn->isKind(PNK_NEG)) {
1898 pn = UnaryKid(pn);
1899 if (out)
1900 *out = pn;
1901 return -NumberNodeValue(pn);
1902 }
1903
1904 return NumberNodeValue(pn);
1905 }
1906
1907 static NumLit
ExtractSimdValue(ModuleValidator & m,ParseNode * pn)1908 ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
1909 {
1910 MOZ_ASSERT(IsSimdLiteral(m, pn));
1911
1912 AsmJSSimdType type = AsmJSSimdType_int32x4;
1913 JS_ALWAYS_TRUE(IsSimdTuple(m, pn, &type));
1914
1915 ParseNode* arg = CallArgList(pn);
1916 switch (type) {
1917 case AsmJSSimdType_int32x4: {
1918 MOZ_ASSERT(SimdTypeToLength(type) == 4);
1919 int32_t val[4];
1920 for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
1921 uint32_t u32;
1922 JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
1923 val[i] = int32_t(u32);
1924 }
1925 MOZ_ASSERT(arg== nullptr);
1926 return NumLit(NumLit::Int32x4, SimdConstant::CreateX4(val));
1927 }
1928 case AsmJSSimdType_float32x4: {
1929 MOZ_ASSERT(SimdTypeToLength(type) == 4);
1930 float val[4];
1931 for (size_t i = 0; i < 4; i++, arg = NextNode(arg))
1932 val[i] = float(ExtractNumericNonFloatValue(arg));
1933 MOZ_ASSERT(arg == nullptr);
1934 return NumLit(NumLit::Float32x4, SimdConstant::CreateX4(val));
1935 }
1936 }
1937
1938 MOZ_CRASH("Unexpected SIMD type.");
1939 }
1940
1941 static NumLit
ExtractNumericLiteral(ModuleValidator & m,ParseNode * pn)1942 ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn)
1943 {
1944 MOZ_ASSERT(IsNumericLiteral(m, pn));
1945
1946 if (pn->isKind(PNK_CALL)) {
1947 // Float literals are explicitly coerced and thus the coerced literal may be
1948 // any valid (non-float) numeric literal.
1949 if (CallArgListLength(pn) == 1) {
1950 pn = CallArgList(pn);
1951 double d = ExtractNumericNonFloatValue(pn);
1952 return NumLit(NumLit::Float, DoubleValue(d));
1953 }
1954
1955 MOZ_ASSERT(CallArgListLength(pn) == 4);
1956 return ExtractSimdValue(m, pn);
1957 }
1958
1959 double d = ExtractNumericNonFloatValue(pn, &pn);
1960
1961 // The asm.js spec syntactically distinguishes any literal containing a
1962 // decimal point or the literal -0 as having double type.
1963 if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
1964 return NumLit(NumLit::Double, DoubleValue(d));
1965
1966 // The syntactic checks above rule out these double values.
1967 MOZ_ASSERT(!IsNegativeZero(d));
1968 MOZ_ASSERT(!IsNaN(d));
1969
1970 // Although doubles can only *precisely* represent 53-bit integers, they
1971 // can *imprecisely* represent integers much bigger than an int64_t.
1972 // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
1973 // is undefined, so test against the integer bounds using doubles.
1974 if (d < double(INT32_MIN) || d > double(UINT32_MAX))
1975 return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
1976
1977 // With the above syntactic and range limitations, d is definitely an
1978 // integer in the range [INT32_MIN, UINT32_MAX] range.
1979 int64_t i64 = int64_t(d);
1980 if (i64 >= 0) {
1981 if (i64 <= INT32_MAX)
1982 return NumLit(NumLit::Fixnum, Int32Value(i64));
1983 MOZ_ASSERT(i64 <= UINT32_MAX);
1984 return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
1985 }
1986 MOZ_ASSERT(i64 >= INT32_MIN);
1987 return NumLit(NumLit::NegativeInt, Int32Value(i64));
1988 }
1989
1990 static inline bool
IsLiteralInt(NumLit lit,uint32_t * u32)1991 IsLiteralInt(NumLit lit, uint32_t* u32)
1992 {
1993 switch (lit.which()) {
1994 case NumLit::Fixnum:
1995 case NumLit::BigUnsigned:
1996 case NumLit::NegativeInt:
1997 *u32 = lit.toUint32();
1998 return true;
1999 case NumLit::Double:
2000 case NumLit::Float:
2001 case NumLit::OutOfRangeInt:
2002 case NumLit::Int32x4:
2003 case NumLit::Float32x4:
2004 return false;
2005 }
2006 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
2007 }
2008
2009 static inline bool
IsLiteralInt(ModuleValidator & m,ParseNode * pn,uint32_t * u32)2010 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32)
2011 {
2012 return IsNumericLiteral(m, pn) &&
2013 IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
2014 }
2015
2016 /*****************************************************************************/
2017
2018 namespace {
2019
2020 // Encapsulates the building of an asm bytecode function from an asm.js function
2021 // source code, packing the asm.js code into the asm bytecode form that can
2022 // be decoded and compiled with a FunctionCompiler.
2023 class MOZ_STACK_CLASS FunctionValidator
2024 {
2025 public:
2026 struct Local
2027 {
2028 ValType type;
2029 unsigned slot;
Local__anonffd339ad0911::FunctionValidator::Local2030 Local(ValType t, unsigned slot) : type(t), slot(slot) {}
2031 };
2032
2033 private:
2034 typedef HashMap<PropertyName*, Local> LocalMap;
2035 typedef HashMap<PropertyName*, uint32_t> LabelMap;
2036
2037 ModuleValidator& m_;
2038 ParseNode* fn_;
2039
2040 FunctionGenerator fg_;
2041
2042 LocalMap locals_;
2043 LabelMap labels_;
2044
2045 unsigned heapExpressionDepth_;
2046
2047 bool hasAlreadyReturned_;
2048 ExprType ret_;
2049
2050 public:
FunctionValidator(ModuleValidator & m,ParseNode * fn)2051 FunctionValidator(ModuleValidator& m, ParseNode* fn)
2052 : m_(m),
2053 fn_(fn),
2054 locals_(m.cx()),
2055 labels_(m.cx()),
2056 heapExpressionDepth_(0),
2057 hasAlreadyReturned_(false)
2058 {}
2059
m() const2060 ModuleValidator& m() const { return m_; }
module() const2061 const AsmJSModule& module() const { return m_.module(); }
funcIR() const2062 FuncIR& funcIR() const { return fg_.func(); }
cx() const2063 ExclusiveContext* cx() const { return m_.cx(); }
fn() const2064 ParseNode* fn() const { return fn_; }
2065
init(PropertyName * name,unsigned line,unsigned column)2066 bool init(PropertyName* name, unsigned line, unsigned column) {
2067 return locals_.init() &&
2068 labels_.init() &&
2069 m_.mg().startFunc(name, line, column, &fg_);
2070 }
2071
finish(uint32_t funcIndex,const LifoSig & sig,unsigned generateTime)2072 bool finish(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime) {
2073 return m_.mg().finishFunc(funcIndex, sig, generateTime, &fg_);
2074 }
2075
fail(ParseNode * pn,const char * str)2076 bool fail(ParseNode* pn, const char* str) {
2077 return m_.fail(pn, str);
2078 }
2079
failf(ParseNode * pn,const char * fmt,...)2080 bool failf(ParseNode* pn, const char* fmt, ...) {
2081 va_list ap;
2082 va_start(ap, fmt);
2083 m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
2084 va_end(ap);
2085 return false;
2086 }
2087
failName(ParseNode * pn,const char * fmt,PropertyName * name)2088 bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
2089 return m_.failName(pn, fmt, name);
2090 }
2091
2092 /***************************************************** Local scope setup */
2093
addFormal(ParseNode * pn,PropertyName * name,ValType type)2094 bool addFormal(ParseNode* pn, PropertyName* name, ValType type) {
2095 LocalMap::AddPtr p = locals_.lookupForAdd(name);
2096 if (p)
2097 return failName(pn, "duplicate local name '%s' not allowed", name);
2098 return locals_.add(p, name, Local(type, locals_.count()));
2099 }
2100
addVariable(ParseNode * pn,PropertyName * name,const NumLit & init)2101 bool addVariable(ParseNode* pn, PropertyName* name, const NumLit& init) {
2102 LocalMap::AddPtr p = locals_.lookupForAdd(name);
2103 if (p)
2104 return failName(pn, "duplicate local name '%s' not allowed", name);
2105 if (!locals_.add(p, name, Local(init.type(), locals_.count())))
2106 return false;
2107 return funcIR().addVariable(init.value());
2108 }
2109
2110 /*************************************************************************/
2111
enterHeapExpression()2112 void enterHeapExpression() {
2113 heapExpressionDepth_++;
2114 }
leaveHeapExpression()2115 void leaveHeapExpression() {
2116 MOZ_ASSERT(heapExpressionDepth_ > 0);
2117 heapExpressionDepth_--;
2118 }
canCall() const2119 bool canCall() const {
2120 return heapExpressionDepth_ == 0 || !m_.hasChangeHeap();
2121 }
2122
2123 /****************************** For consistency of returns in a function */
2124
hasAlreadyReturned() const2125 bool hasAlreadyReturned() const {
2126 return hasAlreadyReturned_;
2127 }
2128
returnedType() const2129 ExprType returnedType() const {
2130 return ret_;
2131 }
2132
setReturnedType(ExprType ret)2133 void setReturnedType(ExprType ret) {
2134 ret_ = ret;
2135 hasAlreadyReturned_ = true;
2136 }
2137
2138 /**************************************************************** Labels */
2139
lookupLabel(PropertyName * label) const2140 uint32_t lookupLabel(PropertyName* label) const {
2141 if (auto p = labels_.lookup(label))
2142 return p->value();
2143 return -1;
2144 }
2145
addLabel(PropertyName * label,uint32_t * id)2146 bool addLabel(PropertyName* label, uint32_t* id) {
2147 *id = labels_.count();
2148 return labels_.putNew(label, *id);
2149 }
2150
removeLabel(PropertyName * label)2151 void removeLabel(PropertyName* label) {
2152 auto p = labels_.lookup(label);
2153 MOZ_ASSERT(!!p);
2154 labels_.remove(p);
2155 }
2156
2157 /*************************************************** Read-only interface */
2158
lookupLocal(PropertyName * name) const2159 const Local* lookupLocal(PropertyName* name) const {
2160 if (auto p = locals_.lookup(name))
2161 return &p->value();
2162 return nullptr;
2163 }
2164
lookupGlobal(PropertyName * name) const2165 const ModuleValidator::Global* lookupGlobal(PropertyName* name) const {
2166 if (locals_.has(name))
2167 return nullptr;
2168 return m_.lookupGlobal(name);
2169 }
2170
numLocals() const2171 size_t numLocals() const { return locals_.count(); }
2172
2173 /************************************************* Packing interface */
2174
startedPacking() const2175 bool startedPacking() const {
2176 return funcIR().size() != 0;
2177 }
2178
2179 template<class T>
writeOp(T op)2180 size_t writeOp(T op) {
2181 static_assert(sizeof(T) == sizeof(uint8_t), "opcodes must be uint8");
2182 return funcIR().writeU8(uint8_t(op));
2183 }
2184
writeDebugCheckPoint()2185 void writeDebugCheckPoint() {
2186 #ifdef DEBUG
2187 writeOp(Stmt::DebugCheckPoint);
2188 #endif
2189 }
2190
writeU8(uint8_t u)2191 size_t writeU8(uint8_t u) {
2192 return funcIR().writeU8(u);
2193 }
writeU32(uint32_t u)2194 size_t writeU32(uint32_t u) {
2195 return funcIR().writeU32(u);
2196 }
writeI32(int32_t u)2197 size_t writeI32(int32_t u) {
2198 return funcIR().writeI32(u);
2199 }
2200
writeInt32Lit(int32_t i)2201 void writeInt32Lit(int32_t i) {
2202 writeOp(I32::Literal);
2203 funcIR().writeI32(i);
2204 }
2205
writeLit(NumLit lit)2206 void writeLit(NumLit lit) {
2207 switch (lit.which()) {
2208 case NumLit::Fixnum:
2209 case NumLit::NegativeInt:
2210 case NumLit::BigUnsigned:
2211 writeInt32Lit(lit.toInt32());
2212 return;
2213 case NumLit::Float:
2214 writeOp(F32::Literal);
2215 funcIR().writeF32(lit.toFloat());
2216 return;
2217 case NumLit::Double:
2218 writeOp(F64::Literal);
2219 funcIR().writeF64(lit.toDouble());
2220 return;
2221 case NumLit::Int32x4:
2222 writeOp(I32X4::Literal);
2223 funcIR().writeI32X4(lit.simdValue().asInt32x4());
2224 return;
2225 case NumLit::Float32x4:
2226 writeOp(F32X4::Literal);
2227 funcIR().writeF32X4(lit.simdValue().asFloat32x4());
2228 return;
2229 case NumLit::OutOfRangeInt:
2230 break;
2231 }
2232 MOZ_CRASH("unexpected literal type");
2233 }
2234
2235 template<class T>
patchOp(size_t pos,T stmt)2236 void patchOp(size_t pos, T stmt) {
2237 static_assert(sizeof(T) == sizeof(uint8_t), "opcodes must be uint8");
2238 funcIR().patchU8(pos, uint8_t(stmt));
2239 }
patchU8(size_t pos,uint8_t u8)2240 void patchU8(size_t pos, uint8_t u8) {
2241 funcIR().patchU8(pos, u8);
2242 }
2243 template<class T>
patch32(size_t pos,T val)2244 void patch32(size_t pos, T val) {
2245 static_assert(sizeof(T) == sizeof(uint32_t), "patch32 is used for 4-bytes long ops");
2246 funcIR().patch32(pos, val);
2247 }
patchSig(size_t pos,const LifoSig * ptr)2248 void patchSig(size_t pos, const LifoSig* ptr) {
2249 funcIR().patchSig(pos, ptr);
2250 }
2251
tempU8()2252 size_t tempU8() {
2253 return funcIR().writeU8(uint8_t(Stmt::Bad));
2254 }
tempOp()2255 size_t tempOp() {
2256 return tempU8();
2257 }
temp32()2258 size_t temp32() {
2259 size_t ret = funcIR().writeU8(uint8_t(Stmt::Bad));
2260 for (size_t i = 1; i < 4; i++)
2261 funcIR().writeU8(uint8_t(Stmt::Bad));
2262 return ret;
2263 }
tempPtr()2264 size_t tempPtr() {
2265 size_t ret = funcIR().writeU8(uint8_t(Stmt::Bad));
2266 for (size_t i = 1; i < sizeof(intptr_t); i++)
2267 funcIR().writeU8(uint8_t(Stmt::Bad));
2268 return ret;
2269 }
2270 /************************************************** End of build helpers */
2271 };
2272
2273 } /* anonymous namespace */
2274
2275 /*****************************************************************************/
2276 // asm.js type-checking and code-generation algorithm
2277
2278 static bool
CheckIdentifier(ModuleValidator & m,ParseNode * usepn,PropertyName * name)2279 CheckIdentifier(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
2280 {
2281 if (name == m.cx()->names().arguments || name == m.cx()->names().eval)
2282 return m.failName(usepn, "'%s' is not an allowed identifier", name);
2283 return true;
2284 }
2285
2286 static bool
CheckModuleLevelName(ModuleValidator & m,ParseNode * usepn,PropertyName * name)2287 CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
2288 {
2289 if (!CheckIdentifier(m, usepn, name))
2290 return false;
2291
2292 if (name == m.moduleFunctionName() ||
2293 name == m.module().globalArgumentName() ||
2294 name == m.module().importArgumentName() ||
2295 name == m.module().bufferArgumentName() ||
2296 m.lookupGlobal(name))
2297 {
2298 return m.failName(usepn, "duplicate name '%s' not allowed", name);
2299 }
2300
2301 return true;
2302 }
2303
2304 static bool
CheckFunctionHead(ModuleValidator & m,ParseNode * fn)2305 CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
2306 {
2307 JSFunction* fun = FunctionObject(fn);
2308 if (fun->hasRest())
2309 return m.fail(fn, "rest args not allowed");
2310 if (fun->isExprBody())
2311 return m.fail(fn, "expression closures not allowed");
2312 if (fn->pn_funbox->hasDestructuringArgs)
2313 return m.fail(fn, "destructuring args not allowed");
2314 return true;
2315 }
2316
2317 static bool
CheckArgument(ModuleValidator & m,ParseNode * arg,PropertyName ** name)2318 CheckArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
2319 {
2320 if (!IsDefinition(arg))
2321 return m.fail(arg, "duplicate argument name not allowed");
2322
2323 if (arg->isKind(PNK_ASSIGN))
2324 return m.fail(arg, "default arguments not allowed");
2325
2326 if (!CheckIdentifier(m, arg, arg->name()))
2327 return false;
2328
2329 *name = arg->name();
2330 return true;
2331 }
2332
2333 static bool
CheckModuleArgument(ModuleValidator & m,ParseNode * arg,PropertyName ** name)2334 CheckModuleArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
2335 {
2336 if (!CheckArgument(m, arg, name))
2337 return false;
2338
2339 if (!CheckModuleLevelName(m, arg, *name))
2340 return false;
2341
2342 return true;
2343 }
2344
2345 static bool
CheckModuleArguments(ModuleValidator & m,ParseNode * fn)2346 CheckModuleArguments(ModuleValidator& m, ParseNode* fn)
2347 {
2348 unsigned numFormals;
2349 ParseNode* arg1 = FunctionArgsList(fn, &numFormals);
2350 ParseNode* arg2 = arg1 ? NextNode(arg1) : nullptr;
2351 ParseNode* arg3 = arg2 ? NextNode(arg2) : nullptr;
2352
2353 if (numFormals > 3)
2354 return m.fail(fn, "asm.js modules takes at most 3 argument");
2355
2356 PropertyName* arg1Name = nullptr;
2357 if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name))
2358 return false;
2359 m.initGlobalArgumentName(arg1Name);
2360
2361 PropertyName* arg2Name = nullptr;
2362 if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name))
2363 return false;
2364 m.initImportArgumentName(arg2Name);
2365
2366 PropertyName* arg3Name = nullptr;
2367 if (numFormals >= 3 && !CheckModuleArgument(m, arg3, &arg3Name))
2368 return false;
2369 m.initBufferArgumentName(arg3Name);
2370
2371 return true;
2372 }
2373
2374 static bool
CheckPrecedingStatements(ModuleValidator & m,ParseNode * stmtList)2375 CheckPrecedingStatements(ModuleValidator& m, ParseNode* stmtList)
2376 {
2377 MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
2378
2379 ParseNode* stmt = ListHead(stmtList);
2380 for (unsigned i = 0, n = ListLength(stmtList); i < n; i++) {
2381 if (!IsIgnoredDirective(m.cx(), stmt))
2382 return m.fail(stmt, "invalid asm.js statement");
2383 }
2384
2385 return true;
2386 }
2387
2388 static bool
CheckGlobalVariableInitConstant(ModuleValidator & m,PropertyName * varName,ParseNode * initNode,bool isConst)2389 CheckGlobalVariableInitConstant(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
2390 bool isConst)
2391 {
2392 NumLit lit = ExtractNumericLiteral(m, initNode);
2393 if (!lit.valid())
2394 return m.fail(initNode, "global initializer is out of representable integer range");
2395
2396 return m.addGlobalVarInit(varName, lit, isConst);
2397 }
2398
2399 static bool
CheckTypeAnnotation(ModuleValidator & m,ParseNode * coercionNode,ValType * coerceTo,ParseNode ** coercedExpr=nullptr)2400 CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, ValType* coerceTo,
2401 ParseNode** coercedExpr = nullptr)
2402 {
2403 switch (coercionNode->getKind()) {
2404 case PNK_BITOR: {
2405 ParseNode* rhs = BitwiseRight(coercionNode);
2406 uint32_t i;
2407 if (!IsLiteralInt(m, rhs, &i) || i != 0)
2408 return m.fail(rhs, "must use |0 for argument/return coercion");
2409 *coerceTo = ValType::I32;
2410 if (coercedExpr)
2411 *coercedExpr = BitwiseLeft(coercionNode);
2412 return true;
2413 }
2414 case PNK_POS: {
2415 *coerceTo = ValType::F64;
2416 if (coercedExpr)
2417 *coercedExpr = UnaryKid(coercionNode);
2418 return true;
2419 }
2420 case PNK_CALL: {
2421 if (IsCoercionCall(m, coercionNode, coerceTo, coercedExpr))
2422 return true;
2423 }
2424 default:;
2425 }
2426
2427 return m.fail(coercionNode, "must be of the form +x, x|0, fround(x), or a SIMD check(x)");
2428 }
2429
2430 static bool
CheckGlobalVariableImportExpr(ModuleValidator & m,PropertyName * varName,ValType coerceTo,ParseNode * coercedExpr,bool isConst)2431 CheckGlobalVariableImportExpr(ModuleValidator& m, PropertyName* varName, ValType coerceTo,
2432 ParseNode* coercedExpr, bool isConst)
2433 {
2434 if (!coercedExpr->isKind(PNK_DOT))
2435 return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
2436
2437 ParseNode* base = DotBase(coercedExpr);
2438 PropertyName* field = DotMember(coercedExpr);
2439
2440 PropertyName* importName = m.module().importArgumentName();
2441 if (!importName)
2442 return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
2443 if (!IsUseOfName(base, importName))
2444 return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
2445
2446 return m.addGlobalVarImport(varName, field, coerceTo, isConst);
2447 }
2448
2449 static bool
CheckGlobalVariableInitImport(ModuleValidator & m,PropertyName * varName,ParseNode * initNode,bool isConst)2450 CheckGlobalVariableInitImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
2451 bool isConst)
2452 {
2453 ValType coerceTo;
2454 ParseNode* coercedExpr;
2455 if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr))
2456 return false;
2457 return CheckGlobalVariableImportExpr(m, varName, coerceTo, coercedExpr, isConst);
2458 }
2459
2460 static bool
IsArrayViewCtorName(ModuleValidator & m,PropertyName * name,Scalar::Type * type)2461 IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type)
2462 {
2463 JSAtomState& names = m.cx()->names();
2464 if (name == names.Int8Array) {
2465 *type = Scalar::Int8;
2466 } else if (name == names.Uint8Array) {
2467 *type = Scalar::Uint8;
2468 } else if (name == names.Int16Array) {
2469 *type = Scalar::Int16;
2470 } else if (name == names.Uint16Array) {
2471 *type = Scalar::Uint16;
2472 } else if (name == names.Int32Array) {
2473 *type = Scalar::Int32;
2474 } else if (name == names.Uint32Array) {
2475 *type = Scalar::Uint32;
2476 } else if (name == names.Float32Array) {
2477 *type = Scalar::Float32;
2478 } else if (name == names.Float64Array) {
2479 *type = Scalar::Float64;
2480 } else {
2481 return false;
2482 }
2483 return true;
2484 }
2485
2486 static bool
CheckNewArrayViewArgs(ModuleValidator & m,ParseNode * ctorExpr,PropertyName * bufferName)2487 CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName)
2488 {
2489 ParseNode* bufArg = NextNode(ctorExpr);
2490 if (!bufArg || NextNode(bufArg) != nullptr)
2491 return m.fail(ctorExpr, "array view constructor takes exactly one argument");
2492
2493 if (!IsUseOfName(bufArg, bufferName))
2494 return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
2495
2496 return true;
2497 }
2498
2499 static bool
CheckNewArrayView(ModuleValidator & m,PropertyName * varName,ParseNode * newExpr)2500 CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
2501 {
2502 PropertyName* globalName = m.module().globalArgumentName();
2503 if (!globalName)
2504 return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
2505
2506 PropertyName* bufferName = m.module().bufferArgumentName();
2507 if (!bufferName)
2508 return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
2509
2510 ParseNode* ctorExpr = ListHead(newExpr);
2511
2512 PropertyName* field;
2513 Scalar::Type type;
2514 if (ctorExpr->isKind(PNK_DOT)) {
2515 ParseNode* base = DotBase(ctorExpr);
2516
2517 if (!IsUseOfName(base, globalName))
2518 return m.failName(base, "expecting '%s.*Array", globalName);
2519
2520 field = DotMember(ctorExpr);
2521 if (!IsArrayViewCtorName(m, field, &type))
2522 return m.fail(ctorExpr, "could not match typed array name");
2523 } else {
2524 if (!ctorExpr->isKind(PNK_NAME))
2525 return m.fail(ctorExpr, "expecting name of imported array view constructor");
2526
2527 PropertyName* globalName = ctorExpr->name();
2528 const ModuleValidator::Global* global = m.lookupGlobal(globalName);
2529 if (!global)
2530 return m.failName(ctorExpr, "%s not found in module global scope", globalName);
2531
2532 if (global->which() != ModuleValidator::Global::ArrayViewCtor)
2533 return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
2534
2535 field = nullptr;
2536 type = global->viewType();
2537 }
2538
2539 if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
2540 return false;
2541
2542 return m.addArrayView(varName, type, field);
2543 }
2544
2545 static bool
IsSimdTypeName(ModuleValidator & m,PropertyName * name,AsmJSSimdType * type)2546 IsSimdTypeName(ModuleValidator& m, PropertyName* name, AsmJSSimdType* type)
2547 {
2548 if (name == m.cx()->names().int32x4) {
2549 *type = AsmJSSimdType_int32x4;
2550 return true;
2551 }
2552 if (name == m.cx()->names().float32x4) {
2553 *type = AsmJSSimdType_float32x4;
2554 return true;
2555 }
2556 return false;
2557 }
2558
2559 static bool
IsSimdValidOperationType(AsmJSSimdType type,AsmJSSimdOperation op)2560 IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
2561 {
2562 switch (op) {
2563 #define CASE(op) case AsmJSSimdOperation_##op:
2564 FOREACH_COMMONX4_SIMD_OP(CASE)
2565 return true;
2566 FOREACH_INT32X4_SIMD_OP(CASE)
2567 return type == AsmJSSimdType_int32x4;
2568 FOREACH_FLOAT32X4_SIMD_OP(CASE)
2569 return type == AsmJSSimdType_float32x4;
2570 #undef CASE
2571 }
2572 return false;
2573 }
2574
2575 static bool
CheckGlobalMathImport(ModuleValidator & m,ParseNode * initNode,PropertyName * varName,PropertyName * field)2576 CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
2577 PropertyName* field)
2578 {
2579 // Math builtin, with the form glob.Math.[[builtin]]
2580 ModuleValidator::MathBuiltin mathBuiltin;
2581 if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
2582 return m.failName(initNode, "'%s' is not a standard Math builtin", field);
2583
2584 switch (mathBuiltin.kind) {
2585 case ModuleValidator::MathBuiltin::Function:
2586 return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
2587 case ModuleValidator::MathBuiltin::Constant:
2588 return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
2589 default:
2590 break;
2591 }
2592 MOZ_CRASH("unexpected or uninitialized math builtin type");
2593 }
2594
2595 static bool
CheckGlobalAtomicsImport(ModuleValidator & m,ParseNode * initNode,PropertyName * varName,PropertyName * field)2596 CheckGlobalAtomicsImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
2597 PropertyName* field)
2598 {
2599 // Atomics builtin, with the form glob.Atomics.[[builtin]]
2600 AsmJSAtomicsBuiltinFunction func;
2601 if (!m.lookupStandardLibraryAtomicsName(field, &func))
2602 return m.failName(initNode, "'%s' is not a standard Atomics builtin", field);
2603
2604 return m.addAtomicsBuiltinFunction(varName, func, field);
2605 }
2606
2607 static bool
CheckGlobalSimdImport(ModuleValidator & m,ParseNode * initNode,PropertyName * varName,PropertyName * field)2608 CheckGlobalSimdImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
2609 PropertyName* field)
2610 {
2611 if (!m.supportsSimd())
2612 return m.fail(initNode, "SIMD is not supported on this platform");
2613
2614 // SIMD constructor, with the form glob.SIMD.[[type]]
2615 AsmJSSimdType simdType;
2616 if (!IsSimdTypeName(m, field, &simdType))
2617 return m.failName(initNode, "'%s' is not a standard SIMD type", field);
2618 return m.addSimdCtor(varName, simdType, field);
2619 }
2620
2621 static bool
CheckGlobalSimdOperationImport(ModuleValidator & m,const ModuleValidator::Global * global,ParseNode * initNode,PropertyName * varName,PropertyName * ctorVarName,PropertyName * opName)2622 CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global* global,
2623 ParseNode* initNode, PropertyName* varName, PropertyName* ctorVarName,
2624 PropertyName* opName)
2625 {
2626 AsmJSSimdType simdType = global->simdCtorType();
2627 AsmJSSimdOperation simdOp;
2628 if (!m.lookupStandardSimdOpName(opName, &simdOp))
2629 return m.failName(initNode, "'%s' is not a standard SIMD operation", opName);
2630 if (!IsSimdValidOperationType(simdType, simdOp))
2631 return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName);
2632 return m.addSimdOperation(varName, simdType, simdOp, ctorVarName, opName);
2633 }
2634
2635 static bool
CheckGlobalDotImport(ModuleValidator & m,PropertyName * varName,ParseNode * initNode)2636 CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode)
2637 {
2638 ParseNode* base = DotBase(initNode);
2639 PropertyName* field = DotMember(initNode);
2640
2641 if (base->isKind(PNK_DOT)) {
2642 ParseNode* global = DotBase(base);
2643 PropertyName* mathOrAtomicsOrSimd = DotMember(base);
2644
2645 PropertyName* globalName = m.module().globalArgumentName();
2646 if (!globalName)
2647 return m.fail(base, "import statement requires the module have a stdlib parameter");
2648
2649 if (!IsUseOfName(global, globalName)) {
2650 if (global->isKind(PNK_DOT)) {
2651 return m.failName(base, "imports can have at most two dot accesses "
2652 "(e.g. %s.Math.sin)", globalName);
2653 }
2654 return m.failName(base, "expecting %s.*", globalName);
2655 }
2656
2657 if (mathOrAtomicsOrSimd == m.cx()->names().Math)
2658 return CheckGlobalMathImport(m, initNode, varName, field);
2659 if (mathOrAtomicsOrSimd == m.cx()->names().Atomics)
2660 return CheckGlobalAtomicsImport(m, initNode, varName, field);
2661 if (mathOrAtomicsOrSimd == m.cx()->names().SIMD)
2662 return CheckGlobalSimdImport(m, initNode, varName, field);
2663 return m.failName(base, "expecting %s.{Math|SIMD}", globalName);
2664 }
2665
2666 if (!base->isKind(PNK_NAME))
2667 return m.fail(base, "expected name of variable or parameter");
2668
2669 if (base->name() == m.module().globalArgumentName()) {
2670 if (field == m.cx()->names().NaN)
2671 return m.addGlobalConstant(varName, GenericNaN(), field);
2672 if (field == m.cx()->names().Infinity)
2673 return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
2674 if (field == m.cx()->names().byteLength)
2675 return m.addByteLength(varName);
2676
2677 Scalar::Type type;
2678 if (IsArrayViewCtorName(m, field, &type))
2679 return m.addArrayViewCtor(varName, type, field);
2680
2681 return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
2682 }
2683
2684 if (base->name() == m.module().importArgumentName())
2685 return m.addFFI(varName, field);
2686
2687 const ModuleValidator::Global* global = m.lookupGlobal(base->name());
2688 if (!global)
2689 return m.failName(initNode, "%s not found in module global scope", base->name());
2690
2691 if (!global->isSimdCtor())
2692 return m.failName(base, "expecting SIMD constructor name, got %s", field);
2693
2694 return CheckGlobalSimdOperationImport(m, global, initNode, varName, base->name(), field);
2695 }
2696
2697 static bool
CheckModuleGlobal(ModuleValidator & m,ParseNode * var,bool isConst)2698 CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst)
2699 {
2700 if (!IsDefinition(var))
2701 return m.fail(var, "import variable names must be unique");
2702
2703 if (!CheckModuleLevelName(m, var, var->name()))
2704 return false;
2705
2706 ParseNode* initNode = MaybeDefinitionInitializer(var);
2707 if (!initNode)
2708 return m.fail(var, "module import needs initializer");
2709
2710 if (IsNumericLiteral(m, initNode))
2711 return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
2712
2713 if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS) || initNode->isKind(PNK_CALL))
2714 return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
2715
2716 if (initNode->isKind(PNK_NEW))
2717 return CheckNewArrayView(m, var->name(), initNode);
2718
2719 if (initNode->isKind(PNK_DOT))
2720 return CheckGlobalDotImport(m, var->name(), initNode);
2721
2722 return m.fail(initNode, "unsupported import expression");
2723 }
2724
2725 static bool
CheckModuleProcessingDirectives(ModuleValidator & m)2726 CheckModuleProcessingDirectives(ModuleValidator& m)
2727 {
2728 TokenStream& ts = m.parser().tokenStream;
2729 while (true) {
2730 bool matched;
2731 if (!ts.matchToken(&matched, TOK_STRING, TokenStream::Operand))
2732 return false;
2733 if (!matched)
2734 return true;
2735
2736 if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
2737 return m.failOffset(ts.currentToken().pos.begin, "unsupported processing directive");
2738
2739 TokenKind tt;
2740 if (!ts.getToken(&tt))
2741 return false;
2742 if (tt != TOK_SEMI) {
2743 return m.failOffset(ts.currentToken().pos.begin,
2744 "expected semicolon after string literal");
2745 }
2746 }
2747 }
2748
2749 static bool
CheckModuleGlobals(ModuleValidator & m)2750 CheckModuleGlobals(ModuleValidator& m)
2751 {
2752 while (true) {
2753 ParseNode* varStmt;
2754 if (!ParseVarOrConstStatement(m.parser(), &varStmt))
2755 return false;
2756 if (!varStmt)
2757 break;
2758 for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
2759 if (!CheckModuleGlobal(m, var, varStmt->isKind(PNK_CONST)))
2760 return false;
2761 }
2762 }
2763
2764 return true;
2765 }
2766
2767 static bool
ArgFail(FunctionValidator & f,PropertyName * argName,ParseNode * stmt)2768 ArgFail(FunctionValidator& f, PropertyName* argName, ParseNode* stmt)
2769 {
2770 return f.failName(stmt, "expecting argument type declaration for '%s' of the "
2771 "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName);
2772 }
2773
2774 static bool
CheckArgumentType(FunctionValidator & f,ParseNode * stmt,PropertyName * name,ValType * type)2775 CheckArgumentType(FunctionValidator& f, ParseNode* stmt, PropertyName* name, ValType* type)
2776 {
2777 if (!stmt || !IsExpressionStatement(stmt))
2778 return ArgFail(f, name, stmt ? stmt : f.fn());
2779
2780 ParseNode* initNode = ExpressionStatementExpr(stmt);
2781 if (!initNode || !initNode->isKind(PNK_ASSIGN))
2782 return ArgFail(f, name, stmt);
2783
2784 ParseNode* argNode = BinaryLeft(initNode);
2785 ParseNode* coercionNode = BinaryRight(initNode);
2786
2787 if (!IsUseOfName(argNode, name))
2788 return ArgFail(f, name, stmt);
2789
2790 ParseNode* coercedExpr;
2791 if (!CheckTypeAnnotation(f.m(), coercionNode, type, &coercedExpr))
2792 return false;
2793
2794 if (!IsUseOfName(coercedExpr, name))
2795 return ArgFail(f, name, stmt);
2796
2797 return true;
2798 }
2799
2800 static bool
CheckProcessingDirectives(ModuleValidator & m,ParseNode ** stmtIter)2801 CheckProcessingDirectives(ModuleValidator& m, ParseNode** stmtIter)
2802 {
2803 ParseNode* stmt = *stmtIter;
2804
2805 while (stmt && IsIgnoredDirective(m.cx(), stmt))
2806 stmt = NextNode(stmt);
2807
2808 *stmtIter = stmt;
2809 return true;
2810 }
2811
2812 static bool
CheckArguments(FunctionValidator & f,ParseNode ** stmtIter,MallocSig::ArgVector * argTypes)2813 CheckArguments(FunctionValidator& f, ParseNode** stmtIter, MallocSig::ArgVector* argTypes)
2814 {
2815 ParseNode* stmt = *stmtIter;
2816
2817 unsigned numFormals;
2818 ParseNode* argpn = FunctionArgsList(f.fn(), &numFormals);
2819
2820 for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
2821 PropertyName* name;
2822 if (!CheckArgument(f.m(), argpn, &name))
2823 return false;
2824
2825 ValType type;
2826 if (!CheckArgumentType(f, stmt, name, &type))
2827 return false;
2828
2829 if (!argTypes->append(type))
2830 return false;
2831
2832 if (!f.addFormal(argpn, name, type))
2833 return false;
2834 }
2835
2836 *stmtIter = stmt;
2837 return true;
2838 }
2839
2840 static bool
IsLiteralOrConst(FunctionValidator & f,ParseNode * pn,NumLit * lit)2841 IsLiteralOrConst(FunctionValidator& f, ParseNode* pn, NumLit* lit)
2842 {
2843 if (pn->isKind(PNK_NAME)) {
2844 const ModuleValidator::Global* global = f.lookupGlobal(pn->name());
2845 if (!global || global->which() != ModuleValidator::Global::ConstantLiteral)
2846 return false;
2847
2848 *lit = global->constLiteralValue();
2849 return true;
2850 }
2851
2852 if (!IsNumericLiteral(f.m(), pn))
2853 return false;
2854
2855 *lit = ExtractNumericLiteral(f.m(), pn);
2856 return true;
2857 }
2858
2859 static bool
CheckFinalReturn(FunctionValidator & f,ParseNode * lastNonEmptyStmt)2860 CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt)
2861 {
2862 if (!f.hasAlreadyReturned()) {
2863 f.setReturnedType(ExprType::Void);
2864 f.writeOp(Stmt::Ret);
2865 return true;
2866 }
2867
2868 if (!lastNonEmptyStmt->isKind(PNK_RETURN)) {
2869 if (!IsVoid(f.returnedType()))
2870 return f.fail(lastNonEmptyStmt, "void incompatible with previous return type");
2871
2872 f.writeOp(Stmt::Ret);
2873 return true;
2874 }
2875
2876 return true;
2877 }
2878
2879 static bool
CheckVariable(FunctionValidator & f,ParseNode * var)2880 CheckVariable(FunctionValidator& f, ParseNode* var)
2881 {
2882 if (!IsDefinition(var))
2883 return f.fail(var, "local variable names must not restate argument names");
2884
2885 PropertyName* name = var->name();
2886
2887 if (!CheckIdentifier(f.m(), var, name))
2888 return false;
2889
2890 ParseNode* initNode = MaybeDefinitionInitializer(var);
2891 if (!initNode)
2892 return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
2893
2894 NumLit lit;
2895 if (!IsLiteralOrConst(f, initNode, &lit))
2896 return f.failName(var, "var '%s' initializer must be literal or const literal", name);
2897
2898 if (!lit.valid())
2899 return f.failName(var, "var '%s' initializer out of range", name);
2900
2901 return f.addVariable(var, name, lit);
2902 }
2903
2904 static bool
CheckVariables(FunctionValidator & f,ParseNode ** stmtIter)2905 CheckVariables(FunctionValidator& f, ParseNode** stmtIter)
2906 {
2907 ParseNode* stmt = *stmtIter;
2908
2909 for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) {
2910 for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) {
2911 if (!CheckVariable(f, var))
2912 return false;
2913 }
2914 }
2915
2916 *stmtIter = stmt;
2917 return true;
2918 }
2919
2920 static bool
2921 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type);
2922
2923 static bool
CheckNumericLiteral(FunctionValidator & f,ParseNode * num,Type * type)2924 CheckNumericLiteral(FunctionValidator& f, ParseNode* num, Type* type)
2925 {
2926 NumLit lit = ExtractNumericLiteral(f.m(), num);
2927 if (!lit.valid())
2928 return f.fail(num, "numeric literal out of representable integer range");
2929 f.writeLit(lit);
2930 *type = Type::lit(lit);
2931 return true;
2932 }
2933
2934 static bool
CheckVarRef(FunctionValidator & f,ParseNode * varRef,Type * type)2935 CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
2936 {
2937 PropertyName* name = varRef->name();
2938
2939 if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
2940 switch (local->type) {
2941 case ValType::I32: f.writeOp(I32::GetLocal); break;
2942 case ValType::I64: MOZ_CRASH("no int64 in asm.js");
2943 case ValType::F32: f.writeOp(F32::GetLocal); break;
2944 case ValType::F64: f.writeOp(F64::GetLocal); break;
2945 case ValType::I32x4: f.writeOp(I32X4::GetLocal); break;
2946 case ValType::F32x4: f.writeOp(F32X4::GetLocal); break;
2947 }
2948 f.writeU32(local->slot);
2949 *type = Type::var(local->type);
2950 return true;
2951 }
2952
2953 if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
2954 switch (global->which()) {
2955 case ModuleValidator::Global::ConstantLiteral:
2956 f.writeLit(global->constLiteralValue());
2957 *type = global->varOrConstType();
2958 break;
2959 case ModuleValidator::Global::ConstantImport:
2960 case ModuleValidator::Global::Variable: {
2961 switch (global->varOrConstType().which()) {
2962 case Type::Int: f.writeOp(I32::GetGlobal); break;
2963 case Type::Double: f.writeOp(F64::GetGlobal); break;
2964 case Type::Float: f.writeOp(F32::GetGlobal); break;
2965 case Type::Int32x4: f.writeOp(I32X4::GetGlobal); break;
2966 case Type::Float32x4: f.writeOp(F32X4::GetGlobal); break;
2967 default: MOZ_CRASH("unexpected global type");
2968 }
2969
2970 f.writeU32(global->varOrConstGlobalDataOffset());
2971 f.writeU8(uint8_t(global->isConst()));
2972 *type = global->varOrConstType();
2973 break;
2974 }
2975 case ModuleValidator::Global::Function:
2976 case ModuleValidator::Global::FFI:
2977 case ModuleValidator::Global::MathBuiltinFunction:
2978 case ModuleValidator::Global::AtomicsBuiltinFunction:
2979 case ModuleValidator::Global::FuncPtrTable:
2980 case ModuleValidator::Global::ArrayView:
2981 case ModuleValidator::Global::ArrayViewCtor:
2982 case ModuleValidator::Global::SimdCtor:
2983 case ModuleValidator::Global::SimdOperation:
2984 case ModuleValidator::Global::ByteLength:
2985 case ModuleValidator::Global::ChangeHeap:
2986 return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
2987 }
2988 return true;
2989 }
2990
2991 return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
2992 }
2993
2994 static inline bool
IsLiteralOrConstInt(FunctionValidator & f,ParseNode * pn,uint32_t * u32)2995 IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn, uint32_t* u32)
2996 {
2997 NumLit lit;
2998 if (!IsLiteralOrConst(f, pn, &lit))
2999 return false;
3000
3001 return IsLiteralInt(lit, u32);
3002 }
3003
3004 static bool
FoldMaskedArrayIndex(FunctionValidator & f,ParseNode ** indexExpr,int32_t * mask,NeedsBoundsCheck * needsBoundsCheck)3005 FoldMaskedArrayIndex(FunctionValidator& f, ParseNode** indexExpr, int32_t* mask,
3006 NeedsBoundsCheck* needsBoundsCheck)
3007 {
3008 MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND));
3009
3010 ParseNode* indexNode = BitwiseLeft(*indexExpr);
3011 ParseNode* maskNode = BitwiseRight(*indexExpr);
3012
3013 uint32_t mask2;
3014 if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
3015 // Flag the access to skip the bounds check if the mask ensures that an
3016 // 'out of bounds' access can not occur based on the current heap length
3017 // constraint. The unsigned maximum of a masked index is the mask
3018 // itself, so check that the mask is not negative and compare the mask
3019 // to the known minimum heap length.
3020 if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength())
3021 *needsBoundsCheck = NO_BOUNDS_CHECK;
3022 *mask &= mask2;
3023 *indexExpr = indexNode;
3024 return true;
3025 }
3026
3027 return false;
3028 }
3029
3030 static const int32_t NoMask = -1;
3031
3032 static bool
CheckArrayAccess(FunctionValidator & f,ParseNode * viewName,ParseNode * indexExpr,Scalar::Type * viewType,NeedsBoundsCheck * needsBoundsCheck,int32_t * mask)3033 CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
3034 Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask)
3035 {
3036 *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
3037
3038 if (!viewName->isKind(PNK_NAME))
3039 return f.fail(viewName, "base of array access must be a typed array view name");
3040
3041 const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
3042 if (!global || !global->isAnyArrayView())
3043 return f.fail(viewName, "base of array access must be a typed array view name");
3044
3045 *viewType = global->viewType();
3046
3047 uint32_t index;
3048 if (IsLiteralOrConstInt(f, indexExpr, &index)) {
3049 uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
3050 if (byteOffset > INT32_MAX)
3051 return f.fail(indexExpr, "constant index out of range");
3052
3053 unsigned elementSize = TypedArrayElemSize(*viewType);
3054 if (!f.m().tryRequireHeapLengthToBeAtLeast(byteOffset + elementSize)) {
3055 return f.failf(indexExpr, "constant index outside heap size range declared by the "
3056 "change-heap function (0x%x - 0x%x)",
3057 f.m().minHeapLength(), f.m().module().maxHeapLength());
3058 }
3059
3060 *mask = NoMask;
3061 *needsBoundsCheck = NO_BOUNDS_CHECK;
3062 f.writeInt32Lit(byteOffset);
3063 return true;
3064 }
3065
3066 // Mask off the low bits to account for the clearing effect of a right shift
3067 // followed by the left shift implicit in the array access. E.g., H32[i>>2]
3068 // loses the low two bits.
3069 *mask = ~(TypedArrayElemSize(*viewType) - 1);
3070
3071 if (indexExpr->isKind(PNK_RSH)) {
3072 ParseNode* shiftAmountNode = BitwiseRight(indexExpr);
3073
3074 uint32_t shift;
3075 if (!IsLiteralInt(f.m(), shiftAmountNode, &shift))
3076 return f.failf(shiftAmountNode, "shift amount must be constant");
3077
3078 unsigned requiredShift = TypedArrayShift(*viewType);
3079 if (shift != requiredShift)
3080 return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
3081
3082 ParseNode* pointerNode = BitwiseLeft(indexExpr);
3083
3084 if (pointerNode->isKind(PNK_BITAND))
3085 FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
3086
3087 f.enterHeapExpression();
3088
3089 Type pointerType;
3090 if (!CheckExpr(f, pointerNode, &pointerType))
3091 return false;
3092
3093 f.leaveHeapExpression();
3094
3095 if (!pointerType.isIntish())
3096 return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
3097 } else {
3098 // For legacy compatibility, accept Int8/Uint8 accesses with no shift.
3099 if (TypedArrayShift(*viewType) != 0)
3100 return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
3101
3102 MOZ_ASSERT(*mask == NoMask);
3103 bool folded = false;
3104
3105 ParseNode* pointerNode = indexExpr;
3106
3107 if (pointerNode->isKind(PNK_BITAND))
3108 folded = FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
3109
3110 f.enterHeapExpression();
3111
3112 Type pointerType;
3113 if (!CheckExpr(f, pointerNode, &pointerType))
3114 return false;
3115
3116 f.leaveHeapExpression();
3117
3118 if (folded) {
3119 if (!pointerType.isIntish())
3120 return f.failf(pointerNode, "%s is not a subtype of intish", pointerType.toChars());
3121 } else {
3122 if (!pointerType.isInt())
3123 return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
3124 }
3125 }
3126
3127 return true;
3128 }
3129
3130 static bool
CheckAndPrepareArrayAccess(FunctionValidator & f,ParseNode * viewName,ParseNode * indexExpr,Scalar::Type * viewType,NeedsBoundsCheck * needsBoundsCheck,int32_t * mask)3131 CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
3132 Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask)
3133 {
3134 size_t prepareAt = f.tempOp();
3135
3136 if (!CheckArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask))
3137 return false;
3138
3139 // Don't generate the mask op if there is no need for it which could happen for
3140 // a shift of zero or a SIMD access.
3141 if (*mask != NoMask) {
3142 f.patchOp(prepareAt, I32::BitAnd);
3143 f.writeInt32Lit(*mask);
3144 } else {
3145 f.patchOp(prepareAt, I32::Id);
3146 }
3147
3148 return true;
3149 }
3150
3151 static bool
CheckLoadArray(FunctionValidator & f,ParseNode * elem,Type * type)3152 CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
3153 {
3154 Scalar::Type viewType;
3155 NeedsBoundsCheck needsBoundsCheck;
3156 int32_t mask;
3157
3158 size_t opcodeAt = f.tempOp();
3159 size_t needsBoundsCheckAt = f.tempU8();
3160
3161 if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &needsBoundsCheck, &mask))
3162 return false;
3163
3164 switch (viewType) {
3165 case Scalar::Int8: f.patchOp(opcodeAt, I32::SLoad8); break;
3166 case Scalar::Int16: f.patchOp(opcodeAt, I32::SLoad16); break;
3167 case Scalar::Int32: f.patchOp(opcodeAt, I32::SLoad32); break;
3168 case Scalar::Uint8: f.patchOp(opcodeAt, I32::ULoad8); break;
3169 case Scalar::Uint16: f.patchOp(opcodeAt, I32::ULoad16); break;
3170 case Scalar::Uint32: f.patchOp(opcodeAt, I32::ULoad32); break;
3171 case Scalar::Float32: f.patchOp(opcodeAt, F32::Load); break;
3172 case Scalar::Float64: f.patchOp(opcodeAt, F64::Load); break;
3173 default: MOZ_CRASH("unexpected scalar type");
3174 }
3175
3176 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3177
3178 switch (viewType) {
3179 case Scalar::Int8:
3180 case Scalar::Int16:
3181 case Scalar::Int32:
3182 case Scalar::Uint8:
3183 case Scalar::Uint16:
3184 case Scalar::Uint32:
3185 *type = Type::Intish;
3186 break;
3187 case Scalar::Float32:
3188 *type = Type::MaybeFloat;
3189 break;
3190 case Scalar::Float64:
3191 *type = Type::MaybeDouble;
3192 break;
3193 default: MOZ_CRASH("Unexpected array type");
3194 }
3195
3196 return true;
3197 }
3198
3199 static bool
CheckDotAccess(FunctionValidator & f,ParseNode * elem,Type * type)3200 CheckDotAccess(FunctionValidator& f, ParseNode* elem, Type* type)
3201 {
3202 MOZ_ASSERT(elem->isKind(PNK_DOT));
3203
3204 size_t opcodeAt = f.tempOp();
3205
3206 ParseNode* base = DotBase(elem);
3207 Type baseType;
3208 if (!CheckExpr(f, base, &baseType))
3209 return false;
3210 if (!baseType.isSimd())
3211 return f.failf(base, "expected SIMD type, got %s", baseType.toChars());
3212
3213 ModuleValidator& m = f.m();
3214 PropertyName* field = DotMember(elem);
3215
3216 JSAtomState& names = m.cx()->names();
3217
3218 if (field != names.signMask)
3219 return f.fail(base, "dot access field must be signMask");
3220
3221 *type = Type::Signed;
3222 switch (baseType.simdType()) {
3223 case AsmJSSimdType_int32x4: f.patchOp(opcodeAt, I32::I32X4SignMask); break;
3224 case AsmJSSimdType_float32x4: f.patchOp(opcodeAt, I32::F32X4SignMask); break;
3225 }
3226
3227 return true;
3228 }
3229
3230 static bool
CheckStoreArray(FunctionValidator & f,ParseNode * lhs,ParseNode * rhs,Type * type)3231 CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
3232 {
3233 size_t opcodeAt = f.tempOp();
3234 size_t needsBoundsCheckAt = f.tempU8();
3235
3236 Scalar::Type viewType;
3237 NeedsBoundsCheck needsBoundsCheck;
3238 int32_t mask;
3239 if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &needsBoundsCheck, &mask))
3240 return false;
3241
3242 f.enterHeapExpression();
3243
3244 Type rhsType;
3245 if (!CheckExpr(f, rhs, &rhsType))
3246 return false;
3247
3248 f.leaveHeapExpression();
3249
3250 switch (viewType) {
3251 case Scalar::Int8:
3252 case Scalar::Int16:
3253 case Scalar::Int32:
3254 case Scalar::Uint8:
3255 case Scalar::Uint16:
3256 case Scalar::Uint32:
3257 if (!rhsType.isIntish())
3258 return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
3259 break;
3260 case Scalar::Float32:
3261 if (!rhsType.isMaybeDouble() && !rhsType.isFloatish())
3262 return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars());
3263 break;
3264 case Scalar::Float64:
3265 if (!rhsType.isMaybeFloat() && !rhsType.isMaybeDouble())
3266 return f.failf(lhs, "%s is not a subtype of float? or double?", rhsType.toChars());
3267 break;
3268 default:
3269 MOZ_CRASH("Unexpected view type");
3270 }
3271
3272 switch (viewType) {
3273 case Scalar::Int8:
3274 case Scalar::Uint8:
3275 f.patchOp(opcodeAt, I32::Store8);
3276 break;
3277 case Scalar::Int16:
3278 case Scalar::Uint16:
3279 f.patchOp(opcodeAt, I32::Store16);
3280 break;
3281 case Scalar::Int32:
3282 case Scalar::Uint32:
3283 f.patchOp(opcodeAt, I32::Store32);
3284 break;
3285 case Scalar::Float32:
3286 if (rhsType.isFloatish())
3287 f.patchOp(opcodeAt, F32::StoreF32);
3288 else
3289 f.patchOp(opcodeAt, F64::StoreF32);
3290 break;
3291 case Scalar::Float64:
3292 if (rhsType.isFloatish())
3293 f.patchOp(opcodeAt, F32::StoreF64);
3294 else
3295 f.patchOp(opcodeAt, F64::StoreF64);
3296 break;
3297 default: MOZ_CRASH("unexpected scalar type");
3298 }
3299
3300 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3301
3302 *type = rhsType;
3303 return true;
3304 }
3305
3306 static bool
CheckAssignName(FunctionValidator & f,ParseNode * lhs,ParseNode * rhs,Type * type)3307 CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
3308 {
3309 RootedPropertyName name(f.cx(), lhs->name());
3310
3311 size_t opcodeAt = f.tempOp();
3312 size_t indexAt = f.temp32();
3313
3314 Type rhsType;
3315 if (!CheckExpr(f, rhs, &rhsType))
3316 return false;
3317
3318 if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) {
3319 if (!(rhsType <= lhsVar->type)) {
3320 return f.failf(lhs, "%s is not a subtype of %s",
3321 rhsType.toChars(), Type::var(lhsVar->type).toChars());
3322 }
3323
3324 switch (lhsVar->type) {
3325 case ValType::I32: f.patchOp(opcodeAt, I32::SetLocal); break;
3326 case ValType::I64: MOZ_CRASH("no int64 in asm.js");
3327 case ValType::F64: f.patchOp(opcodeAt, F64::SetLocal); break;
3328 case ValType::F32: f.patchOp(opcodeAt, F32::SetLocal); break;
3329 case ValType::I32x4: f.patchOp(opcodeAt, I32X4::SetLocal); break;
3330 case ValType::F32x4: f.patchOp(opcodeAt, F32X4::SetLocal); break;
3331 }
3332
3333 f.patch32(indexAt, lhsVar->slot);
3334 *type = rhsType;
3335 return true;
3336 }
3337
3338 if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
3339 if (global->which() != ModuleValidator::Global::Variable)
3340 return f.failName(lhs, "'%s' is not a mutable variable", name);
3341
3342 if (!(rhsType <= global->varOrConstType())) {
3343 return f.failf(lhs, "%s is not a subtype of %s",
3344 rhsType.toChars(), global->varOrConstType().toChars());
3345 }
3346
3347 switch (global->varOrConstType().which()) {
3348 case Type::Int: f.patchOp(opcodeAt, I32::SetGlobal); break;
3349 case Type::Float: f.patchOp(opcodeAt, F32::SetGlobal); break;
3350 case Type::Double: f.patchOp(opcodeAt, F64::SetGlobal); break;
3351 case Type::Int32x4: f.patchOp(opcodeAt, I32X4::SetGlobal); break;
3352 case Type::Float32x4: f.patchOp(opcodeAt, F32X4::SetGlobal); break;
3353 default: MOZ_CRASH("unexpected global type");
3354 }
3355
3356 f.patch32(indexAt, global->varOrConstGlobalDataOffset());
3357 *type = rhsType;
3358 return true;
3359 }
3360
3361 return f.failName(lhs, "'%s' not found in local or asm.js module scope", name);
3362 }
3363
3364 static bool
CheckAssign(FunctionValidator & f,ParseNode * assign,Type * type)3365 CheckAssign(FunctionValidator& f, ParseNode* assign, Type* type)
3366 {
3367 MOZ_ASSERT(assign->isKind(PNK_ASSIGN));
3368
3369 ParseNode* lhs = BinaryLeft(assign);
3370 ParseNode* rhs = BinaryRight(assign);
3371
3372 if (lhs->getKind() == PNK_ELEM)
3373 return CheckStoreArray(f, lhs, rhs, type);
3374
3375 if (lhs->getKind() == PNK_NAME)
3376 return CheckAssignName(f, lhs, rhs, type);
3377
3378 return f.fail(assign, "left-hand side of assignment must be a variable or array access");
3379 }
3380
3381 static bool
CheckMathIMul(FunctionValidator & f,ParseNode * call,Type * type)3382 CheckMathIMul(FunctionValidator& f, ParseNode* call, Type* type)
3383 {
3384 if (CallArgListLength(call) != 2)
3385 return f.fail(call, "Math.imul must be passed 2 arguments");
3386
3387 ParseNode* lhs = CallArgList(call);
3388 ParseNode* rhs = NextNode(lhs);
3389
3390 f.writeOp(I32::Mul);
3391
3392 Type lhsType;
3393 if (!CheckExpr(f, lhs, &lhsType))
3394 return false;
3395
3396 Type rhsType;
3397 if (!CheckExpr(f, rhs, &rhsType))
3398 return false;
3399
3400 if (!lhsType.isIntish())
3401 return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
3402 if (!rhsType.isIntish())
3403 return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
3404
3405 *type = Type::Signed;
3406 return true;
3407 }
3408
3409 static bool
CheckMathClz32(FunctionValidator & f,ParseNode * call,Type * type)3410 CheckMathClz32(FunctionValidator& f, ParseNode* call, Type* type)
3411 {
3412 if (CallArgListLength(call) != 1)
3413 return f.fail(call, "Math.clz32 must be passed 1 argument");
3414
3415 f.writeOp(I32::Clz);
3416
3417 ParseNode* arg = CallArgList(call);
3418
3419 Type argType;
3420 if (!CheckExpr(f, arg, &argType))
3421 return false;
3422
3423 if (!argType.isIntish())
3424 return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
3425
3426 *type = Type::Fixnum;
3427 return true;
3428 }
3429
3430 static bool
CheckMathAbs(FunctionValidator & f,ParseNode * call,Type * type)3431 CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type)
3432 {
3433 if (CallArgListLength(call) != 1)
3434 return f.fail(call, "Math.abs must be passed 1 argument");
3435
3436 ParseNode* arg = CallArgList(call);
3437
3438 size_t opcodeAt = f.tempOp();
3439
3440 Type argType;
3441 if (!CheckExpr(f, arg, &argType))
3442 return false;
3443
3444 if (argType.isSigned()) {
3445 f.patchOp(opcodeAt, I32::Abs);
3446 *type = Type::Unsigned;
3447 return true;
3448 }
3449
3450 if (argType.isMaybeDouble()) {
3451 f.patchOp(opcodeAt, F64::Abs);
3452 *type = Type::Double;
3453 return true;
3454 }
3455
3456 if (argType.isMaybeFloat()) {
3457 f.patchOp(opcodeAt, F32::Abs);
3458 *type = Type::Floatish;
3459 return true;
3460 }
3461
3462 return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
3463 }
3464
3465 static bool
CheckMathSqrt(FunctionValidator & f,ParseNode * call,Type * type)3466 CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type)
3467 {
3468 if (CallArgListLength(call) != 1)
3469 return f.fail(call, "Math.sqrt must be passed 1 argument");
3470
3471 ParseNode* arg = CallArgList(call);
3472
3473 size_t opcodeAt = f.tempOp();
3474
3475 Type argType;
3476 if (!CheckExpr(f, arg, &argType))
3477 return false;
3478
3479 if (argType.isMaybeDouble()) {
3480 f.patchOp(opcodeAt, F64::Sqrt);
3481 *type = Type::Double;
3482 return true;
3483 }
3484
3485 if (argType.isMaybeFloat()) {
3486 f.patchOp(opcodeAt, F32::Sqrt);
3487 *type = Type::Floatish;
3488 return true;
3489 }
3490
3491 return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
3492 }
3493
3494 static bool
CheckMathMinMax(FunctionValidator & f,ParseNode * callNode,bool isMax,Type * type)3495 CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* type)
3496 {
3497 if (CallArgListLength(callNode) < 2)
3498 return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
3499
3500 size_t opcodeAt = f.tempOp();
3501 size_t numArgsAt = f.tempU8();
3502
3503 ParseNode* firstArg = CallArgList(callNode);
3504 Type firstType;
3505 if (!CheckExpr(f, firstArg, &firstType))
3506 return false;
3507
3508 if (firstType.isMaybeDouble()) {
3509 *type = Type::Double;
3510 firstType = Type::MaybeDouble;
3511 f.patchOp(opcodeAt, isMax ? F64::Max : F64::Min);
3512 } else if (firstType.isMaybeFloat()) {
3513 *type = Type::Float;
3514 firstType = Type::MaybeFloat;
3515 f.patchOp(opcodeAt, isMax ? F32::Max : F32::Min);
3516 } else if (firstType.isSigned()) {
3517 *type = Type::Signed;
3518 firstType = Type::Signed;
3519 f.patchOp(opcodeAt, isMax ? I32::Max : I32::Min);
3520 } else {
3521 return f.failf(firstArg, "%s is not a subtype of double?, float? or signed",
3522 firstType.toChars());
3523 }
3524
3525 unsigned numArgs = CallArgListLength(callNode);
3526 f.patchU8(numArgsAt, numArgs);
3527
3528 ParseNode* nextArg = NextNode(firstArg);
3529 for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) {
3530 Type nextType;
3531 if (!CheckExpr(f, nextArg, &nextType))
3532 return false;
3533 if (!(nextType <= firstType))
3534 return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
3535 }
3536
3537 return true;
3538 }
3539
3540 static bool
CheckSharedArrayAtomicAccess(FunctionValidator & f,ParseNode * viewName,ParseNode * indexExpr,Scalar::Type * viewType,NeedsBoundsCheck * needsBoundsCheck,int32_t * mask)3541 CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
3542 Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck,
3543 int32_t* mask)
3544 {
3545 if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask))
3546 return false;
3547
3548 // Atomic accesses may be made on shared integer arrays only.
3549
3550 // The global will be sane, CheckArrayAccess checks it.
3551 const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
3552 if (global->which() != ModuleValidator::Global::ArrayView || !f.m().module().isSharedView())
3553 return f.fail(viewName, "base of array access must be a shared typed array view name");
3554
3555 switch (*viewType) {
3556 case Scalar::Int8:
3557 case Scalar::Int16:
3558 case Scalar::Int32:
3559 case Scalar::Uint8:
3560 case Scalar::Uint16:
3561 case Scalar::Uint32:
3562 return true;
3563 default:
3564 return f.failf(viewName, "not an integer array");
3565 }
3566
3567 return true;
3568 }
3569
3570 static bool
CheckAtomicsFence(FunctionValidator & f,ParseNode * call,Type * type)3571 CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type)
3572 {
3573 if (CallArgListLength(call) != 0)
3574 return f.fail(call, "Atomics.fence must be passed 0 arguments");
3575
3576 f.writeOp(Stmt::AtomicsFence);
3577 *type = Type::Void;
3578 return true;
3579 }
3580
3581 static bool
CheckAtomicsLoad(FunctionValidator & f,ParseNode * call,Type * type)3582 CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
3583 {
3584 if (CallArgListLength(call) != 2)
3585 return f.fail(call, "Atomics.load must be passed 2 arguments");
3586
3587 ParseNode* arrayArg = CallArgList(call);
3588 ParseNode* indexArg = NextNode(arrayArg);
3589
3590 f.writeOp(I32::AtomicsLoad);
3591 size_t needsBoundsCheckAt = f.tempU8();
3592 size_t viewTypeAt = f.tempU8();
3593
3594 Scalar::Type viewType;
3595 NeedsBoundsCheck needsBoundsCheck;
3596 int32_t mask;
3597 if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
3598 return false;
3599
3600 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3601 f.patchU8(viewTypeAt, uint8_t(viewType));
3602
3603 *type = Type::Int;
3604 return true;
3605 }
3606
3607 static bool
CheckAtomicsStore(FunctionValidator & f,ParseNode * call,Type * type)3608 CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
3609 {
3610 if (CallArgListLength(call) != 3)
3611 return f.fail(call, "Atomics.store must be passed 3 arguments");
3612
3613 ParseNode* arrayArg = CallArgList(call);
3614 ParseNode* indexArg = NextNode(arrayArg);
3615 ParseNode* valueArg = NextNode(indexArg);
3616
3617 f.writeOp(I32::AtomicsStore);
3618 size_t needsBoundsCheckAt = f.tempU8();
3619 size_t viewTypeAt = f.tempU8();
3620
3621 Scalar::Type viewType;
3622 NeedsBoundsCheck needsBoundsCheck;
3623 int32_t mask;
3624 if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
3625 return false;
3626
3627 Type rhsType;
3628 if (!CheckExpr(f, valueArg, &rhsType))
3629 return false;
3630
3631 if (!rhsType.isIntish())
3632 return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
3633
3634 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3635 f.patchU8(viewTypeAt, uint8_t(viewType));
3636
3637 *type = rhsType;
3638 return true;
3639 }
3640
3641 static bool
CheckAtomicsBinop(FunctionValidator & f,ParseNode * call,Type * type,js::jit::AtomicOp op)3642 CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op)
3643 {
3644 if (CallArgListLength(call) != 3)
3645 return f.fail(call, "Atomics binary operator must be passed 3 arguments");
3646
3647 ParseNode* arrayArg = CallArgList(call);
3648 ParseNode* indexArg = NextNode(arrayArg);
3649 ParseNode* valueArg = NextNode(indexArg);
3650
3651 f.writeOp(I32::AtomicsBinOp);
3652 size_t needsBoundsCheckAt = f.tempU8();
3653 size_t viewTypeAt = f.tempU8();
3654 f.writeU8(uint8_t(op));
3655
3656 Scalar::Type viewType;
3657 NeedsBoundsCheck needsBoundsCheck;
3658 int32_t mask;
3659 if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
3660 return false;
3661
3662 Type valueArgType;
3663 if (!CheckExpr(f, valueArg, &valueArgType))
3664 return false;
3665
3666 if (!valueArgType.isIntish())
3667 return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
3668
3669 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3670 f.patchU8(viewTypeAt, uint8_t(viewType));
3671
3672 *type = Type::Int;
3673 return true;
3674 }
3675
3676 static bool
CheckAtomicsIsLockFree(FunctionValidator & f,ParseNode * call,Type * type)3677 CheckAtomicsIsLockFree(FunctionValidator& f, ParseNode* call, Type* type)
3678 {
3679 if (CallArgListLength(call) != 1)
3680 return f.fail(call, "Atomics.isLockFree must be passed 1 argument");
3681
3682 ParseNode* sizeArg = CallArgList(call);
3683
3684 uint32_t size;
3685 if (!IsLiteralInt(f.m(), sizeArg, &size))
3686 return f.fail(sizeArg, "Atomics.isLockFree requires an integer literal argument");
3687
3688 f.writeInt32Lit(AtomicOperations::isLockfree(size));
3689 *type = Type::Int;
3690 return true;
3691 }
3692
3693 static bool
CheckAtomicsCompareExchange(FunctionValidator & f,ParseNode * call,Type * type)3694 CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type)
3695 {
3696 if (CallArgListLength(call) != 4)
3697 return f.fail(call, "Atomics.compareExchange must be passed 4 arguments");
3698
3699 ParseNode* arrayArg = CallArgList(call);
3700 ParseNode* indexArg = NextNode(arrayArg);
3701 ParseNode* oldValueArg = NextNode(indexArg);
3702 ParseNode* newValueArg = NextNode(oldValueArg);
3703
3704 f.writeOp(I32::AtomicsCompareExchange);
3705 size_t needsBoundsCheckAt = f.tempU8();
3706 size_t viewTypeAt = f.tempU8();
3707
3708 Scalar::Type viewType;
3709 NeedsBoundsCheck needsBoundsCheck;
3710 int32_t mask;
3711 if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
3712 return false;
3713
3714 Type oldValueArgType;
3715 if (!CheckExpr(f, oldValueArg, &oldValueArgType))
3716 return false;
3717
3718 Type newValueArgType;
3719 if (!CheckExpr(f, newValueArg, &newValueArgType))
3720 return false;
3721
3722 if (!oldValueArgType.isIntish())
3723 return f.failf(oldValueArg, "%s is not a subtype of intish", oldValueArgType.toChars());
3724
3725 if (!newValueArgType.isIntish())
3726 return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
3727
3728 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3729 f.patchU8(viewTypeAt, uint8_t(viewType));
3730
3731 *type = Type::Int;
3732 return true;
3733 }
3734
3735 static bool
CheckAtomicsExchange(FunctionValidator & f,ParseNode * call,Type * type)3736 CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
3737 {
3738 if (CallArgListLength(call) != 3)
3739 return f.fail(call, "Atomics.exchange must be passed 3 arguments");
3740
3741 ParseNode* arrayArg = CallArgList(call);
3742 ParseNode* indexArg = NextNode(arrayArg);
3743 ParseNode* valueArg = NextNode(indexArg);
3744
3745 f.writeOp(I32::AtomicsExchange);
3746 size_t needsBoundsCheckAt = f.tempU8();
3747 size_t viewTypeAt = f.tempU8();
3748
3749 Scalar::Type viewType;
3750 NeedsBoundsCheck needsBoundsCheck;
3751 int32_t mask;
3752 if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
3753 return false;
3754
3755 Type valueArgType;
3756 if (!CheckExpr(f, valueArg, &valueArgType))
3757 return false;
3758
3759 if (!valueArgType.isIntish())
3760 return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars());
3761
3762 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
3763 f.patchU8(viewTypeAt, uint8_t(viewType));
3764
3765 *type = Type::Int;
3766 return true;
3767 }
3768
3769 static bool
CheckAtomicsBuiltinCall(FunctionValidator & f,ParseNode * callNode,AsmJSAtomicsBuiltinFunction func,Type * type)3770 CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
3771 Type* type)
3772 {
3773 switch (func) {
3774 case AsmJSAtomicsBuiltin_compareExchange:
3775 return CheckAtomicsCompareExchange(f, callNode, type);
3776 case AsmJSAtomicsBuiltin_exchange:
3777 return CheckAtomicsExchange(f, callNode, type);
3778 case AsmJSAtomicsBuiltin_load:
3779 return CheckAtomicsLoad(f, callNode, type);
3780 case AsmJSAtomicsBuiltin_store:
3781 return CheckAtomicsStore(f, callNode, type);
3782 case AsmJSAtomicsBuiltin_fence:
3783 return CheckAtomicsFence(f, callNode, type);
3784 case AsmJSAtomicsBuiltin_add:
3785 return CheckAtomicsBinop(f, callNode, type, AtomicFetchAddOp);
3786 case AsmJSAtomicsBuiltin_sub:
3787 return CheckAtomicsBinop(f, callNode, type, AtomicFetchSubOp);
3788 case AsmJSAtomicsBuiltin_and:
3789 return CheckAtomicsBinop(f, callNode, type, AtomicFetchAndOp);
3790 case AsmJSAtomicsBuiltin_or:
3791 return CheckAtomicsBinop(f, callNode, type, AtomicFetchOrOp);
3792 case AsmJSAtomicsBuiltin_xor:
3793 return CheckAtomicsBinop(f, callNode, type, AtomicFetchXorOp);
3794 case AsmJSAtomicsBuiltin_isLockFree:
3795 return CheckAtomicsIsLockFree(f, callNode, type);
3796 default:
3797 MOZ_CRASH("unexpected atomicsBuiltin function");
3798 }
3799 }
3800
3801 typedef bool (*CheckArgType)(FunctionValidator& f, ParseNode* argNode, Type type);
3802
3803 template <CheckArgType checkArg>
3804 static bool
CheckCallArgs(FunctionValidator & f,ParseNode * callNode,MallocSig::ArgVector * args)3805 CheckCallArgs(FunctionValidator& f, ParseNode* callNode, MallocSig::ArgVector* args)
3806 {
3807 ParseNode* argNode = CallArgList(callNode);
3808 for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) {
3809 Type type;
3810 if (!CheckExpr(f, argNode, &type))
3811 return false;
3812
3813 if (!checkArg(f, argNode, type))
3814 return false;
3815
3816 if (!args->append(type.checkedValueType()))
3817 return false;
3818 }
3819 return true;
3820 }
3821
3822 template <class SigT>
3823 static bool
CheckSignatureAgainstExisting(ModuleValidator & m,ParseNode * usepn,SigT & sig,const LifoSig & existing)3824 CheckSignatureAgainstExisting(ModuleValidator& m, ParseNode* usepn, SigT& sig,
3825 const LifoSig& existing)
3826 {
3827 if (sig.args().length() != existing.args().length()) {
3828 return m.failf(usepn, "incompatible number of arguments (%u here vs. %u before)",
3829 sig.args().length(), existing.args().length());
3830 }
3831
3832 for (unsigned i = 0; i < sig.args().length(); i++) {
3833 if (sig.arg(i) != existing.arg(i)) {
3834 return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)",
3835 i, Type::var(sig.arg(i)).toChars(), Type::var(existing.arg(i)).toChars());
3836 }
3837 }
3838
3839 if (sig.ret() != existing.ret()) {
3840 return m.failf(usepn, "%s incompatible with previous return of type %s",
3841 Type::ret(sig.ret()).toChars(), Type::ret(existing.ret()).toChars());
3842 }
3843
3844 MOZ_ASSERT(sig == existing);
3845 return true;
3846 }
3847
3848 static bool
CheckFunctionSignature(ModuleValidator & m,ParseNode * usepn,const MallocSig & sig,PropertyName * name,ModuleValidator::Func ** func)3849 CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, const MallocSig& sig,
3850 PropertyName* name, ModuleValidator::Func** func)
3851 {
3852 ModuleValidator::Func* existing = m.lookupFunction(name);
3853 if (!existing) {
3854 if (!CheckModuleLevelName(m, usepn, name))
3855 return false;
3856 return m.addFunction(name, usepn->pn_pos.begin, sig, func);
3857 }
3858
3859 if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig()))
3860 return false;
3861
3862 *func = existing;
3863 return true;
3864 }
3865
3866 static bool
CheckIsVarType(FunctionValidator & f,ParseNode * argNode,Type type)3867 CheckIsVarType(FunctionValidator& f, ParseNode* argNode, Type type)
3868 {
3869 if (!type.isVarType())
3870 return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars());
3871 return true;
3872 }
3873
3874 static void
WriteCallLineCol(FunctionValidator & f,ParseNode * pn)3875 WriteCallLineCol(FunctionValidator& f, ParseNode* pn)
3876 {
3877 uint32_t line, column;
3878 f.m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
3879 f.writeU32(line);
3880 f.writeU32(column);
3881 }
3882
3883 static bool
CheckInternalCall(FunctionValidator & f,ParseNode * callNode,PropertyName * calleeName,ExprType ret,Type * type)3884 CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calleeName,
3885 ExprType ret, Type* type)
3886 {
3887 if (!f.canCall()) {
3888 return f.fail(callNode, "call expressions may not be nested inside heap expressions "
3889 "when the module contains a change-heap function");
3890 }
3891
3892 switch (ret) {
3893 case ExprType::Void: f.writeOp(Stmt::CallInternal); break;
3894 case ExprType::I32: f.writeOp(I32::CallInternal); break;
3895 case ExprType::I64: MOZ_CRASH("no int64 in asm.js");
3896 case ExprType::F32: f.writeOp(F32::CallInternal); break;
3897 case ExprType::F64: f.writeOp(F64::CallInternal); break;
3898 case ExprType::I32x4: f.writeOp(I32X4::CallInternal); break;
3899 case ExprType::F32x4: f.writeOp(F32X4::CallInternal); break;
3900 }
3901
3902 // Function's index, to find out the function's entry
3903 size_t funcIndexAt = f.temp32();
3904 // Function's signature in lifo
3905 size_t sigAt = f.tempPtr();
3906 // Call node position (asm.js specific)
3907 WriteCallLineCol(f, callNode);
3908
3909 MallocSig::ArgVector args;
3910 if (!CheckCallArgs<CheckIsVarType>(f, callNode, &args))
3911 return false;
3912
3913 MallocSig sig(Move(args), ret);
3914
3915 ModuleValidator::Func* callee;
3916 if (!CheckFunctionSignature(f.m(), callNode, sig, calleeName, &callee))
3917 return false;
3918
3919 f.patch32(funcIndexAt, callee->index());
3920 f.patchSig(sigAt, &callee->sig());
3921 *type = Type::ret(ret);
3922 return true;
3923 }
3924
3925 template <class SigT>
3926 static bool
CheckFuncPtrTableAgainstExisting(ModuleValidator & m,ParseNode * usepn,PropertyName * name,SigT & sig,unsigned mask,uint32_t * funcPtrTableIndex)3927 CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyName* name,
3928 SigT& sig, unsigned mask, uint32_t* funcPtrTableIndex)
3929 {
3930 if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
3931 if (existing->which() != ModuleValidator::Global::FuncPtrTable)
3932 return m.failName(usepn, "'%s' is not a function-pointer table", name);
3933
3934 ModuleValidator::FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex());
3935 if (mask != table.mask())
3936 return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
3937
3938 if (!CheckSignatureAgainstExisting(m, usepn, sig, table.sig()))
3939 return false;
3940
3941 *funcPtrTableIndex = existing->funcPtrTableIndex();
3942 return true;
3943 }
3944
3945 if (!CheckModuleLevelName(m, usepn, name))
3946 return false;
3947
3948 if (!m.declareFuncPtrTable(name, usepn->pn_pos.begin, sig, mask, funcPtrTableIndex))
3949 return m.fail(usepn, "table too big");
3950
3951 return true;
3952 }
3953
3954 static bool
CheckFuncPtrCall(FunctionValidator & f,ParseNode * callNode,ExprType ret,Type * type)3955 CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* type)
3956 {
3957 if (!f.canCall()) {
3958 return f.fail(callNode, "function-pointer call expressions may not be nested inside heap "
3959 "expressions when the module contains a change-heap function");
3960 }
3961
3962 ParseNode* callee = CallCallee(callNode);
3963 ParseNode* tableNode = ElemBase(callee);
3964 ParseNode* indexExpr = ElemIndex(callee);
3965
3966 if (!tableNode->isKind(PNK_NAME))
3967 return f.fail(tableNode, "expecting name of function-pointer array");
3968
3969 PropertyName* name = tableNode->name();
3970 if (const ModuleValidator::Global* existing = f.lookupGlobal(name)) {
3971 if (existing->which() != ModuleValidator::Global::FuncPtrTable)
3972 return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
3973 }
3974
3975 if (!indexExpr->isKind(PNK_BITAND))
3976 return f.fail(indexExpr, "function-pointer table index expression needs & mask");
3977
3978 ParseNode* indexNode = BitwiseLeft(indexExpr);
3979 ParseNode* maskNode = BitwiseRight(indexExpr);
3980
3981 uint32_t mask;
3982 if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
3983 return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1");
3984
3985 // Opcode
3986 switch (ret) {
3987 case ExprType::Void: f.writeOp(Stmt::CallIndirect); break;
3988 case ExprType::I32: f.writeOp(I32::CallIndirect); break;
3989 case ExprType::I64: MOZ_CRASH("no in64 in asm.js");
3990 case ExprType::F32: f.writeOp(F32::CallIndirect); break;
3991 case ExprType::F64: f.writeOp(F64::CallIndirect); break;
3992 case ExprType::I32x4: f.writeOp(I32X4::CallIndirect); break;
3993 case ExprType::F32x4: f.writeOp(F32X4::CallIndirect); break;
3994 }
3995
3996 // Table's mask
3997 f.writeU32(mask);
3998 // Global data offset
3999 size_t globalDataOffsetAt = f.temp32();
4000 // Signature
4001 size_t sigAt = f.tempPtr();
4002 // Call node position (asm.js specific)
4003 WriteCallLineCol(f, callNode);
4004
4005 Type indexType;
4006 if (!CheckExpr(f, indexNode, &indexType))
4007 return false;
4008
4009 if (!indexType.isIntish())
4010 return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars());
4011
4012 MallocSig::ArgVector args;
4013 if (!CheckCallArgs<CheckIsVarType>(f, callNode, &args))
4014 return false;
4015
4016 MallocSig sig(Move(args), ret);
4017
4018 uint32_t funcPtrTableIndex;
4019 if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, sig, mask, &funcPtrTableIndex))
4020 return false;
4021
4022 uint32_t globalDataOffset = f.m().module().funcPtrTable(funcPtrTableIndex).globalDataOffset();
4023 f.patch32(globalDataOffsetAt, globalDataOffset);
4024 f.patchSig(sigAt, &f.m().funcPtrTable(funcPtrTableIndex).sig());
4025
4026 *type = Type::ret(ret);
4027 return true;
4028 }
4029
4030 static bool
CheckIsExternType(FunctionValidator & f,ParseNode * argNode,Type type)4031 CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type)
4032 {
4033 if (!type.isExtern())
4034 return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
4035 return true;
4036 }
4037
4038 static bool
CheckFFICall(FunctionValidator & f,ParseNode * callNode,unsigned ffiIndex,ExprType ret,Type * type)4039 CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprType ret,
4040 Type* type)
4041 {
4042 if (!f.canCall()) {
4043 return f.fail(callNode, "FFI call expressions may not be nested inside heap "
4044 "expressions when the module contains a change-heap function");
4045 }
4046
4047 PropertyName* calleeName = CallCallee(callNode)->name();
4048
4049 if (ret == ExprType::F32)
4050 return f.fail(callNode, "FFI calls can't return float");
4051 if (IsSimdType(ret))
4052 return f.fail(callNode, "FFI calls can't return SIMD values");
4053
4054 switch (ret) {
4055 case ExprType::Void: f.writeOp(Stmt::CallImport); break;
4056 case ExprType::I32: f.writeOp(I32::CallImport); break;
4057 case ExprType::I64: MOZ_CRASH("no int64 in asm.js");
4058 case ExprType::F32: f.writeOp(F32::CallImport); break;
4059 case ExprType::F64: f.writeOp(F64::CallImport); break;
4060 case ExprType::I32x4: f.writeOp(I32X4::CallImport); break;
4061 case ExprType::F32x4: f.writeOp(F32X4::CallImport); break;
4062 }
4063
4064 // Global data offset
4065 size_t offsetAt = f.temp32();
4066 // Pointer to the exit's signature in the module's lifo
4067 size_t sigAt = f.tempPtr();
4068 // Call node position (asm.js specific)
4069 WriteCallLineCol(f, callNode);
4070
4071 MallocSig::ArgVector args;
4072 if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args))
4073 return false;
4074
4075 MallocSig sig(Move(args), ret);
4076
4077 unsigned exitIndex = 0;
4078 const LifoSig* lifoSig = nullptr;
4079 if (!f.m().addExit(calleeName, Move(sig), ffiIndex, &exitIndex, &lifoSig))
4080 return false;
4081
4082 JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0);
4083 f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset());
4084 f.patchSig(sigAt, lifoSig);
4085 *type = Type::ret(ret);
4086 return true;
4087 }
4088
4089 static bool
CheckFloatCoercionArg(FunctionValidator & f,ParseNode * inputNode,Type inputType,size_t opcodeAt)4090 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType,
4091 size_t opcodeAt)
4092 {
4093 if (inputType.isMaybeDouble()) {
4094 f.patchOp(opcodeAt, F32::FromF64);
4095 return true;
4096 }
4097 if (inputType.isSigned()) {
4098 f.patchOp(opcodeAt, F32::FromS32);
4099 return true;
4100 }
4101 if (inputType.isUnsigned()) {
4102 f.patchOp(opcodeAt, F32::FromU32);
4103 return true;
4104 }
4105 if (inputType.isFloatish()) {
4106 f.patchOp(opcodeAt, F32::Id);
4107 return true;
4108 }
4109
4110 return f.failf(inputNode, "%s is not a subtype of signed, unsigned, double? or floatish",
4111 inputType.toChars());
4112 }
4113
4114 static bool
4115 CheckCoercedCall(FunctionValidator& f, ParseNode* call, ExprType ret, Type* type);
4116
4117 static bool
CheckCoercionArg(FunctionValidator & f,ParseNode * arg,ValType expected,Type * type)4118 CheckCoercionArg(FunctionValidator& f, ParseNode* arg, ValType expected, Type* type)
4119 {
4120 ExprType ret = ToExprType(expected);
4121 if (arg->isKind(PNK_CALL))
4122 return CheckCoercedCall(f, arg, ret, type);
4123
4124 size_t opcodeAt = f.tempOp();
4125
4126 Type argType;
4127 if (!CheckExpr(f, arg, &argType))
4128 return false;
4129
4130 switch (expected) {
4131 case ValType::F32:
4132 if (!CheckFloatCoercionArg(f, arg, argType, opcodeAt))
4133 return false;
4134 break;
4135 case ValType::I64:
4136 MOZ_CRASH("no int64 in asm.js");
4137 case ValType::I32x4:
4138 if (!argType.isInt32x4())
4139 return f.fail(arg, "argument to SIMD int32x4 coercion isn't int32x4");
4140 f.patchOp(opcodeAt, I32X4::Id);
4141 break;
4142 case ValType::F32x4:
4143 if (!argType.isFloat32x4())
4144 return f.fail(arg, "argument to SIMD float32x4 coercion isn't float32x4");
4145 f.patchOp(opcodeAt, F32X4::Id);
4146 break;
4147 case ValType::I32:
4148 case ValType::F64:
4149 MOZ_CRASH("not call coercions");
4150 }
4151
4152 *type = Type::ret(ret);
4153 return true;
4154 }
4155
4156 static bool
CheckMathFRound(FunctionValidator & f,ParseNode * callNode,Type * type)4157 CheckMathFRound(FunctionValidator& f, ParseNode* callNode, Type* type)
4158 {
4159 if (CallArgListLength(callNode) != 1)
4160 return f.fail(callNode, "Math.fround must be passed 1 argument");
4161
4162 ParseNode* argNode = CallArgList(callNode);
4163 Type argType;
4164 if (!CheckCoercionArg(f, argNode, ValType::F32, &argType))
4165 return false;
4166
4167 MOZ_ASSERT(argType == Type::Float);
4168 *type = Type::Float;
4169 return true;
4170 }
4171
4172 static bool
CheckMathBuiltinCall(FunctionValidator & f,ParseNode * callNode,AsmJSMathBuiltinFunction func,Type * type)4173 CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
4174 Type* type)
4175 {
4176 unsigned arity = 0;
4177 F32 f32;
4178 F64 f64;
4179 switch (func) {
4180 case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, type);
4181 case AsmJSMathBuiltin_clz32: return CheckMathClz32(f, callNode, type);
4182 case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, type);
4183 case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, type);
4184 case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, type);
4185 case AsmJSMathBuiltin_min: return CheckMathMinMax(f, callNode, /* isMax = */ false, type);
4186 case AsmJSMathBuiltin_max: return CheckMathMinMax(f, callNode, /* isMax = */ true, type);
4187 case AsmJSMathBuiltin_ceil: arity = 1; f64 = F64::Ceil; f32 = F32::Ceil; break;
4188 case AsmJSMathBuiltin_floor: arity = 1; f64 = F64::Floor; f32 = F32::Floor; break;
4189 case AsmJSMathBuiltin_sin: arity = 1; f64 = F64::Sin; f32 = F32::Bad; break;
4190 case AsmJSMathBuiltin_cos: arity = 1; f64 = F64::Cos; f32 = F32::Bad; break;
4191 case AsmJSMathBuiltin_tan: arity = 1; f64 = F64::Tan; f32 = F32::Bad; break;
4192 case AsmJSMathBuiltin_asin: arity = 1; f64 = F64::Asin; f32 = F32::Bad; break;
4193 case AsmJSMathBuiltin_acos: arity = 1; f64 = F64::Acos; f32 = F32::Bad; break;
4194 case AsmJSMathBuiltin_atan: arity = 1; f64 = F64::Atan; f32 = F32::Bad; break;
4195 case AsmJSMathBuiltin_exp: arity = 1; f64 = F64::Exp; f32 = F32::Bad; break;
4196 case AsmJSMathBuiltin_log: arity = 1; f64 = F64::Log; f32 = F32::Bad; break;
4197 case AsmJSMathBuiltin_pow: arity = 2; f64 = F64::Pow; f32 = F32::Bad; break;
4198 case AsmJSMathBuiltin_atan2: arity = 2; f64 = F64::Atan2; f32 = F32::Bad; break;
4199 default: MOZ_CRASH("unexpected mathBuiltin function");
4200 }
4201
4202 unsigned actualArity = CallArgListLength(callNode);
4203 if (actualArity != arity)
4204 return f.failf(callNode, "call passed %u arguments, expected %u", actualArity, arity);
4205
4206 size_t opcodeAt = f.tempOp();
4207 // Call node position (asm.js specific)
4208 WriteCallLineCol(f, callNode);
4209
4210 Type firstType;
4211 ParseNode* argNode = CallArgList(callNode);
4212 if (!CheckExpr(f, argNode, &firstType))
4213 return false;
4214
4215 if (!firstType.isMaybeFloat() && !firstType.isMaybeDouble())
4216 return f.fail(argNode, "arguments to math call should be a subtype of double? or float?");
4217
4218 bool opIsDouble = firstType.isMaybeDouble();
4219 if (!opIsDouble && f32 == F32::Bad)
4220 return f.fail(callNode, "math builtin cannot be used as float");
4221
4222 if (opIsDouble)
4223 f.patchOp(opcodeAt, f64);
4224 else
4225 f.patchOp(opcodeAt, f32);
4226
4227 if (arity == 2) {
4228 Type secondType;
4229 argNode = NextNode(argNode);
4230 if (!CheckExpr(f, argNode, &secondType))
4231 return false;
4232
4233 if (firstType.isMaybeDouble() && !secondType.isMaybeDouble())
4234 return f.fail(argNode, "both arguments to math builtin call should be the same type");
4235 if (firstType.isMaybeFloat() && !secondType.isMaybeFloat())
4236 return f.fail(argNode, "both arguments to math builtin call should be the same type");
4237 }
4238
4239 *type = opIsDouble ? Type::Double : Type::Floatish;
4240 return true;
4241 }
4242
4243 namespace {
4244 // Include CheckSimdCallArgs in unnamed namespace to avoid MSVC name lookup bug.
4245
4246 template<class CheckArgOp>
4247 static bool
CheckSimdCallArgs(FunctionValidator & f,ParseNode * call,unsigned expectedArity,const CheckArgOp & checkArg)4248 CheckSimdCallArgs(FunctionValidator& f, ParseNode* call, unsigned expectedArity,
4249 const CheckArgOp& checkArg)
4250 {
4251 unsigned numArgs = CallArgListLength(call);
4252 if (numArgs != expectedArity)
4253 return f.failf(call, "expected %u arguments to SIMD call, got %u", expectedArity, numArgs);
4254
4255 ParseNode* arg = CallArgList(call);
4256 for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) {
4257 MOZ_ASSERT(!!arg);
4258 Type argType;
4259 if (!CheckExpr(f, arg, &argType))
4260 return false;
4261 if (!checkArg(f, arg, i, argType))
4262 return false;
4263 }
4264
4265 return true;
4266 }
4267
4268 template<class CheckArgOp>
4269 static bool
CheckSimdCallArgsPatchable(FunctionValidator & f,ParseNode * call,unsigned expectedArity,const CheckArgOp & checkArg)4270 CheckSimdCallArgsPatchable(FunctionValidator& f, ParseNode* call, unsigned expectedArity,
4271 const CheckArgOp& checkArg)
4272 {
4273 unsigned numArgs = CallArgListLength(call);
4274 if (numArgs != expectedArity)
4275 return f.failf(call, "expected %u arguments to SIMD call, got %u", expectedArity, numArgs);
4276
4277 ParseNode* arg = CallArgList(call);
4278 for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) {
4279 MOZ_ASSERT(!!arg);
4280 Type argType;
4281 size_t patchAt = f.tempOp();
4282 if (!CheckExpr(f, arg, &argType))
4283 return false;
4284 if (!checkArg(f, arg, i, argType, patchAt))
4285 return false;
4286 }
4287
4288 return true;
4289 }
4290
4291
4292 class CheckArgIsSubtypeOf
4293 {
4294 Type formalType_;
4295
4296 public:
CheckArgIsSubtypeOf(AsmJSSimdType t)4297 explicit CheckArgIsSubtypeOf(AsmJSSimdType t) : formalType_(t) {}
4298
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType) const4299 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
4300 {
4301 if (!(actualType <= formalType_)) {
4302 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
4303 formalType_.toChars());
4304 }
4305 return true;
4306 }
4307 };
4308
4309 static inline Type
SimdToCoercedScalarType(AsmJSSimdType t)4310 SimdToCoercedScalarType(AsmJSSimdType t)
4311 {
4312 switch (t) {
4313 case AsmJSSimdType_int32x4:
4314 return Type::Intish;
4315 case AsmJSSimdType_float32x4:
4316 return Type::Floatish;
4317 }
4318 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
4319 }
4320
4321 class CheckSimdScalarArgs
4322 {
4323 AsmJSSimdType simdType_;
4324 Type formalType_;
4325
4326 public:
CheckSimdScalarArgs(AsmJSSimdType simdType)4327 explicit CheckSimdScalarArgs(AsmJSSimdType simdType)
4328 : simdType_(simdType), formalType_(SimdToCoercedScalarType(simdType))
4329 {}
4330
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType,size_t patchAt) const4331 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType,
4332 size_t patchAt) const
4333 {
4334 if (!(actualType <= formalType_)) {
4335 // As a special case, accept doublelit arguments to float32x4 ops by
4336 // re-emitting them as float32 constants.
4337 if (simdType_ != AsmJSSimdType_float32x4 || !actualType.isDoubleLit()) {
4338 return f.failf(arg, "%s is not a subtype of %s%s",
4339 actualType.toChars(), formalType_.toChars(),
4340 simdType_ == AsmJSSimdType_float32x4 ? " or doublelit" : "");
4341 }
4342
4343 // We emitted a double literal and actually want a float32.
4344 MOZ_ASSERT(patchAt != size_t(-1));
4345 f.patchOp(patchAt, F32::FromF64);
4346 return true;
4347 }
4348
4349 if (patchAt == size_t(-1))
4350 return true;
4351
4352 switch (simdType_) {
4353 case AsmJSSimdType_int32x4: f.patchOp(patchAt, I32::Id); return true;
4354 case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32::Id); return true;
4355 }
4356
4357 MOZ_CRASH("unexpected simd type");
4358 }
4359 };
4360
4361 class CheckSimdSelectArgs
4362 {
4363 Type formalType_;
4364
4365 public:
CheckSimdSelectArgs(AsmJSSimdType t)4366 explicit CheckSimdSelectArgs(AsmJSSimdType t) : formalType_(t) {}
4367
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType) const4368 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
4369 {
4370 if (argIndex == 0) {
4371 // First argument of select is an int32x4 mask.
4372 if (!(actualType <= Type::Int32x4))
4373 return f.failf(arg, "%s is not a subtype of Int32x4", actualType.toChars());
4374 return true;
4375 }
4376
4377 if (!(actualType <= formalType_)) {
4378 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
4379 formalType_.toChars());
4380 }
4381 return true;
4382 }
4383 };
4384
4385 class CheckSimdVectorScalarArgs
4386 {
4387 AsmJSSimdType formalSimdType_;
4388
4389 public:
CheckSimdVectorScalarArgs(AsmJSSimdType t)4390 explicit CheckSimdVectorScalarArgs(AsmJSSimdType t) : formalSimdType_(t) {}
4391
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType,size_t patchAt=-1) const4392 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType,
4393 size_t patchAt = -1) const
4394 {
4395 MOZ_ASSERT(argIndex < 2);
4396 if (argIndex == 0) {
4397 // First argument is the vector
4398 if (!(actualType <= Type(formalSimdType_))) {
4399 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
4400 Type(formalSimdType_).toChars());
4401 }
4402
4403 if (patchAt == size_t(-1))
4404 return true;
4405
4406 switch (formalSimdType_) {
4407 case AsmJSSimdType_int32x4: f.patchOp(patchAt, I32X4::Id); return true;
4408 case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32X4::Id); return true;
4409 }
4410
4411 MOZ_CRASH("unexpected simd type");
4412 }
4413
4414 // Second argument is the scalar
4415 return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, patchAt);
4416 }
4417 };
4418
4419 class CheckSimdExtractLaneArgs
4420 {
4421 AsmJSSimdType formalSimdType_;
4422
4423 public:
CheckSimdExtractLaneArgs(AsmJSSimdType t)4424 explicit CheckSimdExtractLaneArgs(AsmJSSimdType t) : formalSimdType_(t) {}
4425
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType) const4426 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
4427 {
4428 MOZ_ASSERT(argIndex < 2);
4429 if (argIndex == 0) {
4430 // First argument is the vector
4431 if (!(actualType <= Type(formalSimdType_))) {
4432 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
4433 Type(formalSimdType_).toChars());
4434 }
4435 return true;
4436 }
4437
4438 uint32_t laneIndex;
4439 // Second argument is the lane < vector length
4440 if (!IsLiteralOrConstInt(f, arg, &laneIndex))
4441 return f.failf(arg, "lane selector should be a constant integer literal");
4442 if (laneIndex >= SimdTypeToLength(formalSimdType_))
4443 return f.failf(arg, "lane selector should be in bounds");
4444 return true;
4445 }
4446 };
4447
4448 class CheckSimdReplaceLaneArgs
4449 {
4450 AsmJSSimdType formalSimdType_;
4451
4452 public:
CheckSimdReplaceLaneArgs(AsmJSSimdType t)4453 explicit CheckSimdReplaceLaneArgs(AsmJSSimdType t) : formalSimdType_(t) {}
4454
operator ()(FunctionValidator & f,ParseNode * arg,unsigned argIndex,Type actualType,size_t patchAt) const4455 bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType,
4456 size_t patchAt) const
4457 {
4458 MOZ_ASSERT(argIndex < 3);
4459 uint32_t u32;
4460 switch (argIndex) {
4461 case 0:
4462 // First argument is the vector
4463 if (!(actualType <= Type(formalSimdType_))) {
4464 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
4465 Type(formalSimdType_).toChars());
4466 }
4467 switch (formalSimdType_) {
4468 case AsmJSSimdType_int32x4: f.patchOp(patchAt, I32X4::Id); break;
4469 case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32X4::Id); break;
4470 }
4471 return true;
4472 case 1:
4473 // Second argument is the lane (< vector length).
4474 if (!IsLiteralOrConstInt(f, arg, &u32))
4475 return f.failf(arg, "lane selector should be a constant integer literal");
4476 if (u32 >= SimdTypeToLength(formalSimdType_))
4477 return f.failf(arg, "lane selector should be in bounds");
4478 f.patchOp(patchAt, I32::Id);
4479 return true;
4480 case 2:
4481 // Third argument is the scalar
4482 return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, patchAt);
4483 }
4484 return false;
4485 }
4486 };
4487
4488 } // namespace
4489
4490 static void
SwitchPackOp(FunctionValidator & f,AsmJSSimdType type,I32X4 i32x4,F32X4 f32x4)4491 SwitchPackOp(FunctionValidator& f, AsmJSSimdType type, I32X4 i32x4, F32X4 f32x4)
4492 {
4493 switch (type) {
4494 case AsmJSSimdType_int32x4: f.writeOp(i32x4); return;
4495 case AsmJSSimdType_float32x4: f.writeOp(f32x4); return;
4496 }
4497 MOZ_CRASH("unexpected simd type");
4498 }
4499
4500 static bool
CheckSimdUnary(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,MSimdUnaryArith::Operation op,Type * type)4501 CheckSimdUnary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4502 MSimdUnaryArith::Operation op, Type* type)
4503 {
4504 SwitchPackOp(f, opType, I32X4::Unary, F32X4::Unary);
4505 f.writeU8(uint8_t(op));
4506 if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
4507 return false;
4508 *type = opType;
4509 return true;
4510 }
4511
4512 template<class OpKind>
4513 inline bool
CheckSimdBinaryGuts(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,OpKind op,Type * type)4514 CheckSimdBinaryGuts(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, OpKind op,
4515 Type* type)
4516 {
4517 f.writeU8(uint8_t(op));
4518 if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
4519 return false;
4520 *type = opType;
4521 return true;
4522 }
4523
4524 static bool
CheckSimdBinary(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,MSimdBinaryArith::Operation op,Type * type)4525 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4526 MSimdBinaryArith::Operation op, Type* type)
4527 {
4528 SwitchPackOp(f, opType, I32X4::Binary, F32X4::Binary);
4529 return CheckSimdBinaryGuts(f, call, opType, op, type);
4530 }
4531
4532 static bool
CheckSimdBinary(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,MSimdBinaryBitwise::Operation op,Type * type)4533 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4534 MSimdBinaryBitwise::Operation op, Type* type)
4535 {
4536 SwitchPackOp(f, opType, I32X4::BinaryBitwise, F32X4::BinaryBitwise);
4537 return CheckSimdBinaryGuts(f, call, opType, op, type);
4538 }
4539
4540 static bool
CheckSimdBinary(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,MSimdBinaryComp::Operation op,Type * type)4541 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4542 MSimdBinaryComp::Operation op, Type* type)
4543 {
4544 switch (opType) {
4545 case AsmJSSimdType_int32x4: f.writeOp(I32X4::BinaryCompI32X4); break;
4546 case AsmJSSimdType_float32x4: f.writeOp(I32X4::BinaryCompF32X4); break;
4547 }
4548 f.writeU8(uint8_t(op));
4549 if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
4550 return false;
4551 *type = Type::Int32x4;
4552 return true;
4553 }
4554
4555 static bool
CheckSimdBinary(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,MSimdShift::Operation op,Type * type)4556 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4557 MSimdShift::Operation op, Type* type)
4558 {
4559 f.writeOp(I32X4::BinaryShift);
4560 f.writeU8(uint8_t(op));
4561 if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType)))
4562 return false;
4563 *type = Type::Int32x4;
4564 return true;
4565 }
4566
4567 static bool
CheckSimdExtractLane(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4568 CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4569 {
4570 switch (opType) {
4571 case AsmJSSimdType_int32x4:
4572 f.writeOp(I32::I32X4ExtractLane);
4573 *type = Type::Signed;
4574 break;
4575 case AsmJSSimdType_float32x4:
4576 f.writeOp(F32::F32X4ExtractLane);
4577 *type = Type::Float;
4578 break;
4579 }
4580 return CheckSimdCallArgs(f, call, 2, CheckSimdExtractLaneArgs(opType));
4581 }
4582
4583 static bool
CheckSimdReplaceLane(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4584 CheckSimdReplaceLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4585 {
4586 SwitchPackOp(f, opType, I32X4::ReplaceLane, F32X4::ReplaceLane);
4587 if (!CheckSimdCallArgsPatchable(f, call, 3, CheckSimdReplaceLaneArgs(opType)))
4588 return false;
4589 *type = opType;
4590 return true;
4591 }
4592
4593 typedef bool IsBitCast;
4594
4595 namespace {
4596 // Include CheckSimdCast in unnamed namespace to avoid MSVC name lookup bug (due to the use of Type).
4597
4598 static bool
CheckSimdCast(FunctionValidator & f,ParseNode * call,AsmJSSimdType fromType,AsmJSSimdType toType,bool bitcast,Type * type)4599 CheckSimdCast(FunctionValidator& f, ParseNode* call, AsmJSSimdType fromType, AsmJSSimdType toType,
4600 bool bitcast, Type* type)
4601 {
4602 SwitchPackOp(f, toType,
4603 bitcast ? I32X4::FromF32X4Bits : I32X4::FromF32X4,
4604 bitcast ? F32X4::FromI32X4Bits : F32X4::FromI32X4);
4605 if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType)))
4606 return false;
4607 *type = toType;
4608 return true;
4609 }
4610
4611 } // namespace
4612
4613 static bool
CheckSimdShuffleSelectors(FunctionValidator & f,ParseNode * lane,int32_t lanes[4],uint32_t maxLane)4614 CheckSimdShuffleSelectors(FunctionValidator& f, ParseNode* lane, int32_t lanes[4], uint32_t maxLane)
4615 {
4616 for (unsigned i = 0; i < 4; i++, lane = NextNode(lane)) {
4617 uint32_t u32;
4618 if (!IsLiteralInt(f.m(), lane, &u32))
4619 return f.failf(lane, "lane selector should be a constant integer literal");
4620 if (u32 >= maxLane)
4621 return f.failf(lane, "lane selector should be less than %u", maxLane);
4622 lanes[i] = int32_t(u32);
4623 }
4624 return true;
4625 }
4626
4627 static bool
CheckSimdSwizzle(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4628 CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4629 {
4630 unsigned numArgs = CallArgListLength(call);
4631 if (numArgs != 5)
4632 return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs);
4633
4634 SwitchPackOp(f, opType, I32X4::Swizzle, F32X4::Swizzle);
4635
4636 Type retType = opType;
4637 ParseNode* vec = CallArgList(call);
4638 Type vecType;
4639 if (!CheckExpr(f, vec, &vecType))
4640 return false;
4641 if (!(vecType <= retType))
4642 return f.failf(vec, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
4643
4644 int32_t lanes[4];
4645 if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, 4))
4646 return false;
4647
4648 for (unsigned i = 0; i < 4; i++)
4649 f.writeU8(uint8_t(lanes[i]));
4650
4651 *type = retType;
4652 return true;
4653 }
4654
4655 static bool
CheckSimdShuffle(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4656 CheckSimdShuffle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4657 {
4658 unsigned numArgs = CallArgListLength(call);
4659 if (numArgs != 6)
4660 return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs);
4661
4662 SwitchPackOp(f, opType, I32X4::Shuffle, F32X4::Shuffle);
4663
4664 Type retType = opType;
4665 ParseNode* arg = CallArgList(call);
4666 for (unsigned i = 0; i < 2; i++, arg = NextNode(arg)) {
4667 Type type;
4668 if (!CheckExpr(f, arg, &type))
4669 return false;
4670 if (!(type <= retType))
4671 return f.failf(arg, "%s is not a subtype of %s", type.toChars(), retType.toChars());
4672 }
4673
4674 int32_t lanes[4];
4675 if (!CheckSimdShuffleSelectors(f, arg, lanes, 8))
4676 return false;
4677
4678 for (unsigned i = 0; i < 4; i++)
4679 f.writeU8(uint8_t(lanes[i]));
4680
4681 *type = retType;
4682 return true;
4683 }
4684
4685 static bool
CheckSimdLoadStoreArgs(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Scalar::Type * viewType,NeedsBoundsCheck * needsBoundsCheck)4686 CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4687 Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck)
4688 {
4689 ParseNode* view = CallArgList(call);
4690 if (!view->isKind(PNK_NAME))
4691 return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
4692
4693 const ModuleValidator::Global* global = f.lookupGlobal(view->name());
4694 if (!global ||
4695 global->which() != ModuleValidator::Global::ArrayView ||
4696 global->viewType() != Scalar::Uint8)
4697 {
4698 return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
4699 }
4700
4701 *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
4702
4703 switch (opType) {
4704 case AsmJSSimdType_int32x4: *viewType = Scalar::Int32x4; break;
4705 case AsmJSSimdType_float32x4: *viewType = Scalar::Float32x4; break;
4706 }
4707
4708 ParseNode* indexExpr = NextNode(view);
4709 uint32_t indexLit;
4710 if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
4711 if (indexLit > INT32_MAX)
4712 return f.fail(indexExpr, "constant index out of range");
4713
4714 if (!f.m().tryRequireHeapLengthToBeAtLeast(indexLit + Simd128DataSize)) {
4715 return f.failf(indexExpr, "constant index outside heap size range declared by the "
4716 "change-heap function (0x%x - 0x%x)",
4717 f.m().minHeapLength(), f.m().module().maxHeapLength());
4718 }
4719
4720 *needsBoundsCheck = NO_BOUNDS_CHECK;
4721 f.writeInt32Lit(indexLit);
4722 return true;
4723 }
4724
4725 f.enterHeapExpression();
4726
4727 Type indexType;
4728 if (!CheckExpr(f, indexExpr, &indexType))
4729 return false;
4730 if (!indexType.isIntish())
4731 return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
4732
4733 f.leaveHeapExpression();
4734
4735 return true;
4736 }
4737
4738 static bool
CheckSimdLoad(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,unsigned numElems,Type * type)4739 CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4740 unsigned numElems, Type* type)
4741 {
4742 unsigned numArgs = CallArgListLength(call);
4743 if (numArgs != 2)
4744 return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs);
4745
4746 SwitchPackOp(f, opType, I32X4::Load, F32X4::Load);
4747 size_t viewTypeAt = f.tempU8();
4748 size_t needsBoundsCheckAt = f.tempU8();
4749 f.writeU8(numElems);
4750
4751 Scalar::Type viewType;
4752 NeedsBoundsCheck needsBoundsCheck;
4753 if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &needsBoundsCheck))
4754 return false;
4755
4756 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
4757 f.patchU8(viewTypeAt, uint8_t(viewType));
4758
4759 *type = opType;
4760 return true;
4761 }
4762
4763 static bool
CheckSimdStore(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,unsigned numElems,Type * type)4764 CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
4765 unsigned numElems, Type* type)
4766 {
4767 unsigned numArgs = CallArgListLength(call);
4768 if (numArgs != 3)
4769 return f.failf(call, "expected 3 arguments to SIMD store, got %u", numArgs);
4770
4771 SwitchPackOp(f, opType, I32X4::Store, F32X4::Store);
4772 size_t viewTypeAt = f.tempU8();
4773 size_t needsBoundsCheckAt = f.tempU8();
4774 f.writeU8(numElems);
4775
4776 Scalar::Type viewType;
4777 NeedsBoundsCheck needsBoundsCheck;
4778 if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &needsBoundsCheck))
4779 return false;
4780
4781 Type retType = opType;
4782 ParseNode* vecExpr = NextNode(NextNode(CallArgList(call)));
4783 Type vecType;
4784 if (!CheckExpr(f, vecExpr, &vecType))
4785 return false;
4786 if (!(vecType <= retType))
4787 return f.failf(vecExpr, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
4788
4789 f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
4790 f.patchU8(viewTypeAt, uint8_t(viewType));
4791
4792 *type = vecType;
4793 return true;
4794 }
4795
4796 static bool
CheckSimdSelect(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,bool isElementWise,Type * type)4797 CheckSimdSelect(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, bool isElementWise,
4798 Type* type)
4799 {
4800 SwitchPackOp(f, opType,
4801 isElementWise ? I32X4::Select : I32X4::BitSelect,
4802 isElementWise ? F32X4::Select : F32X4::BitSelect);
4803 if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType)))
4804 return false;
4805 *type = opType;
4806 return true;
4807 }
4808
4809 static bool
CheckSimdCheck(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4810 CheckSimdCheck(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4811 {
4812 ValType coerceTo;
4813 ParseNode* argNode;
4814 if (!IsCoercionCall(f.m(), call, &coerceTo, &argNode))
4815 return f.failf(call, "expected 1 argument in call to check");
4816 return CheckCoercionArg(f, argNode, coerceTo, type);
4817 }
4818
4819 static bool
CheckSimdSplat(FunctionValidator & f,ParseNode * call,AsmJSSimdType opType,Type * type)4820 CheckSimdSplat(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
4821 {
4822 SwitchPackOp(f, opType, I32X4::Splat, F32X4::Splat);
4823 if (!CheckSimdCallArgsPatchable(f, call, 1, CheckSimdScalarArgs(opType)))
4824 return false;
4825 *type = opType;
4826 return true;
4827 }
4828
4829 static bool
CheckSimdOperationCall(FunctionValidator & f,ParseNode * call,const ModuleValidator::Global * global,Type * type)4830 CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
4831 Type* type)
4832 {
4833 MOZ_ASSERT(global->isSimdOperation());
4834
4835 AsmJSSimdType opType = global->simdOperationType();
4836
4837 switch (global->simdOperation()) {
4838 case AsmJSSimdOperation_check:
4839 return CheckSimdCheck(f, call, opType, type);
4840
4841 #define OP_CHECK_CASE_LIST_(OP) \
4842 case AsmJSSimdOperation_##OP: \
4843 return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Op_##OP, type);
4844 ARITH_COMMONX4_SIMD_OP(OP_CHECK_CASE_LIST_)
4845 BINARY_ARITH_FLOAT32X4_SIMD_OP(OP_CHECK_CASE_LIST_)
4846 #undef OP_CHECK_CASE_LIST_
4847
4848 case AsmJSSimdOperation_lessThan:
4849 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, type);
4850 case AsmJSSimdOperation_lessThanOrEqual:
4851 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, type);
4852 case AsmJSSimdOperation_equal:
4853 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, type);
4854 case AsmJSSimdOperation_notEqual:
4855 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::notEqual, type);
4856 case AsmJSSimdOperation_greaterThan:
4857 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThan, type);
4858 case AsmJSSimdOperation_greaterThanOrEqual:
4859 return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThanOrEqual, type);
4860
4861 case AsmJSSimdOperation_and:
4862 return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::and_, type);
4863 case AsmJSSimdOperation_or:
4864 return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::or_, type);
4865 case AsmJSSimdOperation_xor:
4866 return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::xor_, type);
4867
4868 case AsmJSSimdOperation_extractLane:
4869 return CheckSimdExtractLane(f, call, opType, type);
4870 case AsmJSSimdOperation_replaceLane:
4871 return CheckSimdReplaceLane(f, call, opType, type);
4872
4873 case AsmJSSimdOperation_fromInt32x4:
4874 return CheckSimdCast(f, call, AsmJSSimdType_int32x4, opType, IsBitCast(false), type);
4875 case AsmJSSimdOperation_fromFloat32x4:
4876 return CheckSimdCast(f, call, AsmJSSimdType_float32x4, opType, IsBitCast(false), type);
4877 case AsmJSSimdOperation_fromInt32x4Bits:
4878 return CheckSimdCast(f, call, AsmJSSimdType_int32x4, opType, IsBitCast(true), type);
4879 case AsmJSSimdOperation_fromFloat32x4Bits:
4880 return CheckSimdCast(f, call, AsmJSSimdType_float32x4, opType, IsBitCast(true), type);
4881
4882 case AsmJSSimdOperation_shiftLeftByScalar:
4883 return CheckSimdBinary(f, call, opType, MSimdShift::lsh, type);
4884 case AsmJSSimdOperation_shiftRightArithmeticByScalar:
4885 return CheckSimdBinary(f, call, opType, MSimdShift::rsh, type);
4886 case AsmJSSimdOperation_shiftRightLogicalByScalar:
4887 return CheckSimdBinary(f, call, opType, MSimdShift::ursh, type);
4888
4889 case AsmJSSimdOperation_abs:
4890 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::abs, type);
4891 case AsmJSSimdOperation_neg:
4892 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::neg, type);
4893 case AsmJSSimdOperation_not:
4894 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::not_, type);
4895 case AsmJSSimdOperation_sqrt:
4896 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::sqrt, type);
4897 case AsmJSSimdOperation_reciprocalApproximation:
4898 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocalApproximation, type);
4899 case AsmJSSimdOperation_reciprocalSqrtApproximation:
4900 return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocalSqrtApproximation, type);
4901
4902 case AsmJSSimdOperation_swizzle:
4903 return CheckSimdSwizzle(f, call, opType, type);
4904 case AsmJSSimdOperation_shuffle:
4905 return CheckSimdShuffle(f, call, opType, type);
4906
4907 case AsmJSSimdOperation_load:
4908 return CheckSimdLoad(f, call, opType, 4, type);
4909 case AsmJSSimdOperation_load1:
4910 return CheckSimdLoad(f, call, opType, 1, type);
4911 case AsmJSSimdOperation_load2:
4912 return CheckSimdLoad(f, call, opType, 2, type);
4913 case AsmJSSimdOperation_load3:
4914 return CheckSimdLoad(f, call, opType, 3, type);
4915 case AsmJSSimdOperation_store:
4916 return CheckSimdStore(f, call, opType, 4, type);
4917 case AsmJSSimdOperation_store1:
4918 return CheckSimdStore(f, call, opType, 1, type);
4919 case AsmJSSimdOperation_store2:
4920 return CheckSimdStore(f, call, opType, 2, type);
4921 case AsmJSSimdOperation_store3:
4922 return CheckSimdStore(f, call, opType, 3, type);
4923
4924 case AsmJSSimdOperation_selectBits:
4925 return CheckSimdSelect(f, call, opType, /*isElementWise */ false, type);
4926 case AsmJSSimdOperation_select:
4927 return CheckSimdSelect(f, call, opType, /*isElementWise */ true, type);
4928
4929 case AsmJSSimdOperation_splat:
4930 return CheckSimdSplat(f, call, opType, type);
4931 }
4932 MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
4933 }
4934
4935 static bool
CheckSimdCtorCall(FunctionValidator & f,ParseNode * call,const ModuleValidator::Global * global,Type * type)4936 CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
4937 Type* type)
4938 {
4939 MOZ_ASSERT(call->isKind(PNK_CALL));
4940
4941 AsmJSSimdType simdType = global->simdCtorType();
4942 SwitchPackOp(f, simdType, I32X4::Ctor, F32X4::Ctor);
4943
4944 unsigned length = SimdTypeToLength(simdType);
4945 if (!CheckSimdCallArgsPatchable(f, call, length, CheckSimdScalarArgs(simdType)))
4946 return false;
4947
4948 *type = simdType;
4949 return true;
4950 }
4951
4952 static bool
CheckUncoercedCall(FunctionValidator & f,ParseNode * expr,Type * type)4953 CheckUncoercedCall(FunctionValidator& f, ParseNode* expr, Type* type)
4954 {
4955 MOZ_ASSERT(expr->isKind(PNK_CALL));
4956
4957 const ModuleValidator::Global* global;
4958 if (IsCallToGlobal(f.m(), expr, &global)) {
4959 if (global->isMathFunction())
4960 return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), type);
4961 if (global->isAtomicsFunction())
4962 return CheckAtomicsBuiltinCall(f, expr, global->atomicsBuiltinFunction(), type);
4963 if (global->isSimdCtor())
4964 return CheckSimdCtorCall(f, expr, global, type);
4965 if (global->isSimdOperation())
4966 return CheckSimdOperationCall(f, expr, global, type);
4967 }
4968
4969 return f.fail(expr, "all function calls must either be calls to standard lib math functions, "
4970 "standard atomic functions, standard SIMD constructors or operations, "
4971 "ignored (via f(); or comma-expression), coerced to signed (via f()|0), "
4972 "coerced to float (via fround(f())) or coerced to double (via +f())");
4973 }
4974
4975 static bool
CoerceResult(FunctionValidator & f,ParseNode * expr,ExprType expected,Type actual,size_t patchAt,Type * type)4976 CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actual, size_t patchAt,
4977 Type* type)
4978 {
4979 // At this point, the bytecode resembles this:
4980 // | patchAt | the thing we wanted to coerce | current position |>
4981 switch (expected) {
4982 case ExprType::Void:
4983 if (actual.isIntish())
4984 f.patchOp(patchAt, Stmt::I32Expr);
4985 else if (actual.isFloatish())
4986 f.patchOp(patchAt, Stmt::F32Expr);
4987 else if (actual.isMaybeDouble())
4988 f.patchOp(patchAt, Stmt::F64Expr);
4989 else if (actual.isInt32x4())
4990 f.patchOp(patchAt, Stmt::I32X4Expr);
4991 else if (actual.isFloat32x4())
4992 f.patchOp(patchAt, Stmt::F32X4Expr);
4993 else if (actual.isVoid())
4994 f.patchOp(patchAt, Stmt::Id);
4995 else
4996 MOZ_CRASH("unhandled return type");
4997 break;
4998 case ExprType::I32:
4999 if (!actual.isIntish())
5000 return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
5001 f.patchOp(patchAt, I32::Id);
5002 break;
5003 case ExprType::I64:
5004 MOZ_CRASH("no int64 in asm.js");
5005 case ExprType::F32:
5006 if (!CheckFloatCoercionArg(f, expr, actual, patchAt))
5007 return false;
5008 break;
5009 case ExprType::F64:
5010 if (actual.isMaybeDouble())
5011 f.patchOp(patchAt, F64::Id);
5012 else if (actual.isMaybeFloat())
5013 f.patchOp(patchAt, F64::FromF32);
5014 else if (actual.isSigned())
5015 f.patchOp(patchAt, F64::FromS32);
5016 else if (actual.isUnsigned())
5017 f.patchOp(patchAt, F64::FromU32);
5018 else
5019 return f.failf(expr, "%s is not a subtype of double?, float?, signed or unsigned", actual.toChars());
5020 break;
5021 case ExprType::I32x4:
5022 if (!actual.isInt32x4())
5023 return f.failf(expr, "%s is not a subtype of int32x4", actual.toChars());
5024 f.patchOp(patchAt, I32X4::Id);
5025 break;
5026 case ExprType::F32x4:
5027 if (!actual.isFloat32x4())
5028 return f.failf(expr, "%s is not a subtype of float32x4", actual.toChars());
5029 f.patchOp(patchAt, F32X4::Id);
5030 break;
5031 }
5032
5033 *type = Type::ret(expected);
5034 return true;
5035 }
5036
5037 static bool
CheckCoercedMathBuiltinCall(FunctionValidator & f,ParseNode * callNode,AsmJSMathBuiltinFunction func,ExprType ret,Type * type)5038 CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
5039 ExprType ret, Type* type)
5040 {
5041 size_t opcodeAt = f.tempOp();
5042 Type actual;
5043 if (!CheckMathBuiltinCall(f, callNode, func, &actual))
5044 return false;
5045 return CoerceResult(f, callNode, ret, actual, opcodeAt, type);
5046 }
5047
5048 static bool
CheckCoercedSimdCall(FunctionValidator & f,ParseNode * call,const ModuleValidator::Global * global,ExprType ret,Type * type)5049 CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
5050 ExprType ret, Type* type)
5051 {
5052 size_t opcodeAt = f.tempOp();
5053
5054 Type actual;
5055 if (global->isSimdCtor()) {
5056 if (!CheckSimdCtorCall(f, call, global, &actual))
5057 return false;
5058 MOZ_ASSERT(actual.isSimd());
5059 } else {
5060 MOZ_ASSERT(global->isSimdOperation());
5061 if (!CheckSimdOperationCall(f, call, global, &actual))
5062 return false;
5063 MOZ_ASSERT_IF(global->simdOperation() != AsmJSSimdOperation_extractLane, actual.isSimd());
5064 }
5065
5066 return CoerceResult(f, call, ret, actual, opcodeAt, type);
5067 }
5068
5069 static bool
CheckCoercedAtomicsBuiltinCall(FunctionValidator & f,ParseNode * callNode,AsmJSAtomicsBuiltinFunction func,ExprType ret,Type * type)5070 CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode,
5071 AsmJSAtomicsBuiltinFunction func, ExprType ret, Type* type)
5072 {
5073 size_t opcodeAt = f.tempOp();
5074 Type actual;
5075 if (!CheckAtomicsBuiltinCall(f, callNode, func, &actual))
5076 return false;
5077 return CoerceResult(f, callNode, ret, actual, opcodeAt, type);
5078 }
5079
5080 static bool
CheckCoercedCall(FunctionValidator & f,ParseNode * call,ExprType ret,Type * type)5081 CheckCoercedCall(FunctionValidator& f, ParseNode* call, ExprType ret, Type* type)
5082 {
5083 JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
5084
5085 if (IsNumericLiteral(f.m(), call)) {
5086 size_t coerceOp = f.tempOp();
5087 NumLit lit = ExtractNumericLiteral(f.m(), call);
5088 f.writeLit(lit);
5089 return CoerceResult(f, call, ret, Type::lit(lit), coerceOp, type);
5090 }
5091
5092 ParseNode* callee = CallCallee(call);
5093
5094 if (callee->isKind(PNK_ELEM))
5095 return CheckFuncPtrCall(f, call, ret, type);
5096
5097 if (!callee->isKind(PNK_NAME))
5098 return f.fail(callee, "unexpected callee expression type");
5099
5100 PropertyName* calleeName = callee->name();
5101
5102 if (const ModuleValidator::Global* global = f.lookupGlobal(calleeName)) {
5103 switch (global->which()) {
5104 case ModuleValidator::Global::FFI:
5105 return CheckFFICall(f, call, global->ffiIndex(), ret, type);
5106 case ModuleValidator::Global::MathBuiltinFunction:
5107 return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), ret, type);
5108 case ModuleValidator::Global::AtomicsBuiltinFunction:
5109 return CheckCoercedAtomicsBuiltinCall(f, call, global->atomicsBuiltinFunction(), ret, type);
5110 case ModuleValidator::Global::ConstantLiteral:
5111 case ModuleValidator::Global::ConstantImport:
5112 case ModuleValidator::Global::Variable:
5113 case ModuleValidator::Global::FuncPtrTable:
5114 case ModuleValidator::Global::ArrayView:
5115 case ModuleValidator::Global::ArrayViewCtor:
5116 case ModuleValidator::Global::ByteLength:
5117 case ModuleValidator::Global::ChangeHeap:
5118 return f.failName(callee, "'%s' is not callable function", callee->name());
5119 case ModuleValidator::Global::SimdCtor:
5120 case ModuleValidator::Global::SimdOperation:
5121 return CheckCoercedSimdCall(f, call, global, ret, type);
5122 case ModuleValidator::Global::Function:
5123 break;
5124 }
5125 }
5126
5127 return CheckInternalCall(f, call, calleeName, ret, type);
5128 }
5129
5130 static bool
CheckPos(FunctionValidator & f,ParseNode * pos,Type * type)5131 CheckPos(FunctionValidator& f, ParseNode* pos, Type* type)
5132 {
5133 MOZ_ASSERT(pos->isKind(PNK_POS));
5134 ParseNode* operand = UnaryKid(pos);
5135
5136 if (operand->isKind(PNK_CALL))
5137 return CheckCoercedCall(f, operand, ExprType::F64, type);
5138
5139 size_t opcodeAt = f.tempOp();
5140 Type actual;
5141 if (!CheckExpr(f, operand, &actual))
5142 return false;
5143
5144 return CoerceResult(f, operand, ExprType::F64, actual, opcodeAt, type);
5145 }
5146
5147 static bool
CheckNot(FunctionValidator & f,ParseNode * expr,Type * type)5148 CheckNot(FunctionValidator& f, ParseNode* expr, Type* type)
5149 {
5150 MOZ_ASSERT(expr->isKind(PNK_NOT));
5151 ParseNode* operand = UnaryKid(expr);
5152
5153 f.writeOp(I32::Not);
5154
5155 Type operandType;
5156 if (!CheckExpr(f, operand, &operandType))
5157 return false;
5158
5159 if (!operandType.isInt())
5160 return f.failf(operand, "%s is not a subtype of int", operandType.toChars());
5161
5162 *type = Type::Int;
5163 return true;
5164 }
5165
5166 static bool
CheckNeg(FunctionValidator & f,ParseNode * expr,Type * type)5167 CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type)
5168 {
5169 MOZ_ASSERT(expr->isKind(PNK_NEG));
5170 ParseNode* operand = UnaryKid(expr);
5171
5172 size_t opcodeAt = f.tempOp();
5173
5174 Type operandType;
5175 if (!CheckExpr(f, operand, &operandType))
5176 return false;
5177
5178 if (operandType.isInt()) {
5179 f.patchOp(opcodeAt, I32::Neg);
5180 *type = Type::Intish;
5181 return true;
5182 }
5183
5184 if (operandType.isMaybeDouble()) {
5185 f.patchOp(opcodeAt, F64::Neg);
5186 *type = Type::Double;
5187 return true;
5188 }
5189
5190 if (operandType.isMaybeFloat()) {
5191 f.patchOp(opcodeAt, F32::Neg);
5192 *type = Type::Floatish;
5193 return true;
5194 }
5195
5196 return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
5197 }
5198
5199 static bool
CheckCoerceToInt(FunctionValidator & f,ParseNode * expr,Type * type)5200 CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type)
5201 {
5202 MOZ_ASSERT(expr->isKind(PNK_BITNOT));
5203 ParseNode* operand = UnaryKid(expr);
5204
5205 size_t opcodeAt = f.tempOp();
5206
5207 Type operandType;
5208 if (!CheckExpr(f, operand, &operandType))
5209 return false;
5210
5211 if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
5212 f.patchOp(opcodeAt, operandType.isMaybeDouble() ? I32::FromF64 : I32::FromF32);
5213 *type = Type::Signed;
5214 return true;
5215 }
5216
5217 if (!operandType.isIntish())
5218 return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
5219
5220 f.patchOp(opcodeAt, I32::Id);
5221 *type = Type::Signed;
5222 return true;
5223 }
5224
5225 static bool
CheckBitNot(FunctionValidator & f,ParseNode * neg,Type * type)5226 CheckBitNot(FunctionValidator& f, ParseNode* neg, Type* type)
5227 {
5228 MOZ_ASSERT(neg->isKind(PNK_BITNOT));
5229 ParseNode* operand = UnaryKid(neg);
5230
5231 if (operand->isKind(PNK_BITNOT))
5232 return CheckCoerceToInt(f, operand, type);
5233
5234 f.writeOp(I32::BitNot);
5235
5236 Type operandType;
5237 if (!CheckExpr(f, operand, &operandType))
5238 return false;
5239
5240 if (!operandType.isIntish())
5241 return f.failf(operand, "%s is not a subtype of intish", operandType.toChars());
5242
5243 *type = Type::Signed;
5244 return true;
5245 }
5246
5247 static bool
5248 CheckAsExprStatement(FunctionValidator& f, ParseNode* exprStmt);
5249
5250 static bool
CheckComma(FunctionValidator & f,ParseNode * comma,Type * type)5251 CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
5252 {
5253 MOZ_ASSERT(comma->isKind(PNK_COMMA));
5254 ParseNode* operands = ListHead(comma);
5255
5256 size_t commaAt = f.tempOp();
5257 f.writeU32(ListLength(comma));
5258
5259 ParseNode* pn = operands;
5260 for (; NextNode(pn); pn = NextNode(pn)) {
5261 if (!CheckAsExprStatement(f, pn))
5262 return false;
5263 }
5264
5265 if (!CheckExpr(f, pn, type))
5266 return false;
5267
5268 if (type->isIntish())
5269 f.patchOp(commaAt, I32::Comma);
5270 else if (type->isFloatish())
5271 f.patchOp(commaAt, F32::Comma);
5272 else if (type->isMaybeDouble())
5273 f.patchOp(commaAt, F64::Comma);
5274 else if (type->isInt32x4())
5275 f.patchOp(commaAt, I32X4::Comma);
5276 else if (type->isFloat32x4())
5277 f.patchOp(commaAt, F32X4::Comma);
5278 else
5279 MOZ_CRASH("unexpected or unimplemented expression statement");
5280
5281 return true;
5282 }
5283
5284 static bool
CheckConditional(FunctionValidator & f,ParseNode * ternary,Type * type)5285 CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
5286 {
5287 MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL));
5288
5289 size_t opcodeAt = f.tempOp();
5290
5291 ParseNode* cond = TernaryKid1(ternary);
5292 ParseNode* thenExpr = TernaryKid2(ternary);
5293 ParseNode* elseExpr = TernaryKid3(ternary);
5294
5295 Type condType;
5296 if (!CheckExpr(f, cond, &condType))
5297 return false;
5298
5299 if (!condType.isInt())
5300 return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5301
5302 Type thenType;
5303 if (!CheckExpr(f, thenExpr, &thenType))
5304 return false;
5305
5306 Type elseType;
5307 if (!CheckExpr(f, elseExpr, &elseType))
5308 return false;
5309
5310 if (thenType.isInt() && elseType.isInt()) {
5311 f.patchOp(opcodeAt, I32::Conditional);
5312 *type = Type::Int;
5313 } else if (thenType.isDouble() && elseType.isDouble()) {
5314 f.patchOp(opcodeAt, F64::Conditional);
5315 *type = Type::Double;
5316 } else if (thenType.isFloat() && elseType.isFloat()) {
5317 f.patchOp(opcodeAt, F32::Conditional);
5318 *type = Type::Float;
5319 } else if (elseType.isInt32x4() && thenType.isInt32x4()) {
5320 f.patchOp(opcodeAt, I32X4::Conditional);
5321 *type = Type::Int32x4;
5322 } else if (elseType.isFloat32x4() && thenType.isFloat32x4()) {
5323 f.patchOp(opcodeAt, F32X4::Conditional);
5324 *type = Type::Float32x4;
5325 } else {
5326 return f.failf(ternary, "then/else branches of conditional must both produce int, float, "
5327 "double or SIMD types, current types are %s and %s",
5328 thenType.toChars(), elseType.toChars());
5329 }
5330
5331 return true;
5332 }
5333
5334 static bool
IsValidIntMultiplyConstant(ModuleValidator & m,ParseNode * expr)5335 IsValidIntMultiplyConstant(ModuleValidator& m, ParseNode* expr)
5336 {
5337 if (!IsNumericLiteral(m, expr))
5338 return false;
5339
5340 NumLit lit = ExtractNumericLiteral(m, expr);
5341 switch (lit.which()) {
5342 case NumLit::Fixnum:
5343 case NumLit::NegativeInt:
5344 if (abs(lit.toInt32()) < (1<<20))
5345 return true;
5346 return false;
5347 case NumLit::BigUnsigned:
5348 case NumLit::Double:
5349 case NumLit::Float:
5350 case NumLit::OutOfRangeInt:
5351 case NumLit::Int32x4:
5352 case NumLit::Float32x4:
5353 return false;
5354 }
5355
5356 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
5357 }
5358
5359 static bool
CheckMultiply(FunctionValidator & f,ParseNode * star,Type * type)5360 CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type)
5361 {
5362 MOZ_ASSERT(star->isKind(PNK_STAR));
5363 ParseNode* lhs = MultiplyLeft(star);
5364 ParseNode* rhs = MultiplyRight(star);
5365
5366 size_t opcodeAt = f.tempOp();
5367
5368 Type lhsType;
5369 if (!CheckExpr(f, lhs, &lhsType))
5370 return false;
5371
5372 Type rhsType;
5373 if (!CheckExpr(f, rhs, &rhsType))
5374 return false;
5375
5376 if (lhsType.isInt() && rhsType.isInt()) {
5377 if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
5378 return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
5379 f.patchOp(opcodeAt, I32::Mul);
5380 *type = Type::Intish;
5381 return true;
5382 }
5383
5384 if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
5385 f.patchOp(opcodeAt, F64::Mul);
5386 *type = Type::Double;
5387 return true;
5388 }
5389
5390 if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
5391 f.patchOp(opcodeAt, F32::Mul);
5392 *type = Type::Floatish;
5393 return true;
5394 }
5395
5396 return f.fail(star, "multiply operands must be both int, both double? or both float?");
5397 }
5398
5399 static bool
CheckAddOrSub(FunctionValidator & f,ParseNode * expr,Type * type,unsigned * numAddOrSubOut=nullptr)5400 CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAddOrSubOut = nullptr)
5401 {
5402 JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
5403
5404 MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
5405 ParseNode* lhs = AddSubLeft(expr);
5406 ParseNode* rhs = AddSubRight(expr);
5407
5408 Type lhsType, rhsType;
5409 unsigned lhsNumAddOrSub, rhsNumAddOrSub;
5410
5411 size_t opcodeAt = f.tempOp();
5412
5413 if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
5414 if (!CheckAddOrSub(f, lhs, &lhsType, &lhsNumAddOrSub))
5415 return false;
5416 if (lhsType == Type::Intish)
5417 lhsType = Type::Int;
5418 } else {
5419 if (!CheckExpr(f, lhs, &lhsType))
5420 return false;
5421 lhsNumAddOrSub = 0;
5422 }
5423
5424 if (rhs->isKind(PNK_ADD) || rhs->isKind(PNK_SUB)) {
5425 if (!CheckAddOrSub(f, rhs, &rhsType, &rhsNumAddOrSub))
5426 return false;
5427 if (rhsType == Type::Intish)
5428 rhsType = Type::Int;
5429 } else {
5430 if (!CheckExpr(f, rhs, &rhsType))
5431 return false;
5432 rhsNumAddOrSub = 0;
5433 }
5434
5435 unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
5436 if (numAddOrSub > (1<<20))
5437 return f.fail(expr, "too many + or - without intervening coercion");
5438
5439 if (lhsType.isInt() && rhsType.isInt()) {
5440 f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? I32::Add : I32::Sub);
5441 *type = Type::Intish;
5442 } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
5443 f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? F64::Add : F64::Sub);
5444 *type = Type::Double;
5445 } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
5446 f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? F32::Add : F32::Sub);
5447 *type = Type::Floatish;
5448 } else {
5449 return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
5450 lhsType.toChars(), rhsType.toChars());
5451 }
5452
5453 if (numAddOrSubOut)
5454 *numAddOrSubOut = numAddOrSub;
5455 return true;
5456 }
5457
5458 static bool
CheckDivOrMod(FunctionValidator & f,ParseNode * expr,Type * type)5459 CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type)
5460 {
5461 MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
5462
5463 size_t opcodeAt = f.tempOp();
5464
5465 ParseNode* lhs = DivOrModLeft(expr);
5466 ParseNode* rhs = DivOrModRight(expr);
5467
5468 Type lhsType, rhsType;
5469 if (!CheckExpr(f, lhs, &lhsType))
5470 return false;
5471 if (!CheckExpr(f, rhs, &rhsType))
5472 return false;
5473
5474 if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
5475 f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? F64::Div : F64::Mod);
5476 *type = Type::Double;
5477 return true;
5478 }
5479
5480 if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
5481 if (expr->isKind(PNK_DIV))
5482 f.patchOp(opcodeAt, F32::Div);
5483 else
5484 return f.fail(expr, "modulo cannot receive float arguments");
5485 *type = Type::Floatish;
5486 return true;
5487 }
5488
5489 if (lhsType.isSigned() && rhsType.isSigned()) {
5490 f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? I32::SDiv : I32::SMod);
5491 *type = Type::Intish;
5492 return true;
5493 }
5494
5495 if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
5496 f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? I32::UDiv : I32::UMod);
5497 *type = Type::Intish;
5498 return true;
5499 }
5500
5501 return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
5502 "%s and %s are given", lhsType.toChars(), rhsType.toChars());
5503 }
5504
5505 static bool
CheckComparison(FunctionValidator & f,ParseNode * comp,Type * type)5506 CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type)
5507 {
5508 MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
5509 comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
5510
5511 size_t opcodeAt = f.tempOp();
5512
5513 ParseNode* lhs = ComparisonLeft(comp);
5514 ParseNode* rhs = ComparisonRight(comp);
5515
5516 Type lhsType, rhsType;
5517 if (!CheckExpr(f, lhs, &lhsType))
5518 return false;
5519 if (!CheckExpr(f, rhs, &rhsType))
5520 return false;
5521
5522 if (!(lhsType.isSigned() && rhsType.isSigned()) &&
5523 !(lhsType.isUnsigned() && rhsType.isUnsigned()) &&
5524 !(lhsType.isDouble() && rhsType.isDouble()) &&
5525 !(lhsType.isFloat() && rhsType.isFloat()))
5526 {
5527 return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
5528 "%s and %s are given", lhsType.toChars(), rhsType.toChars());
5529 }
5530
5531 I32 stmt;
5532 if (lhsType.isSigned() && rhsType.isSigned()) {
5533 switch (comp->getOp()) {
5534 case JSOP_EQ: stmt = I32::EqI32; break;
5535 case JSOP_NE: stmt = I32::NeI32; break;
5536 case JSOP_LT: stmt = I32::SLtI32; break;
5537 case JSOP_LE: stmt = I32::SLeI32; break;
5538 case JSOP_GT: stmt = I32::SGtI32; break;
5539 case JSOP_GE: stmt = I32::SGeI32; break;
5540 default: MOZ_CRASH("unexpected comparison op");
5541 }
5542 } else if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
5543 switch (comp->getOp()) {
5544 case JSOP_EQ: stmt = I32::EqI32; break;
5545 case JSOP_NE: stmt = I32::NeI32; break;
5546 case JSOP_LT: stmt = I32::ULtI32; break;
5547 case JSOP_LE: stmt = I32::ULeI32; break;
5548 case JSOP_GT: stmt = I32::UGtI32; break;
5549 case JSOP_GE: stmt = I32::UGeI32; break;
5550 default: MOZ_CRASH("unexpected comparison op");
5551 }
5552 } else if (lhsType.isDouble()) {
5553 switch (comp->getOp()) {
5554 case JSOP_EQ: stmt = I32::EqF64; break;
5555 case JSOP_NE: stmt = I32::NeF64; break;
5556 case JSOP_LT: stmt = I32::LtF64; break;
5557 case JSOP_LE: stmt = I32::LeF64; break;
5558 case JSOP_GT: stmt = I32::GtF64; break;
5559 case JSOP_GE: stmt = I32::GeF64; break;
5560 default: MOZ_CRASH("unexpected comparison op");
5561 }
5562 } else if (lhsType.isFloat()) {
5563 switch (comp->getOp()) {
5564 case JSOP_EQ: stmt = I32::EqF32; break;
5565 case JSOP_NE: stmt = I32::NeF32; break;
5566 case JSOP_LT: stmt = I32::LtF32; break;
5567 case JSOP_LE: stmt = I32::LeF32; break;
5568 case JSOP_GT: stmt = I32::GtF32; break;
5569 case JSOP_GE: stmt = I32::GeF32; break;
5570 default: MOZ_CRASH("unexpected comparison op");
5571 }
5572 } else {
5573 MOZ_CRASH("unexpected type");
5574 }
5575
5576 f.patchOp(opcodeAt, stmt);
5577 *type = Type::Int;
5578 return true;
5579 }
5580
5581 static bool
CheckBitwise(FunctionValidator & f,ParseNode * bitwise,Type * type)5582 CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type)
5583 {
5584 ParseNode* lhs = BitwiseLeft(bitwise);
5585 ParseNode* rhs = BitwiseRight(bitwise);
5586
5587 int32_t identityElement;
5588 bool onlyOnRight;
5589 switch (bitwise->getKind()) {
5590 case PNK_BITOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break;
5591 case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break;
5592 case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break;
5593 case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
5594 case PNK_RSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
5595 case PNK_URSH: identityElement = 0; onlyOnRight = true; *type = Type::Unsigned; break;
5596 default: MOZ_CRASH("not a bitwise op");
5597 }
5598
5599 uint32_t i;
5600 if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) && i == uint32_t(identityElement)) {
5601 Type rhsType;
5602 if (!CheckExpr(f, rhs, &rhsType))
5603 return false;
5604 if (!rhsType.isIntish())
5605 return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars());
5606 return true;
5607 }
5608
5609 if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) {
5610 if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL))
5611 return CheckCoercedCall(f, lhs, ExprType::I32, type);
5612
5613 Type lhsType;
5614 if (!CheckExpr(f, lhs, &lhsType))
5615 return false;
5616 if (!lhsType.isIntish())
5617 return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars());
5618 return true;
5619 }
5620
5621 switch (bitwise->getKind()) {
5622 case PNK_BITOR: f.writeOp(I32::BitOr); break;
5623 case PNK_BITAND: f.writeOp(I32::BitAnd); break;
5624 case PNK_BITXOR: f.writeOp(I32::BitXor); break;
5625 case PNK_LSH: f.writeOp(I32::Lsh); break;
5626 case PNK_RSH: f.writeOp(I32::ArithRsh); break;
5627 case PNK_URSH: f.writeOp(I32::LogicRsh); break;
5628 default: MOZ_CRASH("not a bitwise op");
5629 }
5630
5631 Type lhsType;
5632 if (!CheckExpr(f, lhs, &lhsType))
5633 return false;
5634
5635 Type rhsType;
5636 if (!CheckExpr(f, rhs, &rhsType))
5637 return false;
5638
5639 if (!lhsType.isIntish())
5640 return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
5641 if (!rhsType.isIntish())
5642 return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
5643
5644 return true;
5645 }
5646
5647 static bool
CheckExpr(FunctionValidator & f,ParseNode * expr,Type * type)5648 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
5649 {
5650 JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
5651
5652 if (IsNumericLiteral(f.m(), expr))
5653 return CheckNumericLiteral(f, expr, type);
5654
5655 switch (expr->getKind()) {
5656 case PNK_NAME: return CheckVarRef(f, expr, type);
5657 case PNK_ELEM: return CheckLoadArray(f, expr, type);
5658 case PNK_DOT: return CheckDotAccess(f, expr, type);
5659 case PNK_ASSIGN: return CheckAssign(f, expr, type);
5660 case PNK_POS: return CheckPos(f, expr, type);
5661 case PNK_NOT: return CheckNot(f, expr, type);
5662 case PNK_NEG: return CheckNeg(f, expr, type);
5663 case PNK_BITNOT: return CheckBitNot(f, expr, type);
5664 case PNK_COMMA: return CheckComma(f, expr, type);
5665 case PNK_CONDITIONAL: return CheckConditional(f, expr, type);
5666 case PNK_STAR: return CheckMultiply(f, expr, type);
5667 case PNK_CALL: return CheckUncoercedCall(f, expr, type);
5668
5669 case PNK_ADD:
5670 case PNK_SUB: return CheckAddOrSub(f, expr, type);
5671
5672 case PNK_DIV:
5673 case PNK_MOD: return CheckDivOrMod(f, expr, type);
5674
5675 case PNK_LT:
5676 case PNK_LE:
5677 case PNK_GT:
5678 case PNK_GE:
5679 case PNK_EQ:
5680 case PNK_NE: return CheckComparison(f, expr, type);
5681
5682 case PNK_BITOR:
5683 case PNK_BITAND:
5684 case PNK_BITXOR:
5685 case PNK_LSH:
5686 case PNK_RSH:
5687 case PNK_URSH: return CheckBitwise(f, expr, type);
5688
5689 default:;
5690 }
5691
5692 return f.fail(expr, "unsupported expression");
5693 }
5694
5695 static bool
5696 CheckStatement(FunctionValidator& f, ParseNode* stmt);
5697
5698 static bool
CheckAsExprStatement(FunctionValidator & f,ParseNode * expr)5699 CheckAsExprStatement(FunctionValidator& f, ParseNode* expr)
5700 {
5701 if (expr->isKind(PNK_CALL)) {
5702 Type _;
5703 return CheckCoercedCall(f, expr, ExprType::Void, &_);
5704 }
5705
5706 size_t opcodeAt = f.tempOp();
5707
5708 Type type;
5709 if (!CheckExpr(f, expr, &type))
5710 return false;
5711
5712 if (type.isIntish())
5713 f.patchOp(opcodeAt, Stmt::I32Expr);
5714 else if (type.isFloatish())
5715 f.patchOp(opcodeAt, Stmt::F32Expr);
5716 else if (type.isMaybeDouble())
5717 f.patchOp(opcodeAt, Stmt::F64Expr);
5718 else if (type.isInt32x4())
5719 f.patchOp(opcodeAt, Stmt::I32X4Expr);
5720 else if (type.isFloat32x4())
5721 f.patchOp(opcodeAt, Stmt::F32X4Expr);
5722 else
5723 MOZ_CRASH("unexpected or unimplemented expression statement");
5724
5725 return true;
5726 }
5727
5728 static bool
CheckExprStatement(FunctionValidator & f,ParseNode * exprStmt)5729 CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt)
5730 {
5731 MOZ_ASSERT(exprStmt->isKind(PNK_SEMI));
5732 ParseNode* expr = UnaryKid(exprStmt);
5733
5734 if (!expr) {
5735 f.writeOp(Stmt::Noop);
5736 return true;
5737 }
5738
5739 return CheckAsExprStatement(f, expr);
5740 }
5741
5742 enum class InterruptCheckPosition {
5743 Head,
5744 Loop
5745 };
5746
5747 static void
MaybeAddInterruptCheck(FunctionValidator & f,InterruptCheckPosition pos,ParseNode * pn)5748 MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn)
5749 {
5750 if (f.m().module().usesSignalHandlersForInterrupt())
5751 return;
5752
5753 switch (pos) {
5754 case InterruptCheckPosition::Head: f.writeOp(Stmt::InterruptCheckHead); break;
5755 case InterruptCheckPosition::Loop: f.writeOp(Stmt::InterruptCheckLoop); break;
5756 }
5757
5758 unsigned lineno = 0, column = 0;
5759 f.m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &lineno, &column);
5760 f.writeU32(lineno);
5761 f.writeU32(column);
5762 }
5763
5764 static bool
CheckWhile(FunctionValidator & f,ParseNode * whileStmt)5765 CheckWhile(FunctionValidator& f, ParseNode* whileStmt)
5766 {
5767 MOZ_ASSERT(whileStmt->isKind(PNK_WHILE));
5768 ParseNode* cond = BinaryLeft(whileStmt);
5769 ParseNode* body = BinaryRight(whileStmt);
5770
5771 f.writeOp(Stmt::While);
5772
5773 Type condType;
5774 if (!CheckExpr(f, cond, &condType))
5775 return false;
5776 if (!condType.isInt())
5777 return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5778
5779 MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, whileStmt);
5780
5781 return CheckStatement(f, body);
5782 }
5783
5784 static bool
CheckFor(FunctionValidator & f,ParseNode * forStmt)5785 CheckFor(FunctionValidator& f, ParseNode* forStmt)
5786 {
5787 MOZ_ASSERT(forStmt->isKind(PNK_FOR));
5788 ParseNode* forHead = BinaryLeft(forStmt);
5789 ParseNode* body = BinaryRight(forStmt);
5790
5791 if (!forHead->isKind(PNK_FORHEAD))
5792 return f.fail(forHead, "unsupported for-loop statement");
5793
5794 ParseNode* maybeInit = TernaryKid1(forHead);
5795 ParseNode* maybeCond = TernaryKid2(forHead);
5796 ParseNode* maybeInc = TernaryKid3(forHead);
5797
5798 f.writeOp(maybeInit ? (maybeInc ? Stmt::ForInitInc : Stmt::ForInitNoInc)
5799 : (maybeInc ? Stmt::ForNoInitInc : Stmt::ForNoInitNoInc));
5800
5801 if (maybeInit && !CheckAsExprStatement(f, maybeInit))
5802 return false;
5803
5804 if (maybeCond) {
5805 Type condType;
5806 if (!CheckExpr(f, maybeCond, &condType))
5807 return false;
5808 if (!condType.isInt())
5809 return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars());
5810 } else {
5811 f.writeInt32Lit(1);
5812 }
5813
5814 MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, forStmt);
5815
5816 if (!CheckStatement(f, body))
5817 return false;
5818
5819 if (maybeInc && !CheckAsExprStatement(f, maybeInc))
5820 return false;
5821
5822 f.writeDebugCheckPoint();
5823 return true;
5824 }
5825
5826 static bool
CheckDoWhile(FunctionValidator & f,ParseNode * whileStmt)5827 CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt)
5828 {
5829 MOZ_ASSERT(whileStmt->isKind(PNK_DOWHILE));
5830 ParseNode* body = BinaryLeft(whileStmt);
5831 ParseNode* cond = BinaryRight(whileStmt);
5832
5833 f.writeOp(Stmt::DoWhile);
5834
5835 MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, cond);
5836
5837 if (!CheckStatement(f, body))
5838 return false;
5839
5840 Type condType;
5841 if (!CheckExpr(f, cond, &condType))
5842 return false;
5843 if (!condType.isInt())
5844 return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5845
5846 return true;
5847 }
5848
5849 static bool
CheckLabel(FunctionValidator & f,ParseNode * labeledStmt)5850 CheckLabel(FunctionValidator& f, ParseNode* labeledStmt)
5851 {
5852 MOZ_ASSERT(labeledStmt->isKind(PNK_LABEL));
5853 PropertyName* label = LabeledStatementLabel(labeledStmt);
5854 ParseNode* stmt = LabeledStatementStatement(labeledStmt);
5855
5856 f.writeOp(Stmt::Label);
5857
5858 uint32_t labelId;
5859 if (!f.addLabel(label, &labelId))
5860 return false;
5861
5862 f.writeU32(labelId);
5863
5864 if (!CheckStatement(f, stmt))
5865 return false;
5866
5867 f.removeLabel(label);
5868 return true;
5869 }
5870
5871 static bool
CheckIf(FunctionValidator & f,ParseNode * ifStmt)5872 CheckIf(FunctionValidator& f, ParseNode* ifStmt)
5873 {
5874 recurse:
5875 size_t opcodeAt = f.tempOp();
5876
5877 MOZ_ASSERT(ifStmt->isKind(PNK_IF));
5878 ParseNode* cond = TernaryKid1(ifStmt);
5879 ParseNode* thenStmt = TernaryKid2(ifStmt);
5880 ParseNode* elseStmt = TernaryKid3(ifStmt);
5881
5882 Type condType;
5883 if (!CheckExpr(f, cond, &condType))
5884 return false;
5885 if (!condType.isInt())
5886 return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5887
5888 if (!CheckStatement(f, thenStmt))
5889 return false;
5890
5891 if (!elseStmt) {
5892 f.patchOp(opcodeAt, Stmt::IfThen);
5893 } else {
5894 f.patchOp(opcodeAt, Stmt::IfElse);
5895
5896 if (elseStmt->isKind(PNK_IF)) {
5897 ifStmt = elseStmt;
5898 goto recurse;
5899 }
5900
5901 if (!CheckStatement(f, elseStmt))
5902 return false;
5903 }
5904
5905 return true;
5906 }
5907
5908 static bool
CheckCaseExpr(FunctionValidator & f,ParseNode * caseExpr,int32_t * value)5909 CheckCaseExpr(FunctionValidator& f, ParseNode* caseExpr, int32_t* value)
5910 {
5911 if (!IsNumericLiteral(f.m(), caseExpr))
5912 return f.fail(caseExpr, "switch case expression must be an integer literal");
5913
5914 NumLit lit = ExtractNumericLiteral(f.m(), caseExpr);
5915 switch (lit.which()) {
5916 case NumLit::Fixnum:
5917 case NumLit::NegativeInt:
5918 *value = lit.toInt32();
5919 break;
5920 case NumLit::OutOfRangeInt:
5921 case NumLit::BigUnsigned:
5922 return f.fail(caseExpr, "switch case expression out of integer range");
5923 case NumLit::Double:
5924 case NumLit::Float:
5925 case NumLit::Int32x4:
5926 case NumLit::Float32x4:
5927 return f.fail(caseExpr, "switch case expression must be an integer literal");
5928 }
5929
5930 return true;
5931 }
5932
5933 static bool
CheckDefaultAtEnd(FunctionValidator & f,ParseNode * stmt)5934 CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt)
5935 {
5936 for (; stmt; stmt = NextNode(stmt)) {
5937 if (IsDefaultCase(stmt) && NextNode(stmt) != nullptr)
5938 return f.fail(stmt, "default label must be at the end");
5939 }
5940
5941 return true;
5942 }
5943
5944 static bool
CheckSwitchRange(FunctionValidator & f,ParseNode * stmt,int32_t * low,int32_t * high,int32_t * tableLength)5945 CheckSwitchRange(FunctionValidator& f, ParseNode* stmt, int32_t* low, int32_t* high,
5946 int32_t* tableLength)
5947 {
5948 if (IsDefaultCase(stmt)) {
5949 *low = 0;
5950 *high = -1;
5951 *tableLength = 0;
5952 return true;
5953 }
5954
5955 int32_t i = 0;
5956 if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
5957 return false;
5958
5959 *low = *high = i;
5960
5961 ParseNode* initialStmt = stmt;
5962 for (stmt = NextNode(stmt); stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
5963 int32_t i = 0;
5964 if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
5965 return false;
5966
5967 *low = Min(*low, i);
5968 *high = Max(*high, i);
5969 }
5970
5971 int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
5972 if (i64 > 4*1024*1024)
5973 return f.fail(initialStmt, "all switch statements generate tables; this table would be too big");
5974
5975 *tableLength = int32_t(i64);
5976 return true;
5977 }
5978
5979 void
PatchSwitch(FunctionValidator & f,size_t hasDefaultAt,bool hasDefault,size_t lowAt,int32_t low,size_t highAt,int32_t high,size_t numCasesAt,uint32_t numCases)5980 PatchSwitch(FunctionValidator& f,
5981 size_t hasDefaultAt, bool hasDefault,
5982 size_t lowAt, int32_t low,
5983 size_t highAt, int32_t high,
5984 size_t numCasesAt, uint32_t numCases)
5985 {
5986 f.patchU8(hasDefaultAt, uint8_t(hasDefault));
5987 f.patch32(lowAt, low);
5988 f.patch32(highAt, high);
5989 f.patch32(numCasesAt, numCases);
5990 }
5991
5992 static bool
CheckSwitch(FunctionValidator & f,ParseNode * switchStmt)5993 CheckSwitch(FunctionValidator& f, ParseNode* switchStmt)
5994 {
5995 MOZ_ASSERT(switchStmt->isKind(PNK_SWITCH));
5996
5997 f.writeOp(Stmt::Switch);
5998 // Has default
5999 size_t hasDefaultAt = f.tempU8();
6000 // Low / High / Num cases
6001 size_t lowAt = f.temp32();
6002 size_t highAt = f.temp32();
6003 size_t numCasesAt = f.temp32();
6004
6005 ParseNode* switchExpr = BinaryLeft(switchStmt);
6006 ParseNode* switchBody = BinaryRight(switchStmt);
6007
6008 if (!switchBody->isKind(PNK_STATEMENTLIST))
6009 return f.fail(switchBody, "switch body may not contain 'let' declarations");
6010
6011 Type exprType;
6012 if (!CheckExpr(f, switchExpr, &exprType))
6013 return false;
6014
6015 if (!exprType.isSigned())
6016 return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
6017
6018 ParseNode* stmt = ListHead(switchBody);
6019
6020 if (!CheckDefaultAtEnd(f, stmt))
6021 return false;
6022
6023 if (!stmt) {
6024 PatchSwitch(f, hasDefaultAt, false, lowAt, 0, highAt, 0, numCasesAt, 0);
6025 return true;
6026 }
6027
6028 int32_t low = 0, high = 0, tableLength = 0;
6029 if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
6030 return false;
6031
6032 Vector<bool, 8> cases(f.cx());
6033 if (!cases.resize(tableLength))
6034 return false;
6035
6036 uint32_t numCases = 0;
6037 for (; stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
6038 int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32();
6039 unsigned caseIndex = caseValue - low;
6040
6041 if (cases[caseIndex])
6042 return f.fail(stmt, "no duplicate case labels");
6043
6044 cases[caseIndex] = true;
6045 numCases += 1;
6046 f.writeI32(caseValue);
6047
6048 if (!CheckStatement(f, CaseBody(stmt)))
6049 return false;
6050
6051 }
6052
6053 bool hasDefault = false;
6054 if (stmt && IsDefaultCase(stmt)) {
6055 hasDefault = true;
6056 if (!CheckStatement(f, CaseBody(stmt)))
6057 return false;
6058 }
6059
6060 PatchSwitch(f, hasDefaultAt, hasDefault, lowAt, low, highAt, high, numCasesAt, numCases);
6061 return true;
6062 }
6063
6064 static bool
CheckReturnType(FunctionValidator & f,ParseNode * usepn,ExprType ret)6065 CheckReturnType(FunctionValidator& f, ParseNode* usepn, ExprType ret)
6066 {
6067 if (!f.hasAlreadyReturned()) {
6068 f.setReturnedType(ret);
6069 return true;
6070 }
6071
6072 if (f.returnedType() != ret) {
6073 return f.failf(usepn, "%s incompatible with previous return of type %s",
6074 Type::ret(ret).toChars(), Type::ret(f.returnedType()).toChars());
6075 }
6076
6077 return true;
6078 }
6079
6080 static bool
CheckReturn(FunctionValidator & f,ParseNode * returnStmt)6081 CheckReturn(FunctionValidator& f, ParseNode* returnStmt)
6082 {
6083 ParseNode* expr = ReturnExpr(returnStmt);
6084
6085 f.writeOp(Stmt::Ret);
6086
6087 if (!expr)
6088 return CheckReturnType(f, returnStmt, ExprType::Void);
6089
6090 Type type;
6091 if (!CheckExpr(f, expr, &type))
6092 return false;
6093
6094 ExprType ret;
6095 if (type.isSigned())
6096 ret = ExprType::I32;
6097 else if (type.isFloat())
6098 ret = ExprType::F32;
6099 else if (type.isDouble())
6100 ret = ExprType::F64;
6101 else if (type.isInt32x4())
6102 ret = ExprType::I32x4;
6103 else if (type.isFloat32x4())
6104 ret = ExprType::F32x4;
6105 else if (type.isVoid())
6106 ret = ExprType::Void;
6107 else
6108 return f.failf(expr, "%s is not a valid return type", type.toChars());
6109
6110 return CheckReturnType(f, expr, ret);
6111 }
6112
6113 static bool
CheckStatementList(FunctionValidator & f,ParseNode * stmtList)6114 CheckStatementList(FunctionValidator& f, ParseNode* stmtList)
6115 {
6116 MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
6117
6118 f.writeOp(Stmt::Block);
6119 f.writeU32(ListLength(stmtList));
6120
6121 for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
6122 if (!CheckStatement(f, stmt))
6123 return false;
6124 }
6125
6126 f.writeDebugCheckPoint();
6127 return true;
6128 }
6129
6130 static bool
CheckBreakOrContinue(FunctionValidator & f,PropertyName * maybeLabel,Stmt withoutLabel,Stmt withLabel)6131 CheckBreakOrContinue(FunctionValidator& f, PropertyName* maybeLabel,
6132 Stmt withoutLabel, Stmt withLabel)
6133 {
6134 if (!maybeLabel) {
6135 f.writeOp(withoutLabel);
6136 return true;
6137 }
6138
6139 f.writeOp(withLabel);
6140
6141 uint32_t labelId = f.lookupLabel(maybeLabel);
6142 MOZ_ASSERT(labelId != uint32_t(-1));
6143
6144 f.writeU32(labelId);
6145 return true;
6146 }
6147
6148 static bool
CheckStatement(FunctionValidator & f,ParseNode * stmt)6149 CheckStatement(FunctionValidator& f, ParseNode* stmt)
6150 {
6151 JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
6152
6153 switch (stmt->getKind()) {
6154 case PNK_SEMI: return CheckExprStatement(f, stmt);
6155 case PNK_WHILE: return CheckWhile(f, stmt);
6156 case PNK_FOR: return CheckFor(f, stmt);
6157 case PNK_DOWHILE: return CheckDoWhile(f, stmt);
6158 case PNK_LABEL: return CheckLabel(f, stmt);
6159 case PNK_IF: return CheckIf(f, stmt);
6160 case PNK_SWITCH: return CheckSwitch(f, stmt);
6161 case PNK_RETURN: return CheckReturn(f, stmt);
6162 case PNK_STATEMENTLIST: return CheckStatementList(f, stmt);
6163 case PNK_BREAK: return CheckBreakOrContinue(f, LoopControlMaybeLabel(stmt),
6164 Stmt::Break, Stmt::BreakLabel);
6165 case PNK_CONTINUE: return CheckBreakOrContinue(f, LoopControlMaybeLabel(stmt),
6166 Stmt::Continue, Stmt::ContinueLabel);
6167 default:;
6168 }
6169
6170 return f.fail(stmt, "unexpected statement kind");
6171 }
6172
6173 static bool
CheckByteLengthCall(ModuleValidator & m,ParseNode * pn,PropertyName * newBufferName)6174 CheckByteLengthCall(ModuleValidator& m, ParseNode* pn, PropertyName* newBufferName)
6175 {
6176 if (!pn->isKind(PNK_CALL) || !CallCallee(pn)->isKind(PNK_NAME))
6177 return m.fail(pn, "expecting call to imported byteLength");
6178
6179 const ModuleValidator::Global* global = m.lookupGlobal(CallCallee(pn)->name());
6180 if (!global || global->which() != ModuleValidator::Global::ByteLength)
6181 return m.fail(pn, "expecting call to imported byteLength");
6182
6183 if (CallArgListLength(pn) != 1 || !IsUseOfName(CallArgList(pn), newBufferName))
6184 return m.failName(pn, "expecting %s as argument to byteLength call", newBufferName);
6185
6186 return true;
6187 }
6188
6189 static bool
CheckHeapLengthCondition(ModuleValidator & m,ParseNode * cond,PropertyName * newBufferName,uint32_t * mask,uint32_t * minLength,uint32_t * maxLength)6190 CheckHeapLengthCondition(ModuleValidator& m, ParseNode* cond, PropertyName* newBufferName,
6191 uint32_t* mask, uint32_t* minLength, uint32_t* maxLength)
6192 {
6193 if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
6194 return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
6195
6196 ParseNode* cond1 = AndOrLeft(AndOrLeft(cond));
6197 ParseNode* cond2 = AndOrRight(AndOrLeft(cond));
6198 ParseNode* cond3 = AndOrRight(cond);
6199
6200 if (!cond1->isKind(PNK_BITAND))
6201 return m.fail(cond1, "expecting byteLength & K");
6202
6203 if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
6204 return false;
6205
6206 ParseNode* maskNode = BitwiseRight(cond1);
6207 if (!IsLiteralInt(m, maskNode, mask))
6208 return m.fail(maskNode, "expecting integer literal mask");
6209 if (*mask == UINT32_MAX)
6210 return m.fail(maskNode, "invalid mask value");
6211 if ((*mask & 0xffffff) != 0xffffff)
6212 return m.fail(maskNode, "mask value must have the bits 0xffffff set");
6213
6214 if (!cond2->isKind(PNK_LE))
6215 return m.fail(cond2, "expecting byteLength <= L");
6216
6217 if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
6218 return false;
6219
6220 ParseNode* minLengthNode = RelationalRight(cond2);
6221 uint32_t minLengthExclusive;
6222 if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
6223 return m.fail(minLengthNode, "expecting integer literal");
6224 if (minLengthExclusive < 0xffffff || minLengthExclusive == UINT32_MAX)
6225 return m.fail(minLengthNode, "literal must be >= 0xffffff and < 0xffffffff");
6226
6227 // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
6228 *minLength = minLengthExclusive + 1;
6229
6230 if (!cond3->isKind(PNK_GT))
6231 return m.fail(cond3, "expecting byteLength > M");
6232
6233 if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
6234 return false;
6235
6236 ParseNode* maxLengthNode = RelationalRight(cond3);
6237 if (!IsLiteralInt(m, maxLengthNode, maxLength))
6238 return m.fail(maxLengthNode, "expecting integer literal");
6239 if (*maxLength > 0x80000000)
6240 return m.fail(maxLengthNode, "literal must be <= 0x80000000");
6241
6242 if (*maxLength < *minLength)
6243 return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
6244
6245 return true;
6246 }
6247
6248 static bool
CheckReturnBoolLiteral(ModuleValidator & m,ParseNode * stmt,bool retval)6249 CheckReturnBoolLiteral(ModuleValidator& m, ParseNode* stmt, bool retval)
6250 {
6251 if (stmt->isKind(PNK_STATEMENTLIST)) {
6252 ParseNode* next = SkipEmptyStatements(ListHead(stmt));
6253 if (!next)
6254 return m.fail(stmt, "expected return statement");
6255 stmt = next;
6256 if (NextNonEmptyStatement(stmt))
6257 return m.fail(stmt, "expected single return statement");
6258 }
6259
6260 if (!stmt->isKind(PNK_RETURN))
6261 return m.fail(stmt, "expected return statement");
6262
6263 ParseNode* returnExpr = ReturnExpr(stmt);
6264 if (!returnExpr || !returnExpr->isKind(retval ? PNK_TRUE : PNK_FALSE))
6265 return m.failf(stmt, "expected 'return %s;'", retval ? "true" : "false");
6266
6267 return true;
6268 }
6269
6270 static bool
CheckReassignmentTo(ModuleValidator & m,ParseNode * stmt,PropertyName * lhsName,ParseNode ** rhs)6271 CheckReassignmentTo(ModuleValidator& m, ParseNode* stmt, PropertyName* lhsName, ParseNode** rhs)
6272 {
6273 if (!stmt->isKind(PNK_SEMI))
6274 return m.fail(stmt, "missing reassignment");
6275
6276 ParseNode* assign = UnaryKid(stmt);
6277 if (!assign || !assign->isKind(PNK_ASSIGN))
6278 return m.fail(stmt, "missing reassignment");
6279
6280 ParseNode* lhs = BinaryLeft(assign);
6281 if (!IsUseOfName(lhs, lhsName))
6282 return m.failName(lhs, "expecting reassignment of %s", lhsName);
6283
6284 *rhs = BinaryRight(assign);
6285 return true;
6286 }
6287
6288 static bool
CheckChangeHeap(ModuleValidator & m,ParseNode * fn,bool * validated)6289 CheckChangeHeap(ModuleValidator& m, ParseNode* fn, bool* validated)
6290 {
6291 MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
6292
6293 // We don't yet know whether this is a change-heap function.
6294 // The point at which we know we have a change-heap function is once we see
6295 // whether the argument is coerced according to the normal asm.js rules. If
6296 // it is coerced, it's not change-heap and must validate according to normal
6297 // rules; otherwise it must validate as a change-heap function.
6298 *validated = false;
6299
6300 PropertyName* changeHeapName = FunctionName(fn);
6301 if (!CheckModuleLevelName(m, fn, changeHeapName))
6302 return false;
6303
6304 unsigned numFormals;
6305 ParseNode* arg = FunctionArgsList(fn, &numFormals);
6306 if (numFormals != 1)
6307 return true;
6308
6309 PropertyName* newBufferName;
6310 if (!CheckArgument(m, arg, &newBufferName))
6311 return false;
6312
6313 ParseNode* stmtIter = SkipEmptyStatements(ListHead(FunctionStatementList(fn)));
6314 if (!stmtIter || !stmtIter->isKind(PNK_IF))
6315 return true;
6316
6317 // We can now issue validation failures if we see something that isn't a
6318 // valid change-heap function.
6319 *validated = true;
6320
6321 PropertyName* bufferName = m.module().bufferArgumentName();
6322 if (!bufferName)
6323 return m.fail(fn, "to change heaps, the module must have a buffer argument");
6324
6325 ParseNode* cond = TernaryKid1(stmtIter);
6326 ParseNode* thenStmt = TernaryKid2(stmtIter);
6327 if (ParseNode* elseStmt = TernaryKid3(stmtIter))
6328 return m.fail(elseStmt, "unexpected else statement");
6329
6330 uint32_t mask, min = 0, max; // initialize min to silence GCC warning
6331 if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
6332 return false;
6333
6334 if (!CheckReturnBoolLiteral(m, thenStmt, false))
6335 return false;
6336
6337 ParseNode* next = NextNonEmptyStatement(stmtIter);
6338
6339 for (unsigned i = 0; i < m.numArrayViews(); i++, next = NextNonEmptyStatement(stmtIter)) {
6340 if (!next)
6341 return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
6342 stmtIter = next;
6343
6344 const ModuleValidator::ArrayView& view = m.arrayView(i);
6345
6346 ParseNode* rhs;
6347 if (!CheckReassignmentTo(m, stmtIter, view.name, &rhs))
6348 return false;
6349
6350 if (!rhs->isKind(PNK_NEW))
6351 return m.failName(rhs, "expecting assignment of new array view to %s", view.name);
6352
6353 ParseNode* ctorExpr = ListHead(rhs);
6354 if (!ctorExpr->isKind(PNK_NAME))
6355 return m.fail(rhs, "expecting name of imported typed array constructor");
6356
6357 const ModuleValidator::Global* global = m.lookupGlobal(ctorExpr->name());
6358 if (!global || global->which() != ModuleValidator::Global::ArrayViewCtor)
6359 return m.fail(rhs, "expecting name of imported typed array constructor");
6360 if (global->viewType() != view.type)
6361 return m.fail(rhs, "can't change the type of a global view variable");
6362
6363 if (!CheckNewArrayViewArgs(m, ctorExpr, newBufferName))
6364 return false;
6365 }
6366
6367 if (!next)
6368 return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
6369 stmtIter = next;
6370
6371 ParseNode* rhs;
6372 if (!CheckReassignmentTo(m, stmtIter, bufferName, &rhs))
6373 return false;
6374 if (!IsUseOfName(rhs, newBufferName))
6375 return m.failName(stmtIter, "expecting assignment of new buffer to %s", bufferName);
6376
6377 next = NextNonEmptyStatement(stmtIter);
6378 if (!next)
6379 return m.failOffset(stmtIter->pn_pos.end, "expected return statement");
6380 stmtIter = next;
6381
6382 if (!CheckReturnBoolLiteral(m, stmtIter, true))
6383 return false;
6384
6385 stmtIter = NextNonEmptyStatement(stmtIter);
6386 if (stmtIter)
6387 return m.fail(stmtIter, "expecting end of function");
6388
6389 return m.addChangeHeap(changeHeapName, fn, mask, min, max);
6390 }
6391
6392 static bool
ParseFunction(ModuleValidator & m,ParseNode ** fnOut,unsigned * line,unsigned * column)6393 ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line, unsigned* column)
6394 {
6395 TokenStream& tokenStream = m.tokenStream();
6396
6397 tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
6398 tokenStream.srcCoords.lineNumAndColumnIndex(tokenStream.currentToken().pos.end, line, column);
6399
6400 RootedPropertyName name(m.cx());
6401
6402 TokenKind tk;
6403 if (!tokenStream.getToken(&tk, TokenStream::Operand))
6404 return false;
6405 if (tk == TOK_NAME) {
6406 name = tokenStream.currentName();
6407 } else if (tk == TOK_YIELD) {
6408 if (!m.parser().checkYieldNameValidity())
6409 return false;
6410 name = m.cx()->names().yield;
6411 } else {
6412 return false; // The regular parser will throw a SyntaxError, no need to m.fail.
6413 }
6414
6415 ParseNode* fn = m.parser().handler.newFunctionDefinition();
6416 if (!fn)
6417 return false;
6418
6419 // This flows into FunctionBox, so must be tenured.
6420 RootedFunction fun(m.cx(),
6421 NewScriptedFunction(m.cx(), 0, JSFunction::INTERPRETED,
6422 name, gc::AllocKind::FUNCTION,
6423 TenuredObject));
6424 if (!fun)
6425 return false;
6426
6427 AsmJSParseContext* outerpc = m.parser().pc;
6428
6429 Directives directives(outerpc);
6430 FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
6431 if (!funbox)
6432 return false;
6433
6434 Directives newDirectives = directives;
6435 AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives);
6436 if (!funpc.init(m.parser()))
6437 return false;
6438
6439 if (!m.parser().functionArgsAndBodyGeneric(InAllowed, YieldIsName, fn, fun, Statement)) {
6440 if (tokenStream.hadError() || directives == newDirectives)
6441 return false;
6442
6443 return m.fail(fn, "encountered new directive in function");
6444 }
6445
6446 MOZ_ASSERT(!tokenStream.hadError());
6447 MOZ_ASSERT(directives == newDirectives);
6448
6449 fn->pn_blockid = outerpc->blockid();
6450
6451 *fnOut = fn;
6452 return true;
6453 }
6454
6455 static bool
CheckFunction(ModuleValidator & m)6456 CheckFunction(ModuleValidator& m)
6457 {
6458 // asm.js modules can be quite large when represented as parse trees so pop
6459 // the backing LifoAlloc after parsing/compiling each function.
6460 AsmJSParser::Mark mark = m.parser().mark();
6461
6462 int64_t before = PRMJ_Now();
6463
6464 ParseNode* fn = nullptr;
6465 unsigned line = 0, column = 0;
6466 if (!ParseFunction(m, &fn, &line, &column))
6467 return false;
6468
6469 if (!CheckFunctionHead(m, fn))
6470 return false;
6471
6472 if (m.tryOnceToValidateChangeHeap()) {
6473 bool validated;
6474 if (!CheckChangeHeap(m, fn, &validated))
6475 return false;
6476 if (validated)
6477 return true;
6478 }
6479
6480 FunctionValidator f(m, fn);
6481 if (!f.init(FunctionName(fn), line, column))
6482 return m.fail(fn, "internal compiler failure (probably out of memory)");
6483
6484 ParseNode* stmtIter = ListHead(FunctionStatementList(fn));
6485
6486 if (!CheckProcessingDirectives(m, &stmtIter))
6487 return false;
6488
6489 MallocSig::ArgVector args;
6490 if (!CheckArguments(f, &stmtIter, &args))
6491 return false;
6492
6493 if (!CheckVariables(f, &stmtIter))
6494 return false;
6495
6496 MOZ_ASSERT(!f.startedPacking(), "No bytecode should be written at this point.");
6497 MaybeAddInterruptCheck(f, InterruptCheckPosition::Head, fn);
6498
6499 ParseNode* lastNonEmptyStmt = nullptr;
6500 for (; stmtIter; stmtIter = NextNode(stmtIter)) {
6501 if (!CheckStatement(f, stmtIter))
6502 return false;
6503 if (!IsEmptyStatement(stmtIter))
6504 lastNonEmptyStmt = stmtIter;
6505 }
6506
6507 if (!CheckFinalReturn(f, lastNonEmptyStmt))
6508 return false;
6509
6510 MallocSig sig(Move(args), f.returnedType());
6511
6512 ModuleValidator::Func* func = nullptr;
6513 if (!CheckFunctionSignature(m, fn, sig, FunctionName(fn), &func))
6514 return false;
6515
6516 if (func->defined())
6517 return m.failName(fn, "function '%s' already defined", FunctionName(fn));
6518
6519 func->define(fn);
6520
6521 if (!f.finish(func->index(), func->sig(), (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC))
6522 return m.fail(fn, "internal compiler failure (probably out of memory)");
6523
6524 // Release the parser's lifo memory only after the last use of a parse node.
6525 m.parser().release(mark);
6526 return true;
6527 }
6528
6529 static bool
CheckAllFunctionsDefined(ModuleValidator & m)6530 CheckAllFunctionsDefined(ModuleValidator& m)
6531 {
6532 for (unsigned i = 0; i < m.numFunctions(); i++) {
6533 ModuleValidator::Func& f = m.function(i);
6534 if (!f.defined())
6535 return m.failNameOffset(f.firstUse(), "missing definition of function %s", f.name());
6536 }
6537
6538 return true;
6539 }
6540
6541 static bool
CheckFunctions(ModuleValidator & m)6542 CheckFunctions(ModuleValidator& m)
6543 {
6544 while (true) {
6545 TokenKind tk;
6546 if (!PeekToken(m.parser(), &tk))
6547 return false;
6548
6549 if (tk != TOK_FUNCTION)
6550 break;
6551
6552 if (!CheckFunction(m))
6553 return false;
6554 }
6555
6556 return CheckAllFunctionsDefined(m);
6557 }
6558
6559 static bool
CheckFuncPtrTable(ModuleValidator & m,ParseNode * var)6560 CheckFuncPtrTable(ModuleValidator& m, ParseNode* var)
6561 {
6562 if (!IsDefinition(var))
6563 return m.fail(var, "function-pointer table name must be unique");
6564
6565 ParseNode* arrayLiteral = MaybeDefinitionInitializer(var);
6566 if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY))
6567 return m.fail(var, "function-pointer table's initializer must be an array literal");
6568
6569 unsigned length = ListLength(arrayLiteral);
6570
6571 if (!IsPowerOfTwo(length))
6572 return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
6573
6574 unsigned mask = length - 1;
6575
6576 ModuleGenerator::FuncIndexVector elems;
6577 const LifoSig* sig = nullptr;
6578 for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
6579 if (!elem->isKind(PNK_NAME))
6580 return m.fail(elem, "function-pointer table's elements must be names of functions");
6581
6582 PropertyName* funcName = elem->name();
6583 const ModuleValidator::Func* func = m.lookupFunction(funcName);
6584 if (!func)
6585 return m.fail(elem, "function-pointer table's elements must be names of functions");
6586
6587 if (sig) {
6588 if (*sig != func->sig())
6589 return m.fail(elem, "all functions in table must have same signature");
6590 } else {
6591 sig = &func->sig();
6592 }
6593
6594 if (!elems.append(func->index()))
6595 return false;
6596 }
6597
6598 uint32_t funcPtrTableIndex;
6599 if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), *sig, mask, &funcPtrTableIndex))
6600 return false;
6601
6602 if (!m.defineFuncPtrTable(funcPtrTableIndex, Move(elems)))
6603 return m.fail(var, "duplicate function-pointer definition");
6604
6605 return true;
6606 }
6607
6608 static bool
CheckFuncPtrTables(ModuleValidator & m)6609 CheckFuncPtrTables(ModuleValidator& m)
6610 {
6611 while (true) {
6612 ParseNode* varStmt;
6613 if (!ParseVarOrConstStatement(m.parser(), &varStmt))
6614 return false;
6615 if (!varStmt)
6616 break;
6617 for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
6618 if (!CheckFuncPtrTable(m, var))
6619 return false;
6620 }
6621 }
6622
6623 for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
6624 ModuleValidator::FuncPtrTable& funcPtrTable = m.funcPtrTable(i);
6625 if (!funcPtrTable.defined()) {
6626 return m.failNameOffset(funcPtrTable.firstUse(),
6627 "function-pointer table %s wasn't defined",
6628 funcPtrTable.name());
6629 }
6630 }
6631
6632 return true;
6633 }
6634
6635 static bool
CheckModuleExportFunction(ModuleValidator & m,ParseNode * pn,PropertyName * maybeFieldName=nullptr)6636 CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybeFieldName = nullptr)
6637 {
6638 if (!pn->isKind(PNK_NAME))
6639 return m.fail(pn, "expected name of exported function");
6640
6641 PropertyName* funcName = pn->name();
6642 const ModuleValidator::Global* global = m.lookupGlobal(funcName);
6643 if (!global)
6644 return m.failName(pn, "exported function name '%s' not found", funcName);
6645
6646 if (global->which() == ModuleValidator::Global::Function)
6647 return m.addExportedFunction(m.function(global->funcIndex()), maybeFieldName);
6648
6649 if (global->which() == ModuleValidator::Global::ChangeHeap)
6650 return m.addExportedChangeHeap(funcName, *global, maybeFieldName);
6651
6652 return m.failName(pn, "'%s' is not a function", funcName);
6653 }
6654
6655 static bool
CheckModuleExportObject(ModuleValidator & m,ParseNode * object)6656 CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
6657 {
6658 MOZ_ASSERT(object->isKind(PNK_OBJECT));
6659
6660 for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
6661 if (!IsNormalObjectField(m.cx(), pn))
6662 return m.fail(pn, "only normal object properties may be used in the export object literal");
6663
6664 PropertyName* fieldName = ObjectNormalFieldName(m.cx(), pn);
6665
6666 ParseNode* initNode = ObjectNormalFieldInitializer(m.cx(), pn);
6667 if (!initNode->isKind(PNK_NAME))
6668 return m.fail(initNode, "initializer of exported object literal must be name of function");
6669
6670 if (!CheckModuleExportFunction(m, initNode, fieldName))
6671 return false;
6672 }
6673
6674 return true;
6675 }
6676
6677 static bool
CheckModuleReturn(ModuleValidator & m)6678 CheckModuleReturn(ModuleValidator& m)
6679 {
6680 TokenKind tk;
6681 if (!GetToken(m.parser(), &tk))
6682 return false;
6683 TokenStream& ts = m.parser().tokenStream;
6684 if (tk != TOK_RETURN) {
6685 const char* msg = (tk == TOK_RC || tk == TOK_EOF)
6686 ? "expecting return statement"
6687 : "invalid asm.js. statement";
6688 return m.failOffset(ts.currentToken().pos.begin, msg);
6689 }
6690 ts.ungetToken();
6691
6692 ParseNode* returnStmt = m.parser().statement(YieldIsName);
6693 if (!returnStmt)
6694 return false;
6695
6696 ParseNode* returnExpr = ReturnExpr(returnStmt);
6697 if (!returnExpr)
6698 return m.fail(returnStmt, "export statement must return something");
6699
6700 if (returnExpr->isKind(PNK_OBJECT)) {
6701 if (!CheckModuleExportObject(m, returnExpr))
6702 return false;
6703 } else {
6704 if (!CheckModuleExportFunction(m, returnExpr))
6705 return false;
6706 }
6707
6708 // Function statements are not added to the lexical scope in ParseContext
6709 // (since cx->tempLifoAlloc is marked/released after each function
6710 // statement) and thus all the identifiers in the return statement will be
6711 // mistaken as free variables and added to lexdeps. Clear these now.
6712 m.parser().pc->lexdeps->clear();
6713 return true;
6714 }
6715
6716 static bool
CheckModuleEnd(ModuleValidator & m)6717 CheckModuleEnd(ModuleValidator &m)
6718 {
6719 TokenKind tk;
6720 if (!GetToken(m.parser(), &tk))
6721 return false;
6722
6723 if (tk != TOK_EOF && tk != TOK_RC) {
6724 return m.failOffset(m.parser().tokenStream.currentToken().pos.begin,
6725 "top-level export (return) must be the last statement");
6726 }
6727
6728 m.parser().tokenStream.ungetToken();
6729 return true;
6730 }
6731
6732 static bool
CheckModule(ExclusiveContext * cx,AsmJSParser & parser,ParseNode * stmtList,ScopedJSDeletePtr<AsmJSModule> * module,unsigned * time,SlowFunctionVector * slowFuncs)6733 CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
6734 ScopedJSDeletePtr<AsmJSModule>* module, unsigned* time,
6735 SlowFunctionVector* slowFuncs)
6736 {
6737 int64_t before = PRMJ_Now();
6738
6739 ModuleValidator m(cx, parser);
6740 if (!m.init())
6741 return false;
6742
6743 if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) {
6744 if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName))
6745 return false;
6746 m.initModuleFunctionName(moduleFunctionName);
6747 }
6748
6749 if (!CheckFunctionHead(m, m.moduleFunctionNode()))
6750 return false;
6751
6752 if (!CheckModuleArguments(m, m.moduleFunctionNode()))
6753 return false;
6754
6755 if (!CheckPrecedingStatements(m, stmtList))
6756 return false;
6757
6758 if (!CheckModuleProcessingDirectives(m))
6759 return false;
6760
6761 if (!CheckModuleGlobals(m))
6762 return false;
6763
6764 m.startFunctionBodies();
6765
6766 #if !defined(ENABLE_SHARED_ARRAY_BUFFER)
6767 if (m.usesSharedMemory())
6768 return m.failOffset(m.parser().tokenStream.currentToken().pos.begin,
6769 "shared memory and atomics not supported by this build");
6770 #endif
6771
6772 if (!CheckFunctions(m))
6773 return false;
6774
6775 if (!CheckFuncPtrTables(m))
6776 return false;
6777
6778 if (!CheckModuleReturn(m))
6779 return false;
6780
6781 if (!CheckModuleEnd(m))
6782 return false;
6783
6784 if (!m.finish(module, slowFuncs))
6785 return false;
6786
6787 *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
6788 return true;
6789 }
6790
6791 static bool
BuildConsoleMessage(ExclusiveContext * cx,AsmJSModule & module,unsigned time,const SlowFunctionVector & slowFuncs,JS::AsmJSCacheResult cacheResult,ScopedJSFreePtr<char> * out)6792 BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module,
6793 unsigned time, const SlowFunctionVector& slowFuncs,
6794 JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out)
6795 {
6796 #ifndef JS_MORE_DETERMINISTIC
6797 ScopedJSFreePtr<char> slowText;
6798 if (!slowFuncs.empty()) {
6799 slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length()));
6800 if (!slowText)
6801 return true;
6802
6803 for (unsigned i = 0; i < slowFuncs.length(); i++) {
6804 const SlowFunction& func = slowFuncs[i];
6805 JSAutoByteString name;
6806 if (!AtomToPrintableString(cx, func.name, &name))
6807 return false;
6808
6809 slowText.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowText.get(),
6810 name.ptr(), func.line, func.column, func.ms,
6811 i+1 < slowFuncs.length() ? ", " : ""));
6812 if (!slowText)
6813 return true;
6814 }
6815 }
6816
6817 const char* cacheString = "";
6818 switch (cacheResult) {
6819 case JS::AsmJSCache_Success:
6820 cacheString = "stored in cache";
6821 break;
6822 case JS::AsmJSCache_ModuleTooSmall:
6823 cacheString = "not stored in cache (too small to benefit)";
6824 break;
6825 case JS::AsmJSCache_SynchronousScript:
6826 cacheString = "unable to cache asm.js in synchronous scripts; try loading "
6827 "asm.js via <script async> or createElement('script')";
6828 break;
6829 case JS::AsmJSCache_QuotaExceeded:
6830 cacheString = "not enough temporary storage quota to store in cache";
6831 break;
6832 case JS::AsmJSCache_StorageInitFailure:
6833 cacheString = "storage initialization failed (consider filing a bug)";
6834 break;
6835 case JS::AsmJSCache_Disabled_Internal:
6836 cacheString = "caching disabled by internal configuration (consider filing a bug)";
6837 break;
6838 case JS::AsmJSCache_Disabled_ShellFlags:
6839 cacheString = "caching disabled by missing command-line arguments";
6840 break;
6841 case JS::AsmJSCache_Disabled_JitInspector:
6842 cacheString = "caching disabled by active JIT inspector";
6843 break;
6844 case JS::AsmJSCache_InternalError:
6845 cacheString = "unable to store in cache due to internal error (consider filing a bug)";
6846 break;
6847 case JS::AsmJSCache_LIMIT:
6848 MOZ_CRASH("bad AsmJSCacheResult");
6849 break;
6850 }
6851
6852 out->reset(JS_smprintf("total compilation time %dms; %s%s",
6853 time, cacheString, slowText ? slowText.get() : ""));
6854 #endif
6855
6856 return true;
6857 }
6858
6859 static bool
Warn(AsmJSParser & parser,int errorNumber,const char * str)6860 Warn(AsmJSParser& parser, int errorNumber, const char* str)
6861 {
6862 ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
6863 errorNumber == JSMSG_USE_ASM_TYPE_FAIL
6864 ? ParseError
6865 : ParseWarning;
6866 parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
6867 return false;
6868 }
6869
6870 static bool
EstablishPreconditions(ExclusiveContext * cx,AsmJSParser & parser)6871 EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
6872 {
6873 #ifdef JS_CODEGEN_NONE
6874 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of a JIT compiler");
6875 #endif
6876
6877 if (!cx->jitSupportsFloatingPoint())
6878 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
6879
6880 if (cx->gcSystemPageSize() != AsmJSPageSize)
6881 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
6882
6883 switch (parser.options().asmJSOption) {
6884 case AsmJSOption::Disabled:
6885 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
6886 case AsmJSOption::DisabledByDebugger:
6887 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
6888 case AsmJSOption::Enabled:
6889 break;
6890 }
6891
6892 if (parser.pc->isGenerator())
6893 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
6894
6895 if (parser.pc->isArrowFunction())
6896 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
6897
6898 // Class constructors are also methods
6899 if (parser.pc->isMethod())
6900 return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
6901
6902 return true;
6903 }
6904
6905 static bool
NoExceptionPending(ExclusiveContext * cx)6906 NoExceptionPending(ExclusiveContext* cx)
6907 {
6908 return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
6909 }
6910
6911 bool
ValidateAsmJS(ExclusiveContext * cx,AsmJSParser & parser,ParseNode * stmtList,bool * validated)6912 js::ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
6913 {
6914 *validated = false;
6915
6916 // Various conditions disable asm.js optimizations.
6917 if (!EstablishPreconditions(cx, parser))
6918 return NoExceptionPending(cx);
6919
6920 ScopedJSDeletePtr<AsmJSModule> module;
6921 ScopedJSFreePtr<char> message;
6922
6923 // Before spending any time parsing the module, try to look it up in the
6924 // embedding's cache using the chars about to be parsed as the key.
6925 if (!LookupAsmJSModuleInCache(cx, parser, &module, &message))
6926 return false;
6927
6928 // If not present in the cache, parse, validate and generate code in a
6929 // single linear pass over the chars of the asm.js module.
6930 if (!module) {
6931 // "Checking" parses, validates and compiles, producing a fully compiled
6932 // AsmJSModule as result.
6933 unsigned time;
6934 SlowFunctionVector slowFuncs(cx);
6935 if (!CheckModule(cx, parser, stmtList, &module, &time, &slowFuncs))
6936 return NoExceptionPending(cx);
6937
6938 // Try to store the AsmJSModule in the embedding's cache. The
6939 // AsmJSModule must be stored before static linking since static linking
6940 // specializes the AsmJSModule to the current process's address space
6941 // and therefore must be executed after a cache hit.
6942 JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, *module, cx);
6943 module->staticallyLink(cx);
6944
6945 if (!BuildConsoleMessage(cx, *module, time, slowFuncs, cacheResult, &message))
6946 return false;
6947 }
6948
6949 // The AsmJSModuleObject isn't directly referenced by user code; it is only
6950 // referenced (and kept alive by) an internal slot of the asm.js module
6951 // function generated below and asm.js export functions generated when the
6952 // asm.js module function is called.
6953 RootedObject moduleObj(cx, AsmJSModuleObject::create(cx, &module));
6954 if (!moduleObj)
6955 return false;
6956
6957 // The module function dynamically links the AsmJSModule when called and
6958 // generates a set of functions wrapping all the exports.
6959 FunctionBox* funbox = parser.pc->maybeFunction->pn_funbox;
6960 RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj));
6961 if (!moduleFun)
6962 return false;
6963
6964 // Finished! Clobber the default function created by the parser with the new
6965 // asm.js module function. Special cases in the bytecode emitter avoid
6966 // generating bytecode for asm.js functions, allowing this asm.js module
6967 // function to be the finished result.
6968 MOZ_ASSERT(funbox->function()->isInterpreted());
6969 funbox->object = moduleFun;
6970
6971 // Success! Write to the console with a "warning" message.
6972 *validated = true;
6973 Warn(parser, JSMSG_USE_ASM_TYPE_OK, message.get());
6974 return NoExceptionPending(cx);
6975 }
6976
6977 bool
IsAsmJSCompilationAvailable(JSContext * cx,unsigned argc,Value * vp)6978 js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp)
6979 {
6980 CallArgs args = CallArgsFromVp(argc, vp);
6981
6982 // See EstablishPreconditions.
6983 #ifdef JS_CODEGEN_NONE
6984 bool available = false;
6985 #else
6986 bool available = cx->jitSupportsFloatingPoint() &&
6987 cx->gcSystemPageSize() == AsmJSPageSize &&
6988 cx->runtime()->options().asmJS();
6989 #endif
6990
6991 args.rval().set(BooleanValue(available));
6992 return true;
6993 }
6994
6995