v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
asm-parser.cc
Go to the documentation of this file.
1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <math.h>
8#include <string.h>
9
10#include <algorithm>
11#include <optional>
12
13#include "src/asmjs/asm-js.h"
14#include "src/asmjs/asm-types.h"
16#include "src/flags/flags.h"
18#include "src/parsing/scanner.h"
21
22namespace v8 {
23namespace internal {
24namespace wasm {
25
26#ifdef DEBUG
27#define TRACE_ASM_PARSER(...) \
28 if (v8_flags.trace_asm_parser) { \
29 PrintF(__VA_ARGS__); \
30 }
31#else
32#define TRACE_ASM_PARSER(...)
33#endif
34
35#define FAIL_AND_RETURN(ret, msg) \
36 failed_ = true; \
37 failure_message_ = msg; \
38 failure_location_ = static_cast<int>(scanner_.Position()); \
39 TRACE_ASM_PARSER("[asm.js failure: %s, token: '%s', see: %s:%d]\n", msg, \
40 scanner_.Name(scanner_.Token()).c_str(), __FILE__, \
41 __LINE__); \
42 return ret;
43
44#define FAIL(msg) FAIL_AND_RETURN(, msg)
45#define FAILn(msg) FAIL_AND_RETURN(nullptr, msg)
46
47#define EXPECT_TOKEN_OR_RETURN(ret, token) \
48 do { \
49 if (scanner_.Token() != token) { \
50 FAIL_AND_RETURN(ret, "Unexpected token"); \
51 } \
52 scanner_.Next(); \
53 } while (false)
54
55#define EXPECT_TOKEN(token) EXPECT_TOKEN_OR_RETURN(, token)
56#define EXPECT_TOKENn(token) EXPECT_TOKEN_OR_RETURN(nullptr, token)
57
58#define RECURSE_OR_RETURN(ret, call) \
59 do { \
60 DCHECK(!failed_); \
61 if (GetCurrentStackPosition() < stack_limit_) { \
62 FAIL_AND_RETURN(ret, "Stack overflow while parsing asm.js module."); \
63 } \
64 call; \
65 if (failed_) return ret; \
66 } while (false)
67
68#define RECURSE(call) RECURSE_OR_RETURN(, call)
69#define RECURSEn(call) RECURSE_OR_RETURN(nullptr, call)
70
71#define TOK(name) AsmJsScanner::kToken_##name
72
73AsmJsParser::AsmJsParser(Zone* zone, uintptr_t stack_limit,
75 : zone_(zone),
76 scanner_(stream),
77 module_builder_(zone->New<WasmModuleBuilder>(zone)),
78 stack_limit_(stack_limit),
79 block_stack_(zone),
80 global_imports_(zone) {
83}
84
86 auto* d = AsmType::Double();
87 auto* dq = AsmType::DoubleQ();
89 stdlib_dq2d_->AsFunctionType()->AddArgument(dq);
90
92 stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
93 stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
94
95 auto* f = AsmType::Float();
96 auto* fh = AsmType::Floatish();
97 auto* fq = AsmType::FloatQ();
98 auto* fq2fh = AsmType::Function(zone(), fh);
99 fq2fh->AsFunctionType()->AddArgument(fq);
100
101 auto* s = AsmType::Signed();
102 auto* u = AsmType::Unsigned();
103 auto* s2u = AsmType::Function(zone(), u);
104 s2u->AsFunctionType()->AddArgument(s);
105
106 auto* i = AsmType::Int();
108 stdlib_i2s_->AsFunctionType()->AddArgument(i);
109
111 stdlib_ii2s_->AsFunctionType()->AddArgument(i);
112 stdlib_ii2s_->AsFunctionType()->AddArgument(i);
113
114 // The signatures in "9 Standard Library" of the spec draft are outdated and
115 // have been superseded with the following by an errata:
116 // - Math.min/max : (signed, signed...) -> signed
117 // (double, double...) -> double
118 // (float, float...) -> float
119 auto* minmax_d = AsmType::MinMaxType(zone(), d, d);
120 auto* minmax_f = AsmType::MinMaxType(zone(), f, f);
121 auto* minmax_s = AsmType::MinMaxType(zone(), s, s);
123 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_s);
124 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f);
125 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d);
126
127 // The signatures in "9 Standard Library" of the spec draft are outdated and
128 // have been superseded with the following by an errata:
129 // - Math.abs : (signed) -> unsigned
130 // (double?) -> double
131 // (float?) -> floatish
133 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(s2u);
134 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
135 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(fq2fh);
136
137 // The signatures in "9 Standard Library" of the spec draft are outdated and
138 // have been superseded with the following by an errata:
139 // - Math.ceil/floor/sqrt : (double?) -> double
140 // (float?) -> floatish
142 stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
143 stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(fq2fh);
144
146}
147
149 const ZoneVector<AsmType*>& params) {
150 FunctionSig::Builder sig_builder(
151 zone(), !return_type->IsA(AsmType::Void()) ? 1 : 0, params.size());
152 for (auto param : params) {
153 if (param->IsA(AsmType::Double())) {
154 sig_builder.AddParam(kWasmF64);
155 } else if (param->IsA(AsmType::Float())) {
156 sig_builder.AddParam(kWasmF32);
157 } else if (param->IsA(AsmType::Int())) {
158 sig_builder.AddParam(kWasmI32);
159 } else {
160 UNREACHABLE();
161 }
162 }
163 if (!return_type->IsA(AsmType::Void())) {
164 if (return_type->IsA(AsmType::Double())) {
165 sig_builder.AddReturn(kWasmF64);
166 } else if (return_type->IsA(AsmType::Float())) {
167 sig_builder.AddReturn(kWasmF32);
168 } else if (return_type->IsA(AsmType::Signed())) {
169 sig_builder.AddReturn(kWasmI32);
170 } else {
171 UNREACHABLE();
172 }
173 }
174 return sig_builder.Get();
175}
176
179 return !failed_;
180}
181
183 public:
184 explicit TemporaryVariableScope(AsmJsParser* parser) : parser_(parser) {
185 local_depth_ = parser_->function_temp_locals_depth_;
186 parser_->function_temp_locals_depth_++;
187 }
189 DCHECK_EQ(local_depth_, parser_->function_temp_locals_depth_ - 1);
190 parser_->function_temp_locals_depth_--;
191 }
192 uint32_t get() const { return parser_->TempVariable(local_depth_); }
193
194 private:
197};
198
200 AsmJsScanner::token_t token) {
201 const bool is_global = AsmJsScanner::IsGlobal(token);
202 DCHECK(is_global || AsmJsScanner::IsLocal(token));
203 base::Vector<VarInfo>& var_info =
204 is_global ? global_var_info_ : local_var_info_;
205 size_t old_capacity = var_info.size();
206 size_t index = is_global ? AsmJsScanner::GlobalIndex(token)
208 if (is_global && index + 1 > num_globals_) num_globals_ = index + 1;
209 if (index + 1 > old_capacity) {
210 size_t new_size = std::max(2 * old_capacity, index + 1);
211 base::Vector<VarInfo> new_info = zone_->NewVector<VarInfo>(new_size);
212 std::copy(var_info.begin(), var_info.end(), new_info.begin());
213 var_info = new_info;
214 }
215 return &var_info[index];
216}
217
219 DCHECK_EQ(info->kind, VarKind::kGlobal);
220 return info->index + static_cast<uint32_t>(global_imports_.size());
221}
222
224 ValueType vtype, bool mutable_variable,
225 VarInfo* info) {
226 // Allocate a separate variable for the import.
227 // TODO(asmjs): Consider using the imported global directly instead of
228 // allocating a separate global variable for immutable (i.e. const) imports.
229 DeclareGlobal(info, mutable_variable, type, vtype,
231
232 // Record the need to initialize the global from the import.
233 global_imports_.push_back({name, vtype, info});
234}
235
236void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
237 AsmType* type, ValueType vtype,
238 WasmInitExpr init) {
239 info->kind = VarKind::kGlobal;
240 info->type = type;
241 info->index = module_builder_->AddGlobal(vtype, true, init);
242 info->mutable_variable = mutable_variable;
243}
244
246 AsmType* type) {
247 info->kind = kind;
248 info->type = type;
249 info->index = 0; // unused
250 info->mutable_variable = false;
251}
252
253uint32_t AsmJsParser::TempVariable(int index) {
254 if (index + 1 > function_temp_locals_used_) {
255 function_temp_locals_used_ = index + 1;
256 }
258}
259
263
265 if (Check(';')) {
266 // Had a semicolon.
267 } else if (!Peek('}') && !scanner_.IsPrecededByNewline()) {
268 FAIL("Expected ;");
269 }
270}
271
276
283
285 BareEnd();
287}
288
291 info.kind = kind;
292 info.label = label;
293 block_stack_.push_back(info);
294}
295
297 DCHECK_GT(block_stack_.size(), 0);
298 block_stack_.pop_back();
299}
300
302 int count = 0;
303 for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
304 ++it, ++count) {
305 // A 'continue' statement targets ...
306 // - The innermost {kLoop} block if no label is given.
307 // - The matching {kLoop} block (when a label is provided).
308 if (it->kind == BlockKind::kLoop &&
309 (label == kTokenNone || it->label == label)) {
310 return count;
311 }
312 }
313 return -1;
314}
315
317 int count = 0;
318 for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
319 ++it, ++count) {
320 // A 'break' statement targets ...
321 // - The innermost {kRegular} block if no label is given.
322 // - The matching {kRegular} or {kNamed} block (when a label is provided).
323 if ((it->kind == BlockKind::kRegular &&
324 (label == kTokenNone || it->label == label)) ||
325 (it->kind == BlockKind::kNamed && it->label == label)) {
326 return count;
327 }
328 }
329 return -1;
330}
331
332// 6.1 ValidateModule
335 EXPECT_TOKEN('{');
336 EXPECT_TOKEN(TOK(UseAsm));
339 while (Peek(TOK(function))) {
341 }
342 while (Peek(TOK(var))) {
344 }
347 EXPECT_TOKEN('}');
348
349 // Check that all functions were eventually defined.
350 for (auto& info : global_var_info_.SubVector(0, num_globals_)) {
351 if (info.kind == VarKind::kFunction && !info.function_defined) {
352 FAIL("Undefined function");
353 }
354 if (info.kind == VarKind::kTable && !info.function_defined) {
355 FAIL("Undefined function table");
356 }
357 if (info.kind == VarKind::kImportedFunction && !info.function_defined) {
358 // For imported functions without a single call site, we insert a dummy
359 // import here to preserve the fact that there actually was an import.
360 FunctionSig* void_void_sig = FunctionSig::Builder(zone(), 0, 0).Get();
361 module_builder_->AddImport(info.import->function_name, void_void_sig);
362 }
363 }
364
365 // Add start function to initialize things.
368 for (auto& global_import : global_imports_) {
369 uint32_t import_index = module_builder_->AddGlobalImport(
370 global_import.import_name, global_import.value_type,
371 false /* mutability */);
372 start->EmitWithU32V(kExprGlobalGet, import_index);
373 start->EmitWithU32V(kExprGlobalSet, VarIndex(global_import.var_info));
374 }
375 start->Emit(kExprEnd);
376 FunctionSig::Builder b(zone(), 0, 0);
377 start->SetSignature(b.Get());
378}
379
380// 6.1 ValidateModule - parameters
382 EXPECT_TOKEN('(');
383 stdlib_name_ = 0;
384 foreign_name_ = 0;
385 heap_name_ = 0;
386 if (!Peek(')')) {
387 if (!scanner_.IsGlobal()) {
388 FAIL("Expected stdlib parameter");
389 }
391 if (!Peek(')')) {
392 EXPECT_TOKEN(',');
393 if (!scanner_.IsGlobal()) {
394 FAIL("Expected foreign parameter");
395 }
398 FAIL("Duplicate parameter name");
399 }
400 if (!Peek(')')) {
401 EXPECT_TOKEN(',');
402 if (!scanner_.IsGlobal()) {
403 FAIL("Expected heap parameter");
404 }
407 FAIL("Duplicate parameter name");
408 }
409 }
410 }
411 }
412 EXPECT_TOKEN(')');
413}
414
415// 6.1 ValidateModule - variables
417 while (Peek(TOK(var)) || Peek(TOK(const))) {
418 bool mutable_variable = true;
419 if (Check(TOK(var))) {
420 // Had a var.
421 } else {
422 EXPECT_TOKEN(TOK(const));
423 mutable_variable = false;
424 }
425 for (;;) {
426 RECURSE(ValidateModuleVar(mutable_variable));
427 if (Check(',')) {
428 continue;
429 }
430 break;
431 }
433 }
434}
435
436// 6.1 ValidateModule - one variable
437void AsmJsParser::ValidateModuleVar(bool mutable_variable) {
438 if (!scanner_.IsGlobal()) {
439 FAIL("Expected identifier");
440 }
444 FAIL("Cannot shadow parameters");
445 }
447 if (info->kind != VarKind::kUnused) {
448 FAIL("Redefinition of variable");
449 }
450 EXPECT_TOKEN('=');
451 double dvalue = 0.0;
452 uint32_t uvalue = 0;
453 if (CheckForDouble(&dvalue)) {
454 DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
455 WasmInitExpr(dvalue));
456 } else if (CheckForUnsigned(&uvalue)) {
457 if (uvalue > 0x7FFFFFFF) {
458 FAIL("Numeric literal out of range");
459 }
460 DeclareGlobal(info, mutable_variable,
461 mutable_variable ? AsmType::Int() : AsmType::Signed(),
462 kWasmI32, WasmInitExpr(static_cast<int32_t>(uvalue)));
463 } else if (Check('-')) {
464 if (CheckForDouble(&dvalue)) {
465 DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
466 WasmInitExpr(-dvalue));
467 } else if (CheckForUnsigned(&uvalue)) {
468 if (uvalue > 0x7FFFFFFF) {
469 FAIL("Numeric literal out of range");
470 }
471 if (uvalue == 0) {
472 // '-0' is treated as float.
473 DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
474 WasmInitExpr(-0.f));
475 } else {
476 DeclareGlobal(info, mutable_variable,
477 mutable_variable ? AsmType::Int() : AsmType::Signed(),
478 kWasmI32, WasmInitExpr(-static_cast<int32_t>(uvalue)));
479 }
480 } else {
481 FAIL("Expected numeric literal");
482 }
483 } else if (Check(TOK(new))) {
485 } else if (Check(stdlib_name_)) {
486 EXPECT_TOKEN('.');
488 } else if (Peek(foreign_name_) || Peek('+')) {
489 RECURSE(ValidateModuleVarImport(info, mutable_variable));
490 } else if (scanner_.IsGlobal()) {
491 RECURSE(ValidateModuleVarFromGlobal(info, mutable_variable));
492 } else {
493 FAIL("Bad variable declaration");
494 }
495}
496
497// 6.1 ValidateModule - global float declaration
499 bool mutable_variable) {
500 VarInfo* src_info = GetVarInfo(Consume());
501 if (!src_info->type->IsA(stdlib_fround_)) {
502 if (src_info->mutable_variable) {
503 FAIL("Can only use immutable variables in global definition");
504 }
505 if (mutable_variable) {
506 FAIL("Can only define immutable variables with other immutables");
507 }
508 if (!src_info->type->IsA(AsmType::Int()) &&
509 !src_info->type->IsA(AsmType::Float()) &&
510 !src_info->type->IsA(AsmType::Double())) {
511 FAIL("Expected int, float, double, or fround for global definition");
512 }
513 info->kind = VarKind::kGlobal;
514 info->type = src_info->type;
515 info->index = src_info->index;
516 info->mutable_variable = false;
517 return;
518 }
519 EXPECT_TOKEN('(');
520 bool negate = false;
521 if (Check('-')) {
522 negate = true;
523 }
524 double dvalue = 0.0;
525 uint32_t uvalue = 0;
526 if (CheckForDouble(&dvalue)) {
527 if (negate) {
528 dvalue = -dvalue;
529 }
530 DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
532 } else if (CheckForUnsigned(&uvalue)) {
533 dvalue = uvalue;
534 if (negate) {
535 dvalue = -dvalue;
536 }
537 DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
538 WasmInitExpr(static_cast<float>(dvalue)));
539 } else {
540 FAIL("Expected numeric literal");
541 }
542 EXPECT_TOKEN(')');
543}
544
545// 6.1 ValidateModule - foreign imports
547 bool mutable_variable) {
548 if (Check('+')) {
550 EXPECT_TOKEN('.');
552 AddGlobalImport(name, AsmType::Double(), kWasmF64, mutable_variable, info);
553 scanner_.Next();
554 } else {
556 EXPECT_TOKEN('.');
558 scanner_.Next();
559 if (Check('|')) {
560 if (!CheckForZero()) {
561 FAIL("Expected |0 type annotation for foreign integer import");
562 }
563 AddGlobalImport(name, AsmType::Int(), kWasmI32, mutable_variable, info);
564 } else {
565 info->kind = VarKind::kImportedFunction;
566 info->import = zone()->New<FunctionImportInfo>(name, zone());
567 info->mutable_variable = false;
568 }
569 }
570}
571
572// 6.1 ValidateModule - one variable
573// 9 - Standard Library - heap types
576 EXPECT_TOKEN('.');
577 switch (Consume()) {
578#define V(name, _junk1, _junk2, _junk3) \
579 case TOK(name): \
580 DeclareStdlibFunc(info, VarKind::kSpecial, AsmType::name()); \
581 stdlib_uses_.Add(StandardMember::k##name); \
582 break;
584#undef V
585 default:
586 FAIL("Expected ArrayBuffer view");
587 }
588 EXPECT_TOKEN('(');
590 EXPECT_TOKEN(')');
591}
592
593// 6.1 ValidateModule - one variable
594// 9 - Standard Library
596 if (Check(TOK(Math))) {
597 EXPECT_TOKEN('.');
598 switch (Consume()) {
599#define V(name, const_value) \
600 case TOK(name): \
601 DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
602 WasmInitExpr(const_value)); \
603 stdlib_uses_.Add(StandardMember::kMath##name); \
604 break;
606#undef V
607#define V(name, Name, op, sig) \
608 case TOK(name): \
609 DeclareStdlibFunc(info, VarKind::kMath##Name, stdlib_##sig##_); \
610 stdlib_uses_.Add(StandardMember::kMath##Name); \
611 break;
613#undef V
614 default:
615 FAIL("Invalid member of stdlib.Math");
616 }
617 } else if (Check(TOK(Infinity))) {
618 DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
619 WasmInitExpr(std::numeric_limits<double>::infinity()));
621 } else if (Check(TOK(NaN))) {
622 DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
623 WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
625 } else {
626 FAIL("Invalid member of stdlib");
627 }
628}
629
630// 6.2 ValidateExport
632 // clang-format off
633 EXPECT_TOKEN(TOK(return));
634 // clang-format on
635 if (Check('{')) {
636 for (;;) {
638 if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
639 FAIL("Illegal export name");
640 }
641 Consume();
642 EXPECT_TOKEN(':');
643 if (!scanner_.IsGlobal()) {
644 FAIL("Expected function name");
645 }
646 VarInfo* info = GetVarInfo(Consume());
647 if (info->kind != VarKind::kFunction) {
648 FAIL("Expected function");
649 }
650 module_builder_->AddExport(name, info->function_builder);
651 if (Check(',')) {
652 if (!Peek('}')) {
653 continue;
654 }
655 }
656 break;
657 }
658 EXPECT_TOKEN('}');
659 } else {
660 if (!scanner_.IsGlobal()) {
661 FAIL("Single function export must be a function name");
662 }
663 VarInfo* info = GetVarInfo(Consume());
664 if (info->kind != VarKind::kFunction) {
665 FAIL("Single function export must be a function");
666 }
668 info->function_builder);
669 }
670}
671
672// 6.3 ValidateFunctionTable
674 EXPECT_TOKEN(TOK(var));
675 if (!scanner_.IsGlobal()) {
676 FAIL("Expected table name");
677 }
678 VarInfo* table_info = GetVarInfo(Consume());
679 if (table_info->kind == VarKind::kTable) {
680 if (table_info->function_defined) {
681 FAIL("Function table redefined");
682 }
683 table_info->function_defined = true;
684 } else if (table_info->kind != VarKind::kUnused) {
685 FAIL("Function table name collides");
686 }
687 EXPECT_TOKEN('=');
688 EXPECT_TOKEN('[');
689 uint64_t count = 0;
690 for (;;) {
691 if (!scanner_.IsGlobal()) {
692 FAIL("Expected function name");
693 }
694 VarInfo* info = GetVarInfo(Consume());
695 if (info->kind != VarKind::kFunction) {
696 FAIL("Expected function");
697 }
698 // Only store the function into a table if we used the table somewhere
699 // (i.e. tables are first seen at their use sites and allocated there).
700 if (table_info->kind == VarKind::kTable) {
701 if (count >= static_cast<uint64_t>(table_info->mask) + 1) {
702 FAIL("Exceeded function table size");
703 }
704 if (!info->type->IsA(table_info->type)) {
705 FAIL("Function table definition doesn't match use");
706 }
708 0, static_cast<uint32_t>(table_info->index + count), info->index,
710 }
711 ++count;
712 if (Check(',')) {
713 if (!Peek(']')) {
714 continue;
715 }
716 }
717 break;
718 }
719 EXPECT_TOKEN(']');
720 if (table_info->kind == VarKind::kTable &&
721 count != static_cast<uint64_t>(table_info->mask) + 1) {
722 FAIL("Function table size does not match uses");
723 }
725}
726
727// 6.4 ValidateFunction
729 // Remember position of the 'function' token as start position.
730 size_t function_start_position = scanner_.Position();
731
732 EXPECT_TOKEN(TOK(function));
733 if (!scanner_.IsGlobal()) {
734 FAIL("Expected function name");
735 }
736
738 AsmJsScanner::token_t function_name = Consume();
739 VarInfo* function_info = GetVarInfo(function_name);
740 if (function_info->kind == VarKind::kUnused) {
741 function_info->kind = VarKind::kFunction;
742 function_info->function_builder = module_builder_->AddFunction();
743 function_info->index = function_info->function_builder->func_index();
744 function_info->mutable_variable = false;
745 } else if (function_info->kind != VarKind::kFunction) {
746 FAIL("Function name collides with variable");
747 } else if (function_info->function_defined) {
748 FAIL("Function redefined");
749 }
750
751 function_info->function_defined = true;
752 function_info->function_builder->SetName(function_name_str);
754 return_type_ = nullptr;
755
756 // Record start of the function, used as position for the stack check.
758 function_start_position);
759
761 ValidateFunctionParams(&params);
762
763 // Check against limit on number of parameters.
764 if (params.size() > kV8MaxWasmFunctionParams) {
765 FAIL("Number of parameters exceeds internal limit");
766 }
767
769 ValidateFunctionLocals(params.size(), &locals);
770
771 function_temp_locals_offset_ = static_cast<uint32_t>(
772 params.size() + locals.size());
775
776 bool last_statement_is_return = false;
777 while (!failed_ && !Peek('}')) {
778 // clang-format off
779 last_statement_is_return = Peek(TOK(return));
780 // clang-format on
782 }
783
784 size_t function_end_position = scanner_.Position() + 1;
785
786 EXPECT_TOKEN('}');
787
788 if (!last_statement_is_return) {
789 if (return_type_ == nullptr) {
790 return_type_ = AsmType::Void();
791 } else if (!return_type_->IsA(AsmType::Void())) {
792 FAIL("Expected return at end of non-void function");
793 }
794 }
796
797 // TODO(bradnelson): WasmModuleBuilder can't take this in the right order.
798 // We should fix that so we can use it instead.
801 for (auto local : locals) {
803 }
804 // Add bonus temps.
805 for (int i = 0; i < function_temp_locals_used_; ++i) {
807 }
808
809 // Check against limit on number of local variables.
811 FAIL("Number of local variables exceeds internal limit");
812 }
813
814 // End function
816
817 // Emit function end position as the last position for this function.
818 current_function_builder_->AddAsmWasmOffset(function_end_position,
819 function_end_position);
820
822 FAIL("Size of function body exceeds internal limit");
823 }
824 // Record (or validate) function type.
825 AsmType* function_type = AsmType::Function(zone(), return_type_);
826 for (auto t : params) {
827 function_type->AsFunctionType()->AddArgument(t);
828 }
829 function_info = GetVarInfo(function_name);
830 if (function_info->type->IsA(AsmType::None())) {
831 DCHECK_EQ(function_info->kind, VarKind::kFunction);
832 function_info->type = function_type;
833 } else if (!function_type->IsA(function_info->type)) {
834 // TODO(bradnelson): Should IsExactly be used here?
835 FAIL("Function definition doesn't match use");
836 }
837
839 std::fill(local_var_info_.begin(), local_var_info_.end(), VarInfo{});
840}
841
842// 6.4 ValidateFunction
844 // TODO(bradnelson): Do this differently so that the scanner doesn't need to
845 // have a state transition that needs knowledge of how the scanner works
846 // inside.
848 EXPECT_TOKEN('(');
849 CachedVector<AsmJsScanner::token_t> function_parameters(
851 while (!failed_ && !Peek(')')) {
852 if (!scanner_.IsLocal()) {
853 FAIL("Expected parameter name");
854 }
855 function_parameters.push_back(Consume());
856 if (!Peek(')')) {
857 EXPECT_TOKEN(',');
858 }
859 }
860 EXPECT_TOKEN(')');
862 EXPECT_TOKEN('{');
863 // 5.1 Parameter Type Annotations
864 for (auto p : function_parameters) {
865 EXPECT_TOKEN(p);
866 EXPECT_TOKEN('=');
867 VarInfo* info = GetVarInfo(p);
868 if (info->kind != VarKind::kUnused) {
869 FAIL("Duplicate parameter name");
870 }
871 if (Check(p)) {
872 EXPECT_TOKEN('|');
873 if (!CheckForZero()) {
874 FAIL("Bad integer parameter annotation.");
875 }
876 info->kind = VarKind::kLocal;
877 info->type = AsmType::Int();
878 info->index = static_cast<uint32_t>(params->size());
879 params->push_back(AsmType::Int());
880 } else if (Check('+')) {
881 EXPECT_TOKEN(p);
882 info->kind = VarKind::kLocal;
883 info->type = AsmType::Double();
884 info->index = static_cast<uint32_t>(params->size());
885 params->push_back(AsmType::Double());
886 } else {
887 if (!scanner_.IsGlobal() ||
888 !GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
889 FAIL("Expected fround");
890 }
891 EXPECT_TOKEN('(');
892 EXPECT_TOKEN(p);
893 EXPECT_TOKEN(')');
894 info->kind = VarKind::kLocal;
895 info->type = AsmType::Float();
896 info->index = static_cast<uint32_t>(params->size());
897 params->push_back(AsmType::Float());
898 }
900 }
901}
902
903// 6.4 ValidateFunction - locals
905 ZoneVector<ValueType>* locals) {
906 DCHECK(locals->empty());
907 // Local Variables.
908 while (Peek(TOK(var))) {
910 EXPECT_TOKEN(TOK(var));
912 for (;;) {
913 if (!scanner_.IsLocal()) {
914 FAIL("Expected local variable identifier");
915 }
916 VarInfo* info = GetVarInfo(Consume());
917 if (info->kind != VarKind::kUnused) {
918 FAIL("Duplicate local variable name");
919 }
920 // Store types.
921 EXPECT_TOKEN('=');
922 double dvalue = 0.0;
923 uint32_t uvalue = 0;
924 if (Check('-')) {
925 if (CheckForDouble(&dvalue)) {
926 info->kind = VarKind::kLocal;
927 info->type = AsmType::Double();
928 info->index = static_cast<uint32_t>(param_count + locals->size());
929 locals->push_back(kWasmF64);
932 } else if (CheckForUnsigned(&uvalue)) {
933 if (uvalue > 0x7FFFFFFF) {
934 FAIL("Numeric literal out of range");
935 }
936 info->kind = VarKind::kLocal;
937 info->type = AsmType::Int();
938 info->index = static_cast<uint32_t>(param_count + locals->size());
939 locals->push_back(kWasmI32);
940 int32_t value = -static_cast<int32_t>(uvalue);
943 } else {
944 FAIL("Expected variable initial value");
945 }
946 } else if (scanner_.IsGlobal()) {
947 VarInfo* sinfo = GetVarInfo(Consume());
948 if (sinfo->kind == VarKind::kGlobal) {
949 if (sinfo->mutable_variable) {
950 FAIL("Initializing from global requires const variable");
951 }
952 info->kind = VarKind::kLocal;
953 info->type = sinfo->type;
954 info->index = static_cast<uint32_t>(param_count + locals->size());
955 if (sinfo->type->IsA(AsmType::Int())) {
956 locals->push_back(kWasmI32);
957 } else if (sinfo->type->IsA(AsmType::Float())) {
958 locals->push_back(kWasmF32);
959 } else if (sinfo->type->IsA(AsmType::Double())) {
960 locals->push_back(kWasmF64);
961 } else {
962 FAIL("Bad local variable definition");
963 }
965 VarIndex(sinfo));
967 } else if (sinfo->type->IsA(stdlib_fround_)) {
968 EXPECT_TOKEN('(');
969 bool negate = false;
970 if (Check('-')) {
971 negate = true;
972 }
973 if (CheckForDouble(&dvalue)) {
974 info->kind = VarKind::kLocal;
975 info->type = AsmType::Float();
976 info->index = static_cast<uint32_t>(param_count + locals->size());
977 locals->push_back(kWasmF32);
978 if (negate) {
979 dvalue = -dvalue;
980 }
981 float fvalue = DoubleToFloat32(dvalue);
984 } else if (CheckForUnsigned(&uvalue)) {
985 if (uvalue > 0x7FFFFFFF) {
986 FAIL("Numeric literal out of range");
987 }
988 info->kind = VarKind::kLocal;
989 info->type = AsmType::Float();
990 info->index = static_cast<uint32_t>(param_count + locals->size());
991 locals->push_back(kWasmF32);
992 int32_t value = static_cast<int32_t>(uvalue);
993 if (negate) {
994 value = -value;
995 }
996 float fvalue = static_cast<float>(value);
999 } else {
1000 FAIL("Expected variable initial value");
1001 }
1002 EXPECT_TOKEN(')');
1003 } else {
1004 FAIL("expected fround or const global");
1005 }
1006 } else if (CheckForDouble(&dvalue)) {
1007 info->kind = VarKind::kLocal;
1008 info->type = AsmType::Double();
1009 info->index = static_cast<uint32_t>(param_count + locals->size());
1010 locals->push_back(kWasmF64);
1013 } else if (CheckForUnsigned(&uvalue)) {
1014 info->kind = VarKind::kLocal;
1015 info->type = AsmType::Int();
1016 info->index = static_cast<uint32_t>(param_count + locals->size());
1017 locals->push_back(kWasmI32);
1018 int32_t value = static_cast<int32_t>(uvalue);
1021 } else {
1022 FAIL("Expected variable initial value");
1023 }
1024 if (!Peek(',')) {
1025 break;
1026 }
1028 EXPECT_TOKEN(',');
1030 }
1031 SkipSemicolon();
1032 }
1033}
1034
1035// 6.5 ValidateStatement
1037 call_coercion_ = nullptr;
1038 if (Peek('{')) {
1039 RECURSE(Block());
1040 } else if (Peek(';')) {
1042 } else if (Peek(TOK(if))) {
1044 // clang-format off
1045 } else if (Peek(TOK(return))) {
1046 // clang-format on
1048 } else if (IterationStatement()) {
1049 // Handled in IterationStatement.
1050 } else if (Peek(TOK(break))) {
1052 } else if (Peek(TOK(continue))) {
1054 } else if (Peek(TOK(switch))) {
1056 } else {
1058 }
1059}
1060
1061// 6.5.1 Block
1063 bool can_break_to_block = pending_label_ != 0;
1064 if (can_break_to_block) {
1067 }
1068 pending_label_ = 0;
1069 EXPECT_TOKEN('{');
1070 while (!failed_ && !Peek('}')) {
1072 }
1073 EXPECT_TOKEN('}');
1074 if (can_break_to_block) {
1075 End();
1076 }
1077}
1078
1079// 6.5.2 ExpressionStatement
1081 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
1082 // NOTE: Both global or local identifiers can also be used as labels.
1083 scanner_.Next();
1084 if (Peek(':')) {
1085 scanner_.Rewind();
1087 return;
1088 }
1089 scanner_.Rewind();
1090 }
1091 AsmType* ret;
1092 RECURSE(ret = ValidateExpression());
1093 if (!ret->IsA(AsmType::Void())) {
1094 current_function_builder_->Emit(kExprDrop);
1095 }
1096 SkipSemicolon();
1097}
1098
1099// 6.5.3 EmptyStatement
1101
1102// 6.5.4 IfStatement
1104 EXPECT_TOKEN(TOK(if));
1105 EXPECT_TOKEN('(');
1106 RECURSE(Expression(AsmType::Int()));
1107 EXPECT_TOKEN(')');
1111 if (Check(TOK(else))) {
1112 current_function_builder_->Emit(kExprElse);
1114 }
1116 BareEnd();
1117}
1118
1119// 6.5.5 ReturnStatement
1121 // clang-format off
1122 EXPECT_TOKEN(TOK(return));
1123 // clang-format on
1124 if (!Peek(';') && !Peek('}')) {
1125 // TODO(bradnelson): See if this can be factored out.
1126 AsmType* ret;
1128 if (ret->IsA(AsmType::Double())) {
1129 return_type_ = AsmType::Double();
1130 } else if (ret->IsA(AsmType::Float())) {
1131 return_type_ = AsmType::Float();
1132 } else if (ret->IsA(AsmType::Signed())) {
1133 return_type_ = AsmType::Signed();
1134 } else {
1135 FAIL("Invalid return type");
1136 }
1137 } else if (return_type_ == nullptr) {
1138 return_type_ = AsmType::Void();
1139 } else if (!return_type_->IsA(AsmType::Void())) {
1140 FAIL("Invalid void return type");
1141 }
1142 current_function_builder_->Emit(kExprReturn);
1143 SkipSemicolon();
1144}
1145
1146// 6.5.6 IterationStatement
1148 if (Peek(TOK(while))) {
1150 } else if (Peek(TOK(do))) {
1151 DoStatement();
1152 } else if (Peek(TOK(for))) {
1153 ForStatement();
1154 } else {
1155 return false;
1156 }
1157 return true;
1158}
1159
1160// 6.5.6 IterationStatement - while
1162 // a: block {
1164 // b: loop {
1166 pending_label_ = 0;
1167 EXPECT_TOKEN(TOK(while));
1168 EXPECT_TOKEN('(');
1169 RECURSE(Expression(AsmType::Int()));
1170 EXPECT_TOKEN(')');
1171 // if (!CONDITION) break a;
1172 current_function_builder_->Emit(kExprI32Eqz);
1174 // BODY
1176 // continue b;
1178 End();
1179 // }
1180 // }
1181 End();
1182}
1183
1184// 6.5.6 IterationStatement - do
1186 // a: block {
1188 // b: loop {
1189 Loop();
1190 // c: block { // but treated like loop so continue works
1193 pending_label_ = 0;
1194 EXPECT_TOKEN(TOK(do));
1195 // BODY
1197 EXPECT_TOKEN(TOK(while));
1198 End();
1199 // } // end c
1200 EXPECT_TOKEN('(');
1201 RECURSE(Expression(AsmType::Int()));
1202 // if (!CONDITION) break a;
1203 current_function_builder_->Emit(kExprI32Eqz);
1205 // continue b;
1207 EXPECT_TOKEN(')');
1208 // } // end b
1209 End();
1210 // } // end a
1211 End();
1212 SkipSemicolon();
1213}
1214
1215// 6.5.6 IterationStatement - for
1217 EXPECT_TOKEN(TOK(for));
1218 EXPECT_TOKEN('(');
1219 if (!Peek(';')) {
1220 AsmType* ret;
1221 RECURSE(ret = Expression(nullptr));
1222 if (!ret->IsA(AsmType::Void())) {
1223 current_function_builder_->Emit(kExprDrop);
1224 }
1225 }
1226 EXPECT_TOKEN(';');
1227 // a: block {
1229 // b: loop {
1230 Loop();
1231 // c: block { // but treated like loop so continue works
1234 pending_label_ = 0;
1235 if (!Peek(';')) {
1236 // if (!CONDITION) break a;
1237 RECURSE(Expression(AsmType::Int()));
1238 current_function_builder_->Emit(kExprI32Eqz);
1240 }
1241 EXPECT_TOKEN(';');
1242 // Race past INCREMENT
1243 size_t increment_position = scanner_.Position();
1245 EXPECT_TOKEN(')');
1246 // BODY
1248 // } // end c
1249 End();
1250 // INCREMENT
1251 size_t end_position = scanner_.Position();
1252 scanner_.Seek(increment_position);
1253 if (!Peek(')')) {
1254 RECURSE(Expression(nullptr));
1255 // NOTE: No explicit drop because below break is an implicit drop.
1256 }
1257 // continue b;
1259 scanner_.Seek(end_position);
1260 // } // end b
1261 End();
1262 // } // end a
1263 End();
1264}
1265
1266// 6.5.7 BreakStatement
1268 EXPECT_TOKEN(TOK(break));
1269 AsmJsScanner::token_t label_name = kTokenNone;
1270 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
1271 // NOTE: Currently using globals/locals for labels too.
1272 label_name = Consume();
1273 }
1274 int depth = FindBreakLabelDepth(label_name);
1275 if (depth < 0) {
1276 FAIL("Illegal break");
1277 }
1278 current_function_builder_->EmitWithU32V(kExprBr, depth);
1279 SkipSemicolon();
1280}
1281
1282// 6.5.8 ContinueStatement
1284 EXPECT_TOKEN(TOK(continue));
1285 AsmJsScanner::token_t label_name = kTokenNone;
1286 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
1287 // NOTE: Currently using globals/locals for labels too.
1288 label_name = Consume();
1289 }
1290 int depth = FindContinueLabelDepth(label_name);
1291 if (depth < 0) {
1292 FAIL("Illegal continue");
1293 }
1294 current_function_builder_->EmitWithU32V(kExprBr, depth);
1295 SkipSemicolon();
1296}
1297
1298// 6.5.9 LabelledStatement
1301 // NOTE: Currently using globals/locals for labels too.
1302 if (pending_label_ != 0) {
1303 FAIL("Double label unsupported");
1304 }
1306 scanner_.Next();
1307 EXPECT_TOKEN(':');
1309}
1310
1311// 6.5.10 SwitchStatement
1313 EXPECT_TOKEN(TOK(switch));
1314 EXPECT_TOKEN('(');
1315 AsmType* test;
1316 RECURSE(test = Expression(nullptr));
1317 if (!test->IsA(AsmType::Signed())) {
1318 FAIL("Expected signed for switch value");
1319 }
1320 EXPECT_TOKEN(')');
1321 uint32_t tmp = TempVariable(0);
1324 pending_label_ = 0;
1325 // TODO(bradnelson): Make less weird.
1327 GatherCases(&cases);
1328 EXPECT_TOKEN('{');
1329 size_t count = cases.size() + 1;
1330 for (size_t i = 0; i < count; ++i) {
1333 }
1334 int table_pos = 0;
1335 for (auto c : cases) {
1338 current_function_builder_->Emit(kExprI32Eq);
1339 current_function_builder_->EmitWithU32V(kExprBrIf, table_pos++);
1340 }
1341 current_function_builder_->EmitWithU32V(kExprBr, table_pos++);
1342 while (!failed_ && Peek(TOK(case))) {
1344 BareEnd();
1346 }
1348 BareEnd();
1349 if (Peek(TOK(default))) {
1351 }
1352 EXPECT_TOKEN('}');
1353 End();
1354}
1355
1356// 6.6. ValidateCase
1358 EXPECT_TOKEN(TOK(case));
1359 bool negate = false;
1360 if (Check('-')) {
1361 negate = true;
1362 }
1363 uint32_t uvalue;
1364 if (!CheckForUnsigned(&uvalue)) {
1365 FAIL("Expected numeric literal");
1366 }
1367 // TODO(bradnelson): Share negation plumbing.
1368 if ((negate && uvalue > 0x80000000) || (!negate && uvalue > 0x7FFFFFFF)) {
1369 FAIL("Numeric literal out of range");
1370 }
1371 int32_t value = static_cast<int32_t>(uvalue);
1372 DCHECK_IMPLIES(negate && uvalue == 0x80000000, value == kMinInt);
1373 if (negate && value != kMinInt) {
1374 value = -value;
1375 }
1376 EXPECT_TOKEN(':');
1377 while (!failed_ && !Peek('}') && !Peek(TOK(case)) && !Peek(TOK(default))) {
1379 }
1380}
1381
1382// 6.7 ValidateDefault
1384 EXPECT_TOKEN(TOK(default));
1385 EXPECT_TOKEN(':');
1386 while (!failed_ && !Peek('}')) {
1388 }
1389}
1390
1391// 6.8 ValidateExpression
1393 AsmType* ret;
1394 RECURSEn(ret = Expression(nullptr));
1395 return ret;
1396}
1397
1398// 6.8.1 Expression
1400 AsmType* a;
1401 for (;;) {
1403 if (Peek(',')) {
1404 if (a->IsA(AsmType::None())) {
1405 FAILn("Expected actual type");
1406 }
1407 if (!a->IsA(AsmType::Void())) {
1408 current_function_builder_->Emit(kExprDrop);
1409 }
1410 EXPECT_TOKENn(',');
1411 continue;
1412 }
1413 break;
1414 }
1415 if (expected != nullptr && !a->IsA(expected)) {
1416 FAILn("Unexpected type");
1417 }
1418 return a;
1419}
1420
1421// 6.8.2 NumericLiteral
1423 call_coercion_ = nullptr;
1424 double dvalue = 0.0;
1425 uint32_t uvalue = 0;
1426 if (CheckForDouble(&dvalue)) {
1428 return AsmType::Double();
1429 } else if (CheckForUnsigned(&uvalue)) {
1430 if (uvalue <= 0x7FFFFFFF) {
1431 current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
1432 return AsmType::FixNum();
1433 } else {
1434 current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
1435 return AsmType::Unsigned();
1436 }
1437 } else {
1438 FAILn("Expected numeric literal.");
1439 }
1440}
1441
1442// 6.8.3 Identifier
1444 call_coercion_ = nullptr;
1445 if (scanner_.IsLocal()) {
1446 VarInfo* info = GetVarInfo(Consume());
1447 if (info->kind != VarKind::kLocal) {
1448 FAILn("Undefined local variable");
1449 }
1451 return info->type;
1452 } else if (scanner_.IsGlobal()) {
1453 VarInfo* info = GetVarInfo(Consume());
1454 if (info->kind != VarKind::kGlobal) {
1455 FAILn("Undefined global variable");
1456 }
1457 current_function_builder_->EmitWithU32V(kExprGlobalGet, VarIndex(info));
1458 return info->type;
1459 }
1460 UNREACHABLE();
1461}
1462
1463// 6.8.4 CallExpression
1465 AsmType* ret;
1466 if (scanner_.IsGlobal() &&
1469 return AsmType::Float();
1470 } else if (scanner_.IsGlobal() &&
1471 GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
1472 RECURSEn(ret = MemberExpression());
1473 } else if (Peek('(')) {
1475 } else if (PeekCall()) {
1476 RECURSEn(ret = ValidateCall());
1477 } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
1478 RECURSEn(ret = Identifier());
1479 } else {
1480 RECURSEn(ret = NumericLiteral());
1481 }
1482 return ret;
1483}
1484
1485// 6.8.5 MemberExpression
1487 call_coercion_ = nullptr;
1490 if (Peek('=')) {
1492 return heap_access_type_->StoreType();
1493 } else {
1494#define V(array_type, wasmload, wasmstore, type) \
1495 if (heap_access_type_->IsA(AsmType::array_type())) { \
1496 current_function_builder_->EmitWithPrefix( \
1497 kExpr##type##AsmjsLoad##wasmload); \
1498 return heap_access_type_->LoadType(); \
1499 }
1501#undef V
1502 FAILn("Expected valid heap load");
1503 }
1504}
1505
1506// 6.8.6 AssignmentExpression
1508 AsmType* ret;
1509 if (scanner_.IsGlobal() &&
1510 GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
1512 if (Peek('=')) {
1514 FAILn("Invalid assignment target");
1515 }
1518 AsmType* heap_type = heap_access_type_;
1519 EXPECT_TOKENn('=');
1520 AsmType* value;
1521 RECURSEn(value = AssignmentExpression());
1522 if (!value->IsA(ret)) {
1523 FAILn("Illegal type stored to heap view");
1524 }
1525 ret = value;
1526 if (heap_type->IsA(AsmType::Float32Array()) &&
1527 value->IsA(AsmType::DoubleQ())) {
1528 // Assignment to a float32 heap can be used to convert doubles.
1529 current_function_builder_->Emit(kExprF32ConvertF64);
1530 ret = AsmType::FloatQ();
1531 }
1532 if (heap_type->IsA(AsmType::Float64Array()) &&
1533 value->IsA(AsmType::FloatQ())) {
1534 // Assignment to a float64 heap can be used to convert floats.
1535 current_function_builder_->Emit(kExprF64ConvertF32);
1536 ret = AsmType::DoubleQ();
1537 }
1538#define V(array_type, wasmload, wasmstore, type) \
1539 if (heap_type->IsA(AsmType::array_type())) { \
1540 current_function_builder_->EmitWithPrefix( \
1541 kExpr##type##AsmjsStore##wasmstore); \
1542 return ret; \
1543 }
1545#undef V
1546 }
1547 } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
1548 bool is_local = scanner_.IsLocal();
1549 VarInfo* info = GetVarInfo(scanner_.Token());
1550 USE(is_local);
1551 ret = info->type;
1552 scanner_.Next();
1553 if (Check('=')) {
1554 // NOTE: Before this point, this might have been VarKind::kUnused even in
1555 // valid code, as it might be a label.
1556 if (info->kind == VarKind::kUnused) {
1557 FAILn("Undeclared assignment target");
1558 }
1559 if (!info->mutable_variable) {
1560 FAILn("Expected mutable variable in assignment");
1561 }
1562 DCHECK(is_local ? info->kind == VarKind::kLocal
1563 : info->kind == VarKind::kGlobal);
1564 AsmType* value;
1565 RECURSEn(value = AssignmentExpression());
1566 if (!value->IsA(ret)) {
1567 FAILn("Type mismatch in assignment");
1568 }
1569 if (info->kind == VarKind::kLocal) {
1571 } else if (info->kind == VarKind::kGlobal) {
1572 current_function_builder_->EmitWithU32V(kExprGlobalSet, VarIndex(info));
1573 current_function_builder_->EmitWithU32V(kExprGlobalGet, VarIndex(info));
1574 } else {
1575 UNREACHABLE();
1576 }
1577 return ret;
1578 }
1579 scanner_.Rewind();
1581 } else {
1583 }
1584 return ret;
1585}
1586
1587// 6.8.7 UnaryExpression
1589 AsmType* ret;
1590 if (Check('-')) {
1591 uint32_t uvalue;
1592 if (CheckForUnsigned(&uvalue)) {
1593 if (uvalue == 0) {
1595 ret = AsmType::Double();
1596 } else if (uvalue <= 0x80000000) {
1597 // TODO(bradnelson): was supposed to be 0x7FFFFFFF, check errata.
1599 base::NegateWithWraparound(static_cast<int32_t>(uvalue)));
1600 ret = AsmType::Signed();
1601 } else {
1602 FAILn("Integer numeric literal out of range.");
1603 }
1604 } else {
1605 RECURSEn(ret = UnaryExpression());
1606 if (ret->IsA(AsmType::Int())) {
1611 current_function_builder_->Emit(kExprI32Sub);
1612 ret = AsmType::Intish();
1613 } else if (ret->IsA(AsmType::DoubleQ())) {
1614 current_function_builder_->Emit(kExprF64Neg);
1615 ret = AsmType::Double();
1616 } else if (ret->IsA(AsmType::FloatQ())) {
1617 current_function_builder_->Emit(kExprF32Neg);
1618 ret = AsmType::Floatish();
1619 } else {
1620 FAILn("expected int/double?/float?");
1621 }
1622 }
1623 } else if (Peek('+')) {
1624 call_coercion_ = AsmType::Double();
1626 scanner_.Next(); // Done late for correct position.
1627 RECURSEn(ret = UnaryExpression());
1628 // TODO(bradnelson): Generalize.
1629 if (ret->IsA(AsmType::Signed())) {
1630 current_function_builder_->Emit(kExprF64SConvertI32);
1631 ret = AsmType::Double();
1632 } else if (ret->IsA(AsmType::Unsigned())) {
1633 current_function_builder_->Emit(kExprF64UConvertI32);
1634 ret = AsmType::Double();
1635 } else if (ret->IsA(AsmType::DoubleQ())) {
1636 ret = AsmType::Double();
1637 } else if (ret->IsA(AsmType::FloatQ())) {
1638 current_function_builder_->Emit(kExprF64ConvertF32);
1639 ret = AsmType::Double();
1640 } else {
1641 FAILn("expected signed/unsigned/double?/float?");
1642 }
1643 } else if (Check('!')) {
1644 RECURSEn(ret = UnaryExpression());
1645 if (!ret->IsA(AsmType::Int())) {
1646 FAILn("expected int");
1647 }
1648 current_function_builder_->Emit(kExprI32Eqz);
1649 } else if (Check('~')) {
1650 if (Check('~')) {
1651 RECURSEn(ret = UnaryExpression());
1652 if (ret->IsA(AsmType::Double())) {
1653 current_function_builder_->EmitWithPrefix(kExprI32AsmjsSConvertF64);
1654 } else if (ret->IsA(AsmType::FloatQ())) {
1655 current_function_builder_->EmitWithPrefix(kExprI32AsmjsSConvertF32);
1656 } else {
1657 FAILn("expected double or float?");
1658 }
1659 ret = AsmType::Signed();
1660 } else {
1661 RECURSEn(ret = UnaryExpression());
1662 if (!ret->IsA(AsmType::Intish())) {
1663 FAILn("operator ~ expects intish");
1664 }
1666 current_function_builder_->Emit(kExprI32Xor);
1667 ret = AsmType::Signed();
1668 }
1669 } else {
1670 RECURSEn(ret = CallExpression());
1671 }
1672 return ret;
1673}
1674
1675// 6.8.8 MultiplicativeExpression
1677 AsmType* a;
1678 uint32_t uvalue;
1679 if (CheckForUnsignedBelow(0x100000, &uvalue)) {
1680 if (Check('*')) {
1681 AsmType* type;
1682 RECURSEn(type = UnaryExpression());
1683 if (!type->IsA(AsmType::Int())) {
1684 FAILn("Expected int");
1685 }
1686 int32_t value = static_cast<int32_t>(uvalue);
1688 current_function_builder_->Emit(kExprI32Mul);
1689 return AsmType::Intish();
1690 } else {
1691 scanner_.Rewind();
1693 }
1694 } else if (Check('-')) {
1695 if (!PeekForZero() && CheckForUnsignedBelow(0x100000, &uvalue)) {
1696 int32_t value = -static_cast<int32_t>(uvalue);
1698 if (Check('*')) {
1699 AsmType* type;
1700 RECURSEn(type = UnaryExpression());
1701 if (!type->IsA(AsmType::Int())) {
1702 FAILn("Expected int");
1703 }
1704 current_function_builder_->Emit(kExprI32Mul);
1705 return AsmType::Intish();
1706 }
1707 a = AsmType::Signed();
1708 } else {
1709 scanner_.Rewind();
1711 }
1712 } else {
1714 }
1715 for (;;) {
1716 if (Check('*')) {
1717 if (Check('-')) {
1718 if (!PeekForZero() && CheckForUnsigned(&uvalue)) {
1719 if (uvalue >= 0x100000) {
1720 FAILn("Constant multiple out of range");
1721 }
1722 if (!a->IsA(AsmType::Int())) {
1723 FAILn("Integer multiply of expects int");
1724 }
1725 int32_t value = -static_cast<int32_t>(uvalue);
1727 current_function_builder_->Emit(kExprI32Mul);
1728 return AsmType::Intish();
1729 }
1730 scanner_.Rewind();
1731 } else if (CheckForUnsigned(&uvalue)) {
1732 if (uvalue >= 0x100000) {
1733 FAILn("Constant multiple out of range");
1734 }
1735 if (!a->IsA(AsmType::Int())) {
1736 FAILn("Integer multiply of expects int");
1737 }
1738 int32_t value = static_cast<int32_t>(uvalue);
1740 current_function_builder_->Emit(kExprI32Mul);
1741 return AsmType::Intish();
1742 }
1743 AsmType* b;
1745 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1746 current_function_builder_->Emit(kExprF64Mul);
1747 a = AsmType::Double();
1748 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1749 current_function_builder_->Emit(kExprF32Mul);
1750 a = AsmType::Floatish();
1751 } else {
1752 FAILn("expected doubles or floats");
1753 }
1754 } else if (Check('/')) {
1755 AsmType* b;
1757 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1758 current_function_builder_->Emit(kExprF64Div);
1759 a = AsmType::Double();
1760 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1761 current_function_builder_->Emit(kExprF32Div);
1762 a = AsmType::Floatish();
1763 } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
1764 current_function_builder_->EmitWithPrefix(kExprI32AsmjsDivS);
1765 a = AsmType::Intish();
1766 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
1767 current_function_builder_->EmitWithPrefix(kExprI32AsmjsDivU);
1768 a = AsmType::Intish();
1769 } else {
1770 FAILn("expected doubles or floats");
1771 }
1772 } else if (Check('%')) {
1773 AsmType* b;
1775 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1777 a = AsmType::Double();
1778 } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
1779 current_function_builder_->EmitWithPrefix(kExprI32AsmjsRemS);
1780 a = AsmType::Intish();
1781 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
1782 current_function_builder_->EmitWithPrefix(kExprI32AsmjsRemU);
1783 a = AsmType::Intish();
1784 } else {
1785 FAILn("expected doubles or floats");
1786 }
1787 } else {
1788 break;
1789 }
1790 }
1791 return a;
1792}
1793
1794// 6.8.9 AdditiveExpression
1796 AsmType* a;
1798 int n = 0;
1799 for (;;) {
1800 if (Check('+')) {
1801 AsmType* b;
1803 if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
1804 current_function_builder_->Emit(kExprF64Add);
1805 a = AsmType::Double();
1806 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1807 current_function_builder_->Emit(kExprF32Add);
1808 a = AsmType::Floatish();
1809 } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
1810 current_function_builder_->Emit(kExprI32Add);
1811 a = AsmType::Intish();
1812 n = 2;
1813 } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1814 // TODO(bradnelson): b should really only be Int.
1815 // specialize intish to capture count.
1816 ++n;
1817 if (n > (1 << 20)) {
1818 FAILn("more than 2^20 additive values");
1819 }
1820 current_function_builder_->Emit(kExprI32Add);
1821 } else {
1822 FAILn("illegal types for +");
1823 }
1824 } else if (Check('-')) {
1825 AsmType* b;
1827 if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
1828 current_function_builder_->Emit(kExprF64Sub);
1829 a = AsmType::Double();
1830 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1831 current_function_builder_->Emit(kExprF32Sub);
1832 a = AsmType::Floatish();
1833 } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
1834 current_function_builder_->Emit(kExprI32Sub);
1835 a = AsmType::Intish();
1836 n = 2;
1837 } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1838 // TODO(bradnelson): b should really only be Int.
1839 // specialize intish to capture count.
1840 ++n;
1841 if (n > (1 << 20)) {
1842 FAILn("more than 2^20 additive values");
1843 }
1844 current_function_builder_->Emit(kExprI32Sub);
1845 } else {
1846 FAILn("illegal types for +");
1847 }
1848 } else {
1849 break;
1850 }
1851 }
1852 return a;
1853}
1854
1855// 6.8.10 ShiftExpression
1857 AsmType* a = nullptr;
1860 // TODO(bradnelson): Implement backtracking to avoid emitting code
1861 // for the x >>> 0 case (similar to what's there for |0).
1862 for (;;) {
1863 switch (scanner_.Token()) {
1864 case TOK(SAR): {
1865 EXPECT_TOKENn(TOK(SAR));
1867 // Remember position allowing this shift-expression to be used as part
1868 // of a heap access operation expecting `a >> n:NumericLiteral`.
1869 bool imm = false;
1870 size_t old_pos;
1871 size_t old_code;
1872 uint32_t shift_imm;
1873 if (a->IsA(AsmType::Intish()) && CheckForUnsigned(&shift_imm)) {
1874 old_pos = scanner_.Position();
1876 scanner_.Rewind();
1877 imm = true;
1878 }
1879 AsmType* b = nullptr;
1881 // Check for `a >> n:NumericLiteral` pattern.
1882 if (imm && old_pos == scanner_.Position()) {
1883 heap_access_shift_position_ = old_code;
1884 heap_access_shift_value_ = shift_imm;
1885 }
1886 if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) {
1887 FAILn("Expected intish for operator >>.");
1888 }
1889 current_function_builder_->Emit(kExprI32ShrS);
1890 a = AsmType::Signed();
1891 continue;
1892 }
1893#define HANDLE_CASE(op, opcode, name, result) \
1894 case TOK(op): { \
1895 EXPECT_TOKENn(TOK(op)); \
1896 heap_access_shift_position_ = kNoHeapAccessShift; \
1897 AsmType* b = nullptr; \
1898 RECURSEn(b = AdditiveExpression()); \
1899 if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
1900 FAILn("Expected intish for operator " #name "."); \
1901 } \
1902 current_function_builder_->Emit(kExpr##opcode); \
1903 a = AsmType::result(); \
1904 continue; \
1905 }
1906 HANDLE_CASE(SHL, I32Shl, "<<", Signed);
1907 HANDLE_CASE(SHR, I32ShrU, ">>>", Unsigned);
1908#undef HANDLE_CASE
1909 default:
1910 return a;
1911 }
1912 }
1913}
1914
1915// 6.8.11 RelationalExpression
1917 AsmType* a = nullptr;
1919 for (;;) {
1920 switch (scanner_.Token()) {
1921#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
1922 case op: { \
1923 EXPECT_TOKENn(op); \
1924 AsmType* b = nullptr; \
1925 RECURSEn(b = ShiftExpression()); \
1926 if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
1927 current_function_builder_->Emit(kExpr##sop); \
1928 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
1929 current_function_builder_->Emit(kExpr##uop); \
1930 } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
1931 current_function_builder_->Emit(kExpr##dop); \
1932 } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
1933 current_function_builder_->Emit(kExpr##fop); \
1934 } else { \
1935 FAILn("Expected signed, unsigned, double, or float for operator " #name \
1936 "."); \
1937 } \
1938 a = AsmType::Int(); \
1939 continue; \
1940 }
1941 HANDLE_CASE('<', I32LtS, I32LtU, F64Lt, F32Lt, "<");
1942 HANDLE_CASE(TOK(LE), I32LeS, I32LeU, F64Le, F32Le, "<=");
1943 HANDLE_CASE('>', I32GtS, I32GtU, F64Gt, F32Gt, ">");
1944 HANDLE_CASE(TOK(GE), I32GeS, I32GeU, F64Ge, F32Ge, ">=");
1945#undef HANDLE_CASE
1946 default:
1947 return a;
1948 }
1949 }
1950}
1951
1952// 6.8.12 EqualityExpression
1954 AsmType* a = nullptr;
1956 for (;;) {
1957 switch (scanner_.Token()) {
1958#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
1959 case op: { \
1960 EXPECT_TOKENn(op); \
1961 AsmType* b = nullptr; \
1962 RECURSEn(b = RelationalExpression()); \
1963 if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
1964 current_function_builder_->Emit(kExpr##sop); \
1965 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
1966 current_function_builder_->Emit(kExpr##uop); \
1967 } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
1968 current_function_builder_->Emit(kExpr##dop); \
1969 } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
1970 current_function_builder_->Emit(kExpr##fop); \
1971 } else { \
1972 FAILn("Expected signed, unsigned, double, or float for operator " #name \
1973 "."); \
1974 } \
1975 a = AsmType::Int(); \
1976 continue; \
1977 }
1978 HANDLE_CASE(TOK(EQ), I32Eq, I32Eq, F64Eq, F32Eq, "==");
1979 HANDLE_CASE(TOK(NE), I32Ne, I32Ne, F64Ne, F32Ne, "!=");
1980#undef HANDLE_CASE
1981 default:
1982 return a;
1983 }
1984 }
1985}
1986
1987// 6.8.13 BitwiseANDExpression
1989 AsmType* a = nullptr;
1991 while (Check('&')) {
1992 AsmType* b = nullptr;
1994 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1995 current_function_builder_->Emit(kExprI32And);
1996 a = AsmType::Signed();
1997 } else {
1998 FAILn("Expected intish for operator &.");
1999 }
2000 }
2001 return a;
2002}
2003
2004// 6.8.14 BitwiseXORExpression
2006 AsmType* a = nullptr;
2008 while (Check('^')) {
2009 AsmType* b = nullptr;
2011 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
2012 current_function_builder_->Emit(kExprI32Xor);
2013 a = AsmType::Signed();
2014 } else {
2015 FAILn("Expected intish for operator &.");
2016 }
2017 }
2018 return a;
2019}
2020
2021// 6.8.15 BitwiseORExpression
2023 AsmType* a = nullptr;
2026 while (Check('|')) {
2027 AsmType* b = nullptr;
2028 // Remember whether the first operand to this OR-expression has requested
2029 // deferred validation of the |0 annotation.
2030 // NOTE: This has to happen here to work recursively.
2031 bool requires_zero =
2032 AsmType::IsExactly(call_coercion_deferred_, AsmType::Signed());
2033 call_coercion_deferred_ = nullptr;
2034 // TODO(bradnelson): Make it prettier.
2035 bool zero = false;
2036 size_t old_pos;
2037 size_t old_code;
2038 if (a->IsA(AsmType::Intish()) && CheckForZero()) {
2039 old_pos = scanner_.Position();
2041 scanner_.Rewind();
2042 zero = true;
2043 }
2045 // Handle |0 specially.
2046 if (zero && old_pos == scanner_.Position()) {
2048 a = AsmType::Signed();
2049 continue;
2050 }
2051 // Anything not matching |0 breaks the lookahead in {ValidateCall}.
2052 if (requires_zero) {
2053 FAILn("Expected |0 type annotation for call");
2054 }
2055 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
2056 current_function_builder_->Emit(kExprI32Ior);
2057 a = AsmType::Signed();
2058 } else {
2059 FAILn("Expected intish for operator |.");
2060 }
2061 }
2063 return a;
2064}
2065
2066// 6.8.16 ConditionalExpression
2068 AsmType* test = nullptr;
2069 RECURSEn(test = BitwiseORExpression());
2070 if (Check('?')) {
2071 if (!test->IsA(AsmType::Int())) {
2072 FAILn("Expected int in condition of ternary operator.");
2073 }
2075 size_t fixup = current_function_builder_->GetPosition() -
2076 1; // Assumes encoding knowledge.
2077 AsmType* cons = nullptr;
2079 current_function_builder_->Emit(kExprElse);
2080 EXPECT_TOKENn(':');
2081 AsmType* alt = nullptr;
2084 if (cons->IsA(AsmType::Int()) && alt->IsA(AsmType::Int())) {
2086 return AsmType::Int();
2087 } else if (cons->IsA(AsmType::Double()) && alt->IsA(AsmType::Double())) {
2089 return AsmType::Double();
2090 } else if (cons->IsA(AsmType::Float()) && alt->IsA(AsmType::Float())) {
2092 return AsmType::Float();
2093 } else {
2094 FAILn("Type mismatch in ternary operator.");
2095 }
2096 } else {
2097 return test;
2098 }
2099}
2100
2101// 6.8.17 ParenthesiedExpression
2103 call_coercion_ = nullptr;
2104 AsmType* ret;
2105 EXPECT_TOKENn('(');
2106 RECURSEn(ret = Expression(nullptr));
2107 EXPECT_TOKENn(')');
2108 return ret;
2109}
2110
2111// 6.9 ValidateCall
2113 AsmType* return_type = call_coercion_;
2114 call_coercion_ = nullptr;
2115 size_t call_pos = scanner_.Position();
2116 size_t to_number_pos = call_coercion_position_;
2117 bool allow_peek = (call_coercion_deferred_position_ == scanner_.Position());
2118 AsmJsScanner::token_t function_name = Consume();
2119
2120 // Distinguish between ordinary function calls and function table calls. In
2121 // both cases we might be seeing the {function_name} for the first time and
2122 // hence allocate a {VarInfo} here, all subsequent uses of the same name then
2123 // need to match the information stored at this point.
2124 std::optional<TemporaryVariableScope> tmp_scope;
2125 if (Check('[')) {
2126 AsmType* index = nullptr;
2127 RECURSEn(index = EqualityExpression());
2128 if (!index->IsA(AsmType::Intish())) {
2129 FAILn("Expected intish index");
2130 }
2131 EXPECT_TOKENn('&');
2132 uint32_t mask = 0;
2133 if (!CheckForUnsigned(&mask)) {
2134 FAILn("Expected mask literal");
2135 }
2136 if (!base::bits::IsPowerOfTwo(mask + 1)) {
2137 FAILn("Expected power of 2 mask");
2138 }
2140 current_function_builder_->Emit(kExprI32And);
2141 EXPECT_TOKENn(']');
2142 VarInfo* function_info = GetVarInfo(function_name);
2143 if (function_info->kind == VarKind::kUnused) {
2144 if (module_builder_->NumTables() == 0) {
2146 }
2147 uint32_t func_index = module_builder_->IncreaseTableMinSize(0, mask + 1);
2148 if (func_index == std::numeric_limits<uint32_t>::max()) {
2149 FAILn("Exceeded maximum function table size");
2150 }
2151 function_info->kind = VarKind::kTable;
2152 function_info->mask = mask;
2153 function_info->index = func_index;
2154 function_info->mutable_variable = false;
2155 } else {
2156 if (function_info->kind != VarKind::kTable) {
2157 FAILn("Expected call table");
2158 }
2159 if (function_info->mask != mask) {
2160 FAILn("Mask size mismatch");
2161 }
2162 }
2164 current_function_builder_->Emit(kExprI32Add);
2165 // We have to use a temporary for the correct order of evaluation.
2166 tmp_scope.emplace(this);
2167 current_function_builder_->EmitSetLocal(tmp_scope->get());
2168 // The position of function table calls is after the table lookup.
2169 call_pos = scanner_.Position();
2170 } else {
2171 VarInfo* function_info = GetVarInfo(function_name);
2172 if (function_info->kind == VarKind::kUnused) {
2173 function_info->kind = VarKind::kFunction;
2174 function_info->function_builder = module_builder_->AddFunction();
2175 function_info->index = function_info->function_builder->func_index();
2176 function_info->mutable_variable = false;
2177 } else {
2178 if (function_info->kind != VarKind::kFunction &&
2179 function_info->kind < VarKind::kImportedFunction) {
2180 FAILn("Expected function as call target");
2181 }
2182 }
2183 }
2184
2185 // Parse argument list and gather types.
2188 EXPECT_TOKENn('(');
2189 while (!failed_ && !Peek(')')) {
2190 AsmType* t;
2192 param_specific_types.push_back(t);
2193 if (t->IsA(AsmType::Int())) {
2194 param_types.push_back(AsmType::Int());
2195 } else if (t->IsA(AsmType::Float())) {
2196 param_types.push_back(AsmType::Float());
2197 } else if (t->IsA(AsmType::Double())) {
2198 param_types.push_back(AsmType::Double());
2199 } else {
2200 FAILn("Bad function argument type");
2201 }
2202 if (!Peek(')')) {
2203 EXPECT_TOKENn(',');
2204 }
2205 }
2206 EXPECT_TOKENn(')');
2207
2208 // Reload {VarInfo} after parsing arguments as table might have grown.
2209 VarInfo* function_info = GetVarInfo(function_name);
2210
2211 // We potentially use lookahead in order to determine the return type in case
2212 // it is not yet clear from the call context. Special care has to be taken to
2213 // ensure the non-contextual lookahead is valid. The following restrictions
2214 // substantiate the validity of the lookahead implemented below:
2215 // - All calls (except stdlib calls) require some sort of type annotation.
2216 // - The coercion to "signed" is part of the {BitwiseORExpression}, any
2217 // intermittent expressions like parenthesis in `(callsite(..))|0` are
2218 // syntactically not considered coercions.
2219 // - The coercion to "double" as part of the {UnaryExpression} has higher
2220 // precedence and wins in `+callsite(..)|0` cases. Only "float" return
2221 // types are overridden in `fround(callsite(..)|0)` expressions.
2222 // - Expected coercions to "signed" are flagged via {call_coercion_deferred}
2223 // and later on validated as part of {BitwiseORExpression} to ensure they
2224 // indeed apply to the current call expression.
2225 // - The deferred validation is only allowed if {BitwiseORExpression} did
2226 // promise to fulfill the request via {call_coercion_deferred_position}.
2227 if (allow_peek && Peek('|') &&
2228 function_info->kind <= VarKind::kImportedFunction &&
2229 (return_type == nullptr || return_type->IsA(AsmType::Float()))) {
2231 call_coercion_deferred_ = AsmType::Signed();
2232 to_number_pos = scanner_.Position();
2233 return_type = AsmType::Signed();
2234 } else if (return_type == nullptr) {
2235 to_number_pos = call_pos; // No conversion.
2236 return_type = AsmType::Void();
2237 }
2238
2239 // Compute function type and signature based on gathered types.
2240 AsmType* function_type = AsmType::Function(zone(), return_type);
2241 for (auto t : param_types) {
2242 function_type->AsFunctionType()->AddArgument(t);
2243 }
2244 FunctionSig* sig = ConvertSignature(return_type, param_types);
2245 ModuleTypeIndex signature_index = module_builder_->AddSignature(sig, true);
2246
2247 // Emit actual function invocation depending on the kind. At this point we
2248 // also determined the complete function type and can perform checking against
2249 // the expected type or update the expected type in case of first occurrence.
2250 if (function_info->kind == VarKind::kImportedFunction) {
2251 if (param_types.size() > kV8MaxWasmFunctionParams) {
2252 FAILn("Number of parameters exceeds internal limit");
2253 }
2254 for (auto t : param_specific_types) {
2255 if (!t->IsA(AsmType::Extern())) {
2256 FAILn("Imported function args must be type extern");
2257 }
2258 }
2259 if (return_type->IsA(AsmType::Float())) {
2260 FAILn("Imported function can't be called as float");
2261 }
2262 DCHECK_NOT_NULL(function_info->import);
2263 // TODO(bradnelson): Factor out.
2264 uint32_t index;
2265 auto it = function_info->import->cache.find(*sig);
2266 if (it != function_info->import->cache.end()) {
2267 index = it->second;
2268 DCHECK(function_info->function_defined);
2269 } else {
2270 index =
2271 module_builder_->AddImport(function_info->import->function_name, sig);
2272 function_info->import->cache[*sig] = index;
2273 function_info->function_defined = true;
2274 }
2275 current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
2276 current_function_builder_->EmitWithU32V(kExprCallFunction, index);
2277 } else if (function_info->kind > VarKind::kImportedFunction) {
2278 AsmCallableType* callable = function_info->type->AsCallableType();
2279 if (!callable) {
2280 FAILn("Expected callable function");
2281 }
2282 // TODO(bradnelson): Refactor AsmType to not need this.
2283 if (callable->CanBeInvokedWith(return_type, param_specific_types)) {
2284 // Return type ok.
2285 } else if (callable->CanBeInvokedWith(AsmType::Float(),
2286 param_specific_types)) {
2287 return_type = AsmType::Float();
2288 } else if (callable->CanBeInvokedWith(AsmType::Floatish(),
2289 param_specific_types)) {
2290 return_type = AsmType::Floatish();
2291 } else if (callable->CanBeInvokedWith(AsmType::Double(),
2292 param_specific_types)) {
2293 return_type = AsmType::Double();
2294 } else if (callable->CanBeInvokedWith(AsmType::Signed(),
2295 param_specific_types)) {
2296 return_type = AsmType::Signed();
2297 } else if (callable->CanBeInvokedWith(AsmType::Unsigned(),
2298 param_specific_types)) {
2299 return_type = AsmType::Unsigned();
2300 } else {
2301 FAILn("Function use doesn't match definition");
2302 }
2303 switch (function_info->kind) {
2304#define V(name, Name, op, sig) \
2305 case VarKind::kMath##Name: \
2306 if ((op) <= 0xff) { \
2307 current_function_builder_->Emit(op); \
2308 } else { \
2309 current_function_builder_->EmitWithPrefix(op); \
2310 } \
2311 break;
2313#undef V
2314#define V(name, Name, op, sig) \
2315 case VarKind::kMath##Name: \
2316 if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { \
2317 current_function_builder_->Emit(kExprF64##Name); \
2318 } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { \
2319 current_function_builder_->Emit(kExprF32##Name); \
2320 } else { \
2321 UNREACHABLE(); \
2322 } \
2323 break;
2325#undef V
2326 case VarKind::kMathMin:
2327 case VarKind::kMathMax:
2328 if (param_specific_types[0]->IsA(AsmType::Double())) {
2329 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2330 if (function_info->kind == VarKind::kMathMin) {
2331 current_function_builder_->Emit(kExprF64Min);
2332 } else {
2333 current_function_builder_->Emit(kExprF64Max);
2334 }
2335 }
2336 } else if (param_specific_types[0]->IsA(AsmType::Float())) {
2337 // NOTE: Not technically part of the asm.js spec, but Firefox
2338 // accepts it.
2339 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2340 if (function_info->kind == VarKind::kMathMin) {
2341 current_function_builder_->Emit(kExprF32Min);
2342 } else {
2343 current_function_builder_->Emit(kExprF32Max);
2344 }
2345 }
2346 } else if (param_specific_types[0]->IsA(AsmType::Signed())) {
2347 TemporaryVariableScope tmp_x(this);
2348 TemporaryVariableScope tmp_y(this);
2349 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2353 if (function_info->kind == VarKind::kMathMin) {
2354 current_function_builder_->Emit(kExprI32GeS);
2355 } else {
2356 current_function_builder_->Emit(kExprI32LeS);
2357 }
2360 current_function_builder_->Emit(kExprElse);
2363 }
2364 } else {
2365 UNREACHABLE();
2366 }
2367 break;
2368
2369 case VarKind::kMathAbs:
2370 if (param_specific_types[0]->IsA(AsmType::Signed())) {
2375 current_function_builder_->Emit(kExprI32ShrS);
2377 current_function_builder_->Emit(kExprI32Xor);
2379 current_function_builder_->Emit(kExprI32Sub);
2380 } else if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
2381 current_function_builder_->Emit(kExprF64Abs);
2382 } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) {
2383 current_function_builder_->Emit(kExprF32Abs);
2384 } else {
2385 UNREACHABLE();
2386 }
2387 break;
2388
2389 case VarKind::kMathFround:
2390 // NOTE: Handled in {AsmJsParser::CallExpression} specially and treated
2391 // as a coercion to "float" type. Cannot be reached as a call here.
2392 UNREACHABLE();
2393
2394 default:
2395 UNREACHABLE();
2396 }
2397 } else {
2398 DCHECK(function_info->kind == VarKind::kFunction ||
2399 function_info->kind == VarKind::kTable);
2400 if (function_info->type->IsA(AsmType::None())) {
2401 function_info->type = function_type;
2402 } else {
2403 AsmCallableType* callable = function_info->type->AsCallableType();
2404 if (!callable ||
2405 !callable->CanBeInvokedWith(return_type, param_specific_types)) {
2406 FAILn("Function use doesn't match definition");
2407 }
2408 }
2409 if (function_info->kind == VarKind::kTable) {
2410 current_function_builder_->EmitGetLocal(tmp_scope->get());
2411 current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
2412 current_function_builder_->Emit(kExprCallIndirect);
2413 current_function_builder_->EmitU32V(signature_index);
2414 current_function_builder_->EmitU32V(0); // table index
2415 } else {
2416 current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
2417 current_function_builder_->Emit(kExprCallFunction);
2419 }
2420 }
2421
2422 return return_type;
2423}
2424
2425// 6.9 ValidateCall - helper
2427 if (!scanner_.IsGlobal()) {
2428 return false;
2429 }
2431 return true;
2432 }
2434 return true;
2435 }
2438 scanner_.Next();
2439 if (Peek('(') || Peek('[')) {
2440 scanner_.Rewind();
2441 return true;
2442 }
2443 scanner_.Rewind();
2444 }
2445 return false;
2446}
2447
2448// 6.10 ValidateHeapAccess
2450 VarInfo* info = GetVarInfo(Consume());
2451 int32_t size = info->type->ElementSizeInBytes();
2452 EXPECT_TOKEN('[');
2453 uint32_t offset;
2454 if (CheckForUnsigned(&offset)) {
2455 // TODO(bradnelson): Check more things.
2456 // TODO(asmjs): Clarify and explain where this limit is coming from,
2457 // as it is not mandated by the spec directly.
2458 if (offset > 0x7FFFFFFF ||
2459 static_cast<uint64_t>(offset) * static_cast<uint64_t>(size) >
2460 0x7FFFFFFF) {
2461 FAIL("Heap access out of range");
2462 }
2463 if (Check(']')) {
2465 static_cast<uint32_t>(offset * size));
2466 // NOTE: This has to happen here to work recursively.
2467 heap_access_type_ = info->type;
2468 return;
2469 } else {
2470 scanner_.Rewind();
2471 }
2472 }
2473 AsmType* index_type;
2474 if (info->type->IsA(AsmType::Int8Array()) ||
2475 info->type->IsA(AsmType::Uint8Array())) {
2476 RECURSE(index_type = Expression(nullptr));
2477 } else {
2478 RECURSE(index_type = ShiftExpression());
2480 FAIL("Expected shift of word size");
2481 }
2482 if (heap_access_shift_value_ > 3) {
2483 FAIL("Expected valid heap access shift");
2484 }
2485 if ((1 << heap_access_shift_value_) != size) {
2486 FAIL("Expected heap access shift to match heap view");
2487 }
2488 // Delete the code of the actual shift operation.
2490 // Mask bottom bits to match asm.js behavior.
2492 current_function_builder_->Emit(kExprI32And);
2493 }
2494 if (!index_type->IsA(AsmType::Intish())) {
2495 FAIL("Expected intish index");
2496 }
2497 EXPECT_TOKEN(']');
2498 // NOTE: This has to happen here to work recursively.
2499 heap_access_type_ = info->type;
2500}
2501
2502// 6.11 ValidateFloatCoercion
2504 if (!scanner_.IsGlobal() ||
2506 FAIL("Expected fround");
2507 }
2508 scanner_.Next();
2509 EXPECT_TOKEN('(');
2510 call_coercion_ = AsmType::Float();
2511 // NOTE: The coercion position to float is not observable from JavaScript,
2512 // because imported functions are not allowed to have float return type.
2514 AsmType* ret;
2516 if (ret->IsA(AsmType::Floatish())) {
2517 // Do nothing, as already a float.
2518 } else if (ret->IsA(AsmType::DoubleQ())) {
2519 current_function_builder_->Emit(kExprF32ConvertF64);
2520 } else if (ret->IsA(AsmType::Signed())) {
2521 current_function_builder_->Emit(kExprF32SConvertI32);
2522 } else if (ret->IsA(AsmType::Unsigned())) {
2523 current_function_builder_->Emit(kExprF32UConvertI32);
2524 } else {
2525 FAIL("Illegal conversion to float");
2526 }
2527 EXPECT_TOKEN(')');
2528}
2529
2531 int depth = 0;
2532 for (;;) {
2533 if (Peek('(')) {
2534 ++depth;
2535 } else if (Peek(')')) {
2536 --depth;
2537 if (depth < 0) {
2538 break;
2539 }
2540 } else if (Peek(AsmJsScanner::kEndOfInput)) {
2541 break;
2542 }
2543 scanner_.Next();
2544 }
2545}
2546
2548 size_t start = scanner_.Position();
2549 int depth = 0;
2550 for (;;) {
2551 if (Peek('{')) {
2552 ++depth;
2553 } else if (Peek('}')) {
2554 --depth;
2555 if (depth <= 0) {
2556 break;
2557 }
2558 } else if (depth == 1 && Peek(TOK(case))) {
2559 scanner_.Next();
2560 uint32_t uvalue;
2561 bool negate = false;
2562 if (Check('-')) negate = true;
2563 if (!CheckForUnsigned(&uvalue)) {
2564 break;
2565 }
2566 int32_t value = static_cast<int32_t>(uvalue);
2567 DCHECK_IMPLIES(negate && uvalue == 0x80000000, value == kMinInt);
2568 if (negate && value != kMinInt) {
2569 value = -value;
2570 }
2571 cases->push_back(value);
2572 } else if (Peek(AsmJsScanner::kEndOfInput) ||
2573 Peek(AsmJsScanner::kParseError)) {
2574 break;
2575 }
2576 scanner_.Next();
2577 }
2579}
2580
2581#undef TOK
2582#undef RECURSEn
2583#undef RECURSE
2584#undef RECURSE_OR_RETURN
2585#undef EXPECT_TOKENn
2586#undef EXPECT_TOKEN
2587#undef EXPECT_TOKEN_OR_RETURN
2588#undef FAILn
2589#undef FAIL
2590#undef FAIL_AND_RETURN
2591#undef TRACE_ASM_PARSER
2592
2593} // namespace wasm
2594} // namespace internal
2595} // namespace v8
#define STDLIB_ARRAY_TYPE_LIST(V)
Definition asm-names.h:50
#define STDLIB_MATH_VALUE_LIST(V)
Definition asm-names.h:9
#define STDLIB_MATH_FUNCTION_LIST(V)
Definition asm-names.h:41
#define STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
Definition asm-names.h:35
#define STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V)
Definition asm-names.h:20
#define FAIL(msg)
Definition asm-parser.cc:44
#define TOK(name)
Definition asm-parser.cc:71
#define EXPECT_TOKEN(token)
Definition asm-parser.cc:55
#define FAILn(msg)
Definition asm-parser.cc:45
#define EXPECT_TOKENn(token)
Definition asm-parser.cc:56
#define RECURSE(call)
Definition asm-parser.cc:68
#define RECURSEn(call)
Definition asm-parser.cc:69
#define HANDLE_CASE(op, opcode, name, result)
Builtins::Kind kind
Definition builtins.cc:40
constexpr void Add(E element)
Definition enum-set.h:50
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
static size_t GlobalIndex(token_t token)
Definition asm-scanner.h:88
static size_t LocalIndex(token_t token)
Definition asm-scanner.h:84
const std::string & GetIdentifierString() const
Definition asm-scanner.h:47
bool IsPrecededByNewline() const
Definition asm-scanner.h:54
static const char *const kSingleFunctionName
Definition asm-js.h:38
SignatureBuilder< Signature< T >, T > Builder
Definition signature.h:130
void push_back(const T &value)
base::Vector< std::remove_const_t< T > > CloneVector(base::Vector< T > v)
Definition zone.h:159
base::Vector< T > NewVector(size_t length)
Definition zone.h:143
T * New(Args &&... args)
Definition zone.h:114
virtual bool CanBeInvokedWith(AsmType *return_type, const ZoneVector< AsmType * > &args)=0
ZoneVector< BlockInfo > block_stack_
Definition asm-parser.h:202
bool CheckForUnsigned(uint32_t *value)
Definition asm-parser.h:285
AsmJsScanner::token_t Consume()
Definition asm-parser.h:305
CachedVectors< AsmJsScanner::token_t > cached_token_t_vectors_
Definition asm-parser.h:179
bool Peek(AsmJsScanner::token_t token)
Definition asm-parser.h:249
void ValidateModuleVar(bool mutable_variable)
void ValidateFunctionLocals(size_t param_count, ZoneVector< ValueType > *locals)
void ValidateModuleVarNewStdlib(VarInfo *info)
void Begin(AsmJsScanner::token_t label=0)
void GatherCases(ZoneVector< int32_t > *cases)
void ValidateModuleVarImport(VarInfo *info, bool mutable_variable)
base::Vector< VarInfo > local_var_info_
Definition asm-parser.h:174
AsmJsScanner::token_t foreign_name_
Definition asm-parser.h:193
WasmModuleBuilder * module_builder_
Definition asm-parser.h:168
int FindBreakLabelDepth(AsmJsScanner::token_t label)
AsmJsScanner::token_t stdlib_name_
Definition asm-parser.h:192
void ValidateModuleVarStdlib(VarInfo *info)
CachedVectors< int32_t > cached_int_vectors_
Definition asm-parser.h:180
AsmJsScanner::token_t pending_label_
Definition asm-parser.h:241
bool CheckForUnsignedBelow(uint32_t limit, uint32_t *value)
Definition asm-parser.h:295
void DeclareStdlibFunc(VarInfo *info, VarKind kind, AsmType *type)
static const AsmJsScanner::token_t kTokenNone
Definition asm-parser.h:196
void ValidateModuleVarFromGlobal(VarInfo *info, bool mutable_variable)
AsmType * Expression(AsmType *expect)
AsmJsScanner::token_t heap_name_
Definition asm-parser.h:194
base::Vector< VarInfo > global_var_info_
Definition asm-parser.h:173
bool Check(AsmJsScanner::token_t token)
Definition asm-parser.h:257
bool CheckForDouble(double *value)
Definition asm-parser.h:275
WasmFunctionBuilder * current_function_builder_
Definition asm-parser.h:169
uint32_t VarIndex(VarInfo *info)
void BareBegin(BlockKind kind, AsmJsScanner::token_t label=0)
CachedVectors< AsmType * > cached_asm_type_p_vectors_
Definition asm-parser.h:178
AsmJsParser(Zone *zone, uintptr_t stack_limit, Utf16CharacterStream *stream)
Definition asm-parser.cc:73
ZoneLinkedList< GlobalImport > global_imports_
Definition asm-parser.h:245
void ValidateFunctionParams(ZoneVector< AsmType * > *params)
CachedVectors< ValueType > cached_valuetype_vectors_
Definition asm-parser.h:177
int FindContinueLabelDepth(AsmJsScanner::token_t label)
base::Vector< const char > CopyCurrentIdentifierString()
void AddGlobalImport(base::Vector< const char > name, AsmType *type, ValueType vtype, bool mutable_variable, VarInfo *info)
static const size_t kNoHeapAccessShift
Definition asm-parser.h:237
void DeclareGlobal(VarInfo *info, bool mutable_variable, AsmType *type, ValueType vtype, WasmInitExpr init)
void Loop(AsmJsScanner::token_t label=0)
FunctionSig * ConvertSignature(AsmType *return_type, const ZoneVector< AsmType * > &params)
VarInfo * GetVarInfo(AsmJsScanner::token_t token)
uint32_t TempVariable(int index)
static AsmType * FroundType(Zone *zone)
Definition asm-types.cc:171
static AsmType * OverloadedFunction(Zone *zone)
Definition asm-types.h:208
bool IsA(AsmType *that)
Definition asm-types.cc:55
static AsmType * MinMaxType(Zone *zone, AsmType *dest, AsmType *src)
Definition asm-types.cc:229
static AsmType * Function(Zone *zone, AsmType *ret)
Definition asm-types.h:201
AsmCallableType * AsCallableType()
Definition asm-types.cc:13
static bool IsExactly(AsmType *x, AsmType *y)
Definition asm-types.cc:38
void SetAsmFunctionStartPosition(size_t function_position)
void AddAsmWasmOffset(size_t call_position, size_t to_number_position)
void EmitWithU32V(WasmOpcode opcode, uint32_t immediate)
void EmitWithU8(WasmOpcode opcode, const uint8_t immediate)
void SetName(base::Vector< const char > name)
void FixupByte(size_t position, uint8_t value)
static WasmInitExpr DefaultValue(ValueType type)
ModuleTypeIndex AddSignature(const FunctionSig *sig, bool is_final, ModuleTypeIndex supertype=kNoSuperType)
uint32_t AddImport(base::Vector< const char > name, const FunctionSig *sig, base::Vector< const char > module={})
void AddExport(base::Vector< const char > name, ImportExportKindCode kind, uint32_t index)
uint32_t AddGlobal(ValueType type, bool mutability, WasmInitExpr init)
void MarkStartFunction(WasmFunctionBuilder *builder)
uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count)
WasmFunctionBuilder * AddFunction(const FunctionSig *sig=nullptr)
uint32_t AddTable(ValueType type, uint32_t min_size)
void SetIndirectFunction(uint32_t table_index, uint32_t index_in_table, uint32_t direct_function_index, WasmElemSegment::FunctionIndexingMode indexing_mode)
uint32_t AddGlobalImport(base::Vector< const char > name, ValueType type, bool mutability, base::Vector< const char > module={})
Zone * zone_
int start
uint32_t count
Handle< SharedFunctionInfo > info
Label label
int32_t offset
std::optional< TNode< JSArray > > a
Register tmp
int position
Definition liveedit.cc:290
uint32_t const mask
int n
Definition mul-fft.cc:296
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
signed_type NegateWithWraparound(signed_type a)
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
Vector< const char > CStrVector(const char *data)
Definition vector.h:331
constexpr size_t kV8MaxWasmFunctionLocals
Definition wasm-limits.h:52
constexpr size_t kV8MaxWasmFunctionSize
Definition wasm-limits.h:51
constexpr IndependentValueType kWasmF32
constexpr IndependentValueType kWasmI32
constexpr IndependentHeapType kWasmFuncRef
constexpr IndependentValueType kWasmF64
constexpr size_t kV8MaxWasmFunctionParams
Definition wasm-limits.h:53
constexpr int kMinInt
Definition globals.h:375
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
float DoubleToFloat32(double x)
return value
Definition map-inl.h:893
Definition c-api.cc:87
uint32_t test
RegExpParserImpl< CharT > *const parser_
const uintptr_t stack_limit_
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
WasmFunctionBuilder * function_builder
Definition asm-parser.h:94
FunctionImportInfo *uint32_t mask
Definition asm-parser.h:96
Symbol identifier
#define V8_NODISCARD
Definition v8config.h:693
wasm::ValueType type