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