Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: src/asmjs/asm-parser.cc

Issue 2757693003: [wasm][asm.js] Asm.js -> wasm custom parser. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
5 #include "src/asmjs/asm-parser.h"
6
7 // Required to get M_E etc. in MSVC.
Michael Starzinger 2017/03/22 14:55:51 nit: Could we make this comment more explicit, tha
bradn 2017/03/24 03:58:18 Done.
8 #if defined(_WIN32)
9 #define _USE_MATH_DEFINES
10 #endif
11 #include <math.h>
12 #include <string.h>
13
14 #include <algorithm>
15
16 #include "src/asmjs/asm-types.h"
17 #include "src/objects-inl.h"
18 #include "src/objects.h"
19 #include "src/parsing/scanner-character-streams.h"
20 #include "src/wasm/wasm-macro-gen.h"
21 #include "src/wasm/wasm-opcodes.h"
22
23 namespace v8 {
24 namespace internal {
25 namespace wasm {
26
27 #define ASSTRING1(x) #x
28 #define ASSTRING(x) ASSTRING1(x)
vogelheim 2017/03/17 18:01:29 What does this indirect definition accomplish?
bradn 2017/03/24 03:58:18 Sorry leftover, inlined.
29
30 #ifdef DEBUG
31 #define FAIL(msg) \
32 failed_ = true; \
33 failure_message_ = std::string(msg) + \
34 " token: " + scanner_.Name(scanner_.Token()) + \
35 " see: " + __FILE__ + ":" + ASSTRING(__LINE__); \
36 failure_location_ = scanner_.GetPosition(); \
37 return FAILURE_RETURN;
38 #else
39 #define FAIL(msg) \
40 failed_ = true; \
41 failure_message_ = msg; \
42 failure_location_ = scanner_.GetPosition(); \
43 return FAILURE_RETURN;
44 #endif
45
46 #ifdef DEBUG
47 #define SKIP(token) \
marja 2017/03/17 13:49:04 Would it make sense to have these as functions ins
bradn 2017/03/24 03:58:17 It might be possible (and might be denser and ther
48 do { \
49 if (scanner_.Token() != token) { \
50 FAIL(std::string("expected token ") + scanner_.Name(token) + \
51 " but found " + scanner_.Name(scanner_.Token())); \
52 } \
53 scanner_.Next(); \
54 } while (false);
55 #else
56 #define SKIP(token) \
marja 2017/03/17 13:49:04 For naming consistency, SKIP could be called Expec
vogelheim 2017/03/17 17:31:13 +1 to Marja's comment. (I don't care so much abou
bradn 2017/03/24 03:58:18 Acknowledged.
bradn 2017/03/24 03:58:18 As I'm unable to switch this from being a macro at
57 do { \
58 if (scanner_.Token() != token) { \
59 FAIL("unexpected token"); \
60 } \
61 scanner_.Next(); \
62 } while (false);
63 #endif
64
65 #define RECURSE(call) \
66 do { \
67 DCHECK(GetCurrentStackPosition() >= stack_limit_); \
marja 2017/03/17 13:49:04 I like that the stack check is done when recursing
bradn 2017/03/24 03:58:17 Not keen on this pattern myself (got it from the a
68 call; \
69 if (GetCurrentStackPosition() < stack_limit_) { \
70 FAIL("Stack overflow while parsing asm.js module."); \
71 } \
72 if (failed_) return FAILURE_RETURN; \
73 } while (false);
74
75 #define TOK(name) AsmJsScanner::kToken_##name
76
77 AsmJsParser::AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
78 int start, int end)
79 : zone_(zone),
80 builder_(new (zone) WasmModuleBuilder(zone)),
81 return_type_(nullptr),
82 stack_limit_(isolate->stack_guard()->real_climit()),
83 failed_(false),
84 failure_location_(start),
85 inside_heap_assignment_(false),
86 heap_assignment_location_(0),
87 pending_label_(0) {
88 InitializeStdlibTypes();
89 Handle<String> source(String::cast(script->source()), isolate);
90 std::unique_ptr<Utf16CharacterStream> stream(
91 ScannerStream::For(source, start, end));
92 scanner_.SetStream(std::move(stream));
93 }
94
95 void AsmJsParser::InitializeStdlibTypes() {
96 auto* d = AsmType::Double();
97 auto* dq = AsmType::DoubleQ();
98 stdlib_dq2d_ = AsmType::Function(zone(), d);
99 stdlib_dq2d_->AsFunctionType()->AddArgument(dq);
100
101 stdlib_dqdq2d_ = AsmType::Function(zone(), d);
102 stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
103 stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
104
105 auto* f = AsmType::Float();
106 auto* fq = AsmType::FloatQ();
107 stdlib_fq2f_ = AsmType::Function(zone(), f);
108 stdlib_fq2f_->AsFunctionType()->AddArgument(fq);
109
110 auto* s = AsmType::Signed();
111 auto* s2s = AsmType::Function(zone(), s);
112 s2s->AsFunctionType()->AddArgument(s);
113
114 auto* i = AsmType::Int();
115 stdlib_i2s_ = AsmType::Function(zone_, s);
116 stdlib_i2s_->AsFunctionType()->AddArgument(i);
117
118 stdlib_ii2s_ = AsmType::Function(zone(), s);
119 stdlib_ii2s_->AsFunctionType()->AddArgument(i);
120 stdlib_ii2s_->AsFunctionType()->AddArgument(i);
121
122 auto* minmax_d = AsmType::MinMaxType(zone(), d, d);
123 // *VIOLATION* The float variant is not part of the spec, but firefox accepts
124 // it.
125 auto* minmax_f = AsmType::MinMaxType(zone(), f, f);
126 auto* minmax_i = AsmType::MinMaxType(zone(), s, i);
127 stdlib_minmax_ = AsmType::OverloadedFunction(zone());
128 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_i);
129 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f);
130 stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d);
131
132 stdlib_abs_ = AsmType::OverloadedFunction(zone());
133 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(s2s);
134 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
135 stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
136
137 stdlib_ceil_like_ = AsmType::OverloadedFunction(zone());
138 stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
139 stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
140
141 stdlib_fround_ = AsmType::FroundType(zone());
142 }
143
144 FunctionSig* AsmJsParser::ConvertSignature(
145 AsmType* return_type, const std::vector<AsmType*>& params) {
146 FunctionSig::Builder sig_builder(
147 zone(), !return_type->IsA(AsmType::Void()) ? 1 : 0, params.size());
148 for (auto param : params) {
149 if (param->IsA(AsmType::Double())) {
150 sig_builder.AddParam(kWasmF64);
151 } else if (param->IsA(AsmType::Float())) {
152 sig_builder.AddParam(kWasmF32);
153 } else if (param->IsA(AsmType::Int())) {
154 sig_builder.AddParam(kWasmI32);
155 } else {
156 return nullptr;
157 }
158 }
159 if (!return_type->IsA(AsmType::Void())) {
160 if (return_type->IsA(AsmType::Double())) {
161 sig_builder.AddReturn(kWasmF64);
162 } else if (return_type->IsA(AsmType::Float())) {
163 sig_builder.AddReturn(kWasmF32);
164 } else if (return_type->IsA(AsmType::Signed())) {
165 sig_builder.AddReturn(kWasmI32);
166 } else {
167 return 0;
168 }
169 }
170 return sig_builder.Build();
171 }
172
173 #define FAILURE_RETURN
vogelheim 2017/03/17 18:01:29 Hrmpf. This (and lines 1249/1259 and 2214/2215) ef
Michael Starzinger 2017/03/22 14:55:51 +1, I would also prefer to just define two differe
bradn 2017/03/24 03:58:17 Also applies to SKIP->EXPECT_TOKEN. Done.
bradn 2017/03/24 03:58:18 Acknowledged.
174
175 bool AsmJsParser::Run() {
176 ValidateModule();
177 return !failed_;
178 }
179
180 AsmJsParser::VarInfo::VarInfo()
181 : type(AsmType::None()),
182 index(0),
183 mask(0),
184 kind(VarKind::kUnused),
185 function_builder(nullptr),
186 mut(true),
187 import(nullptr) {}
188
189 wasm::AsmJsParser::VarInfo* AsmJsParser::GetVarInfo(
190 AsmJsScanner::token_t token) {
191 if (AsmJsScanner::IsGlobal(token)) {
192 size_t old = global_var_info_.size();
193 size_t index = AsmJsScanner::GlobalIndex(token);
194 size_t sz = std::max(old, index + 1);
195 if (sz != old) {
196 global_var_info_.resize(sz);
197 }
198 return &global_var_info_[index];
199 } else if (AsmJsScanner::IsLocal(token)) {
200 size_t old = local_var_info_.size();
201 size_t index = AsmJsScanner::LocalIndex(token);
202 size_t sz = std::max(old, index + 1);
203 if (sz != old) {
204 local_var_info_.resize(sz);
205 }
206 return &local_var_info_[index];
207 }
208 UNREACHABLE();
209 return nullptr;
210 }
211
212 uint32_t AsmJsParser::VarIndex(VarInfo* info) {
213 if (info->import != nullptr) {
214 return info->index;
215 } else {
216 return info->index + static_cast<uint32_t>(global_imports_.size());
217 }
218 }
219
220 void AsmJsParser::AddGlobalImport(std::string name, AsmType* type,
221 ValueType vtype, bool mut, VarInfo* info) {
marja 2017/03/17 13:49:04 What's "mut"?
marja 2017/03/20 14:28:47 Nevermind, it's actually "mut" in the spec too, so
bradn 2017/03/24 03:58:17 Yeah that was the other reason I used it, but chan
bradn 2017/03/24 03:58:18 mutable
222 if (mut) {
223 // Allocate a separate variable for the import.
224 DeclareGlobal(info, true, type, vtype);
225 // Record the need to initialize the global from the import.
226 global_imports_.push_back({name, 0, info->index, true});
227 } else {
228 // Just use the import directly.
229 global_imports_.push_back({name, 0, info->index, false});
230 }
231 GlobalImport& gi = global_imports_.back();
232 // TODO(bradnelson): Reuse parse buffer memory / make wasm-module-builder
233 // managed the memory for the import name (currently have to keep our
234 // own memory for it).
235 gi.import_index = builder_->AddGlobalImport(
236 name.data(), static_cast<int>(name.size()), vtype);
237 if (!mut) {
238 info->DeclareGlobalImport(type, gi.import_index);
239 }
240 }
241
242 void AsmJsParser::VarInfo::DeclareGlobalImport(AsmType* type, uint32_t index) {
243 kind = VarKind::kGlobal;
244 this->type = type;
245 this->index = index;
246 mut = false;
247 }
248
249 void AsmJsParser::VarInfo::DeclareStdlibFunc(VarKind kind, AsmType* type) {
250 this->kind = kind;
251 this->type = type;
252 index = 0; // unused
253 mut = false;
254 }
255
256 void AsmJsParser::DeclareGlobal(VarInfo* info, bool mut, AsmType* type,
257 ValueType vtype, const WasmInitExpr& init) {
258 info->kind = VarKind::kGlobal;
259 info->type = type;
260 info->index = builder_->AddGlobal(vtype, false, true, init);
261 info->mut = mut;
262 }
263
264 int32_t AsmJsParser::TempVariable(int i) {
265 if (i + 1 > function_temp_locals_used_) {
266 function_temp_locals_used_ = i + 1;
267 }
268 return function_temp_locals_offset_ + i;
269 }
270
271 void AsmJsParser::SkipSemicolon() {
272 if (Peek(';')) {
273 SKIP(';');
274 } else if (!Peek('}') && !scanner_.IsPrecededByNewline()) {
275 FAIL("Expected ;");
276 }
277 }
278
279 void AsmJsParser::Begin(AsmJsScanner::token_t label) {
280 BareBegin(BlockKind::kRegular, label);
281 current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
282 }
283
284 void AsmJsParser::Loop(AsmJsScanner::token_t label) {
285 BareBegin(BlockKind::kLoop, label);
286 current_function_builder_->EmitWithU8(kExprLoop, kLocalVoid);
287 }
288
289 void AsmJsParser::End() {
290 BareEnd();
291 current_function_builder_->Emit(kExprEnd);
292 }
293
294 void AsmJsParser::BareBegin(BlockKind kind, AsmJsScanner::token_t label) {
295 BlockInfo info;
296 info.kind = kind;
297 info.label = label;
298 block_stack_.push_back(info);
299 }
300
301 void AsmJsParser::BareEnd() {
302 DCHECK(block_stack_.size() > 0);
303 block_stack_.pop_back();
304 }
305
306 int AsmJsParser::FindLabelDepth(AsmJsScanner::token_t label, bool loop) {
307 int count = 0;
308 for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
309 ++it, ++count) {
310 if (loop && it->kind != BlockKind::kLoop) {
311 continue;
312 }
313 if (!loop && it->kind != BlockKind::kRegular) {
marja 2017/03/21 13:31:44 I don't get this function... (esp. its usage, when
bradn 2017/03/24 03:58:18 Broke this apart into two methods for finding brea
314 continue;
315 }
316 if (label == 0 || it->label == label) {
marja 2017/03/23 12:16:55 Documenting offline disucssion: IMO this looks lik
bradn 2017/03/24 03:58:17 So I believe this works. The 'if' type blocks are
317 return count;
318 }
319 }
320 return -1;
321 }
322
323 // 6.1 ValidateModule
324 void AsmJsParser::ValidateModule() {
325 RECURSE(ValidateModuleParameters());
marja 2017/03/20 14:28:47 Do we actually need to use RECURSE in constructs l
bradn 2017/03/24 03:58:18 RECURSE doubles as both a stack check and an early
326 SKIP('{');
327 SKIP(TOK(UseAsm));
328 SkipSemicolon();
329 RECURSE(ValidateModuleVars());
330 while (Peek(TOK(function))) {
331 RECURSE(ValidateFunction());
332 }
333 while (Peek(TOK(var))) {
334 RECURSE(ValidateFunctionTable());
335 }
336 RECURSE(ValidateExport());
337
338 // Add start function to init things.
339 WasmFunctionBuilder* start = builder_->AddFunction();
340 builder_->MarkStartFunction(start);
341 for (auto global_import : global_imports_) {
342 if (global_import.needs_init) {
343 start->EmitWithVarInt(kExprGetGlobal, global_import.import_index);
344 start->EmitWithVarInt(kExprSetGlobal,
345 static_cast<uint32_t>(global_import.global_index +
346 global_imports_.size()));
347 }
348 }
349 start->Emit(kExprEnd);
350 FunctionSig::Builder b(zone(), 0, 0);
351 start->SetSignature(b.Build());
352 }
353
354 // 6.1 ValidateModule - parameters
355 void AsmJsParser::ValidateModuleParameters() {
356 SKIP('(');
357 stdlib_name_ = 0;
358 foreign_name_ = 0;
359 heap_name_ = 0;
360 if (!Peek(')')) {
361 if (!scanner_.IsGlobal()) {
362 FAIL("Expected stdlib parameter");
363 }
364 stdlib_name_ = Consume();
365 if (!Peek(')')) {
366 SKIP(',');
367 if (!scanner_.IsGlobal()) {
368 FAIL("Expected foreign parameter");
369 }
370 foreign_name_ = Consume();
371 if (!Peek(')')) {
372 SKIP(',');
373 if (!scanner_.IsGlobal()) {
374 FAIL("Expected heap parameter");
375 }
376 heap_name_ = Consume();
377 }
378 }
379 }
380 SKIP(')');
381 }
382
383 // 6.1 ValidateModule - variables
384 void AsmJsParser::ValidateModuleVars() {
385 while (Peek(TOK(var)) || Peek(TOK(const))) {
386 bool mut = true;
387 if (Peek(TOK(var))) {
388 SKIP(TOK(var));
389 } else {
390 SKIP(TOK(const));
391 mut = false;
392 }
393 for (;;) {
394 RECURSE(ValidateModuleVar(mut));
395 if (Peek(',')) {
396 SKIP(',');
397 continue;
398 }
399 break;
400 }
401 SkipSemicolon();
402 }
403 }
404
405 // 6.1 ValidateModule - one variable
406 void AsmJsParser::ValidateModuleVar(bool mut) {
marja 2017/03/20 14:28:47 Lost... which part of the spec do I need to read t
bradn 2017/03/24 03:58:18 Acknowledged.
407 if (!scanner_.IsGlobal()) {
408 FAIL("Expected identifier");
409 }
410 VarInfo* info = GetVarInfo(Consume());
411 if (info->kind != VarKind::kUnused) {
412 FAIL("Redefinition of variable");
413 }
414 SKIP('=');
415 if (scanner_.IsDouble()) {
marja 2017/03/20 14:28:47 One thing I'm confused about is that which part pr
bradn 2017/03/24 03:58:18 So all of that lives in the parser. It's not share
416 DeclareGlobal(info, mut, AsmType::Double(), kWasmF64,
417 WasmInitExpr(scanner_.AsDouble()));
418 scanner_.Next();
419 } else if (scanner_.IsUnsigned()) {
marja 2017/03/20 14:28:47 I wonder whether it would make sense to have one f
bradn 2017/03/24 03:58:19 Added something like this in parser for now.
420 uint64_t value = scanner_.AsUnsigned();
421 if (value > 0x7fffffff) {
422 FAIL("Numeric literal out of range");
423 }
424 DeclareGlobal(info, mut, AsmType::Int(), kWasmI32,
425 WasmInitExpr(static_cast<int32_t>(value)));
426 scanner_.Next();
427 } else if (Peek('-')) {
428 SKIP('-');
429 if (scanner_.IsDouble()) {
430 DeclareGlobal(info, mut, AsmType::Double(), kWasmF64,
431 WasmInitExpr(-scanner_.AsDouble()));
432 scanner_.Next();
433 } else if (scanner_.IsUnsigned()) {
434 uint64_t value = scanner_.AsUnsigned();
435 if (value > 0x7fffffff) {
436 FAIL("Numeric literal out of range");
437 }
438 DeclareGlobal(info, mut, AsmType::Int(), kWasmI32,
439 WasmInitExpr(-static_cast<int32_t>(value)));
440 scanner_.Next();
441 } else {
442 FAIL("Expected numeric literal");
443 }
444 } else if (Peek('+')) {
445 SKIP('+');
446 SKIP(foreign_name_);
447 SKIP('.');
448 AddGlobalImport(scanner_.GetIdentifierString(), AsmType::Double(), kWasmF64,
449 mut, info);
450 scanner_.Next();
451 } else if (Peek(TOK(new))) {
452 SKIP(TOK(new));
453 SKIP(stdlib_name_);
454 SKIP('.');
455 switch (scanner_.Token()) {
456 #define V(name, _junk1, _junk2, _junk3) \
457 case TOK(name): \
458 info->DeclareStdlibFunc(VarKind::kSpecial, AsmType::name()); \
459 break;
460 STDLIB_ARRAY_TYPE_LIST(V)
461 #undef V
462 default:
463 FAIL("Expected ArrayBuffer view");
464 break;
465 }
466 scanner_.Next();
467 SKIP('(');
468 SKIP(heap_name_);
469 SKIP(')');
470 } else if (Peek(stdlib_name_)) {
471 SKIP(stdlib_name_);
472 SKIP('.');
473 RECURSE(ValidateModuleVarStdlib(info));
474 } else if (Peek(foreign_name_)) {
475 SKIP(foreign_name_);
476 SKIP('.');
477 std::string import_name = scanner_.GetIdentifierString();
478 scanner_.Next();
479 if (Peek('|')) {
480 SKIP('|');
481 if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() != 0) {
marja 2017/03/20 14:57:57 Checking for "|0" sounds such a common use case th
bradn 2017/03/24 03:58:17 Added a helper for checking for 0 which fit 3 plac
482 FAIL("Expected 0 in global signature");
483 }
484 scanner_.Next();
485 AddGlobalImport(import_name, AsmType::Int(), kWasmI32, mut, info);
486 return;
487 }
488 info->kind = VarKind::kImportedFunction;
489 function_import_info_.resize(function_import_info_.size() + 1);
490 info->import = &function_import_info_.back();
491 info->import->name = import_name;
492 } else if (scanner_.IsGlobal()) {
493 if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
494 FAIL("Expected fround");
495 }
496 SKIP('(');
497 bool negate = false;
498 if (Peek('-')) {
499 SKIP('-');
500 negate = true;
501 }
502 if (scanner_.IsDouble()) {
503 double value = scanner_.AsDouble();
504 if (negate) {
505 value = -value;
506 }
507 DeclareGlobal(info, mut, AsmType::Float(), kWasmF32,
508 WasmInitExpr(static_cast<float>(value)));
509 scanner_.Next();
510 } else if (scanner_.IsUnsigned()) {
511 double value = scanner_.AsUnsigned();
512 if (negate) {
513 value = -value;
514 }
515 DeclareGlobal(info, mut, AsmType::Float(), kWasmF32,
516 WasmInitExpr(static_cast<float>(value)));
517 scanner_.Next();
518 } else {
519 FAIL("Expected numeric literal");
520 }
521 SKIP(')');
522 } else {
523 FAIL("Bad variable declaration");
524 }
525 }
marja 2017/03/20 14:28:47 This function is quite long, too.
bradn 2017/03/24 03:58:18 Chopped it up into a few parts. In general this is
526
527 // 6.1 ValidateModule - one variable
528 // 9 - Standard Library
529 void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
530 if (Peek(TOK(Math))) {
531 SKIP(TOK(Math));
532 SKIP('.');
533 switch (Consume()) {
534 #define V(name) \
535 case TOK(name): \
536 DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
537 WasmInitExpr(M_##name)); \
538 break;
539 STDLIB_MATH_VALUE_LIST(V)
540 #undef V
541 #define V(name, Name, op, sig) \
542 case TOK(name): \
543 info->DeclareStdlibFunc(VarKind::kMath##Name, stdlib_##sig##_); \
544 stdlib_uses_.insert(AsmTyper::kMath##Name); \
545 break;
546 STDLIB_MATH_FUNCTION_LIST(V)
547 #undef V
548 default:
549 FAIL("Invalid member of stdlib.Math");
550 }
551 } else if (Peek(TOK(Infinity))) {
552 SKIP(TOK(Infinity));
553 DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
554 WasmInitExpr(std::numeric_limits<double>::infinity()));
555 } else if (Peek(TOK(NaN))) {
556 SKIP(TOK(NaN));
557 DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
558 WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
559 } else {
560 FAIL("Invalid member of stdlib");
561 }
562 }
563
564 // 6.2 ValidateExport
565 void AsmJsParser::ValidateExport() {
566 // clang-format off
567 SKIP(TOK(return));
568 // clang format on
569 if (Peek('{')) {
marja 2017/03/17 13:49:04 Would it make sense to have Check() like in Parser
bradn 2017/03/24 03:58:17 Great idea. Done.
570 SKIP('{');
571 for (;;) {
572 std::string name = scanner_.GetIdentifierString();
573 if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
574 FAIL("Illegal export name");
575 }
576 Consume();
577 SKIP(':');
578 if (!scanner_.IsGlobal()) {
579 FAIL("Expected function name");
580 }
581 VarInfo* info = GetVarInfo(Consume());
582 if (info->kind != VarKind::kFunction) {
583 FAIL("Expected function");
584 }
585 info->function_builder->ExportAs(
586 {name.c_str(), static_cast<int>(name.size())});
587 if (Peek(',')) {
588 SKIP(',');
589 if (!Peek('}')) {
590 continue;
591 }
592 }
593 break;
594 }
595 SKIP('}');
596 } else {
597 if (!scanner_.IsGlobal()) {
598 FAIL("Single function export must be a function name");
599 }
600 VarInfo* info = GetVarInfo(Consume());
601 if (info->kind != VarKind::kFunction) {
602 FAIL("Single function export must be a function");
603 }
604 const char* single_function_name = "__single_function__";
605 info->function_builder->ExportAs(CStrVector(single_function_name));
606 }
607 }
608
609 // 6.3 ValidateFunctionTable
610 void AsmJsParser::ValidateFunctionTable() {
611 SKIP(TOK(var));
612 if (!scanner_.IsGlobal()) {
613 FAIL("Expected table name");
614 }
615 VarInfo* table_info = GetVarInfo(Consume());
616 if (table_info->kind != VarKind::kTable) {
617 FAIL("Unused function table");
marja 2017/03/20 14:57:57 Not sure when this error occurs or what it means.
bradn 2017/03/24 03:58:17 Meant a function table was defined but never used.
618 }
619 // TODO(bradnelson): Check for double use of export name.
620 SKIP('=');
621 SKIP('[');
622 uint64_t count = 0;
623 for (;;) {
624 if (!scanner_.IsGlobal()) {
625 FAIL("Expected function name");
626 }
627 VarInfo* info = GetVarInfo(Consume());
628 if (info->kind != VarKind::kFunction) {
629 FAIL("Expected function");
630 }
631 if (count >= table_info->mask + 1) {
marja 2017/03/20 14:57:58 It's non-trivial to see where the mask is coming f
bradn 2017/03/24 03:58:19 Done.
632 FAIL("Exceeded function table size");
633 }
634 builder_->SetIndirectFunction(
635 static_cast<uint32_t>(table_info->index + count), info->index);
636 ++count;
637 if (Peek(',')) {
638 SKIP(',');
639 if (!Peek(']')) {
640 continue;
641 }
642 }
643 break;
644 }
645 SKIP(']');
646 if (count != table_info->mask + 1) {
647 FAIL("Function table size does not match uses");
648 }
649 SkipSemicolon();
650 }
651
652 // 6.4 ValidateFunction
653 void AsmJsParser::ValidateFunction() {
654 int start_position = scanner_.GetPosition();
655 SKIP(TOK(function));
656 if (!scanner_.IsGlobal()) {
657 FAIL("Expected function name");
658 }
659
660 std::string function_name_raw = scanner_.GetIdentifierString();
661 AsmJsScanner::token_t function_name = Consume();
662 VarInfo* function_info = GetVarInfo(function_name);
663 if (function_info->kind == VarKind::kUnused) {
marja 2017/03/21 13:31:43 So redeclarations are ok? Which test tests that?
bradn 2017/03/24 03:58:17 Ooh, messed that up. They're allowed to be defined
664 function_info->kind = VarKind::kFunction;
665 function_info->function_builder = builder_->AddFunction();
marja 2017/03/21 13:31:44 This line got me confused for a moment; would it h
bradn 2017/03/24 03:58:17 Done.
666 function_info->function_builder->SetName(
667 {function_name_raw.c_str(),
668 static_cast<int>(function_name_raw.size())});
669 function_info->index = function_info->function_builder->func_index();
670 }
671 current_function_builder_ = function_info->function_builder;
672 return_type_ = nullptr;
673
674 // Record start of the function, used as position for the stack check.
675 current_function_builder_->SetAsmFunctionStartPosition(start_position);
676
677 scanner_.EnterLocalScope();
marja 2017/03/21 13:31:44 Continuing my previous comments about EnterLocalSc
bradn 2017/03/24 03:58:17 Agreed this is confusing and needs to be redone. A
678 SKIP('(');
679 std::vector<AsmJsScanner::token_t> function_parameters;
680 while (!failed_ && !Peek(')')) {
681 if (!scanner_.IsLocal()) {
682 FAIL("Expected parameter name");
683 }
684 function_parameters.push_back(Consume());
685 if (!Peek(')')) {
686 SKIP(',');
687 }
688 }
689 SKIP(')');
690 std::vector<AsmType*> params;
691 std::vector<ValueType> locals;
692 scanner_.EnterGlobalScope();
693 SKIP('{');
694 // 5.1 Parameter Type Annotations
695 for (auto p : function_parameters) {
696 SKIP(p);
697 SKIP('=');
698 VarInfo* info = GetVarInfo(p);
699 if (info->kind != VarKind::kUnused) {
700 FAIL("Duplicate parameter name");
701 }
702 if (Peek(p)) {
703 SKIP(p);
704 SKIP('|');
705 if (!Peek(AsmJsScanner::kUnsigned) || scanner_.AsUnsigned() != 0) {
706 FAIL("Bad integer parameter annotation.");
707 }
708 scanner_.Next();
709 info->kind = VarKind::kLocal;
710 info->type = AsmType::Int();
711 info->index = static_cast<uint32_t>(params.size());
712 params.push_back(AsmType::Int());
713 } else if (Peek('+')) {
714 SKIP('+');
715 SKIP(p);
716 info->kind = VarKind::kLocal;
717 info->type = AsmType::Double();
718 info->index = static_cast<uint32_t>(params.size());
719 params.push_back(AsmType::Double());
720 } else {
721 if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
722 FAIL("Expected fround");
723 }
724 SKIP('(');
725 SKIP(p);
726 SKIP(')');
727 info->kind = VarKind::kLocal;
728 info->type = AsmType::Float();
729 info->index = static_cast<uint32_t>(params.size());
730 params.push_back(AsmType::Float());
731 }
732 SkipSemicolon();
733 }
734 // Local Variables.
735 while (Peek(TOK(var))) {
736 scanner_.EnterLocalScope();
737 SKIP(TOK(var));
738 scanner_.EnterGlobalScope();
marja 2017/03/20 14:57:58 This is quite confusing construct! The reason see
bradn 2017/03/24 03:58:18 Agreed this is very messy. I had thought about def
739 for (;;) {
740 if (!scanner_.IsLocal()) {
741 FAIL("Expected local variable identifier");
742 }
743 VarInfo* info = GetVarInfo(Consume());
744 if (info->kind != VarKind::kUnused) {
745 FAIL("Duplicate local variable name");
746 }
747 // Store types.
748 SKIP('=');
749 if (Peek('-')) {
750 SKIP('-');
751 if (scanner_.IsDouble()) {
752 info->kind = VarKind::kLocal;
753 info->type = AsmType::Double();
754 info->index = static_cast<uint32_t>(params.size() + locals.size());
755 locals.push_back(kWasmF64);
756 double value = -scanner_.AsDouble();
757 byte code[] = {WASM_F64(value)};
758 current_function_builder_->EmitCode(code, sizeof(code));
759 current_function_builder_->EmitSetLocal(info->index);
760 scanner_.Next();
761 } else if (scanner_.IsUnsigned()) {
762 if (scanner_.AsUnsigned() > 0x7fffffff) {
763 FAIL("Numeric literal out of range");
764 }
765 info->kind = VarKind::kLocal;
766 info->type = AsmType::Int();
767 info->index = static_cast<uint32_t>(params.size() + locals.size());
768 locals.push_back(kWasmI32);
769 int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
770 current_function_builder_->EmitI32Const(value);
771 current_function_builder_->EmitSetLocal(info->index);
772 scanner_.Next();
773 } else {
774 FAIL("Expected variable initial value");
775 }
776 } else if (scanner_.IsGlobal()) {
777 VarInfo* sinfo = GetVarInfo(Consume());
778 if (sinfo->kind == VarKind::kGlobal) {
779 if (sinfo->mut) {
780 FAIL("Initializing from global requires const variable");
781 }
782 info->kind = VarKind::kLocal;
783 info->type = sinfo->type;
784 info->index = static_cast<uint32_t>(params.size() + locals.size());
785 if (sinfo->type->IsA(AsmType::Int())) {
786 locals.push_back(kWasmI32);
787 } else if (sinfo->type->IsA(AsmType::Float())) {
788 locals.push_back(kWasmF32);
789 } else if (sinfo->type->IsA(AsmType::Double())) {
790 locals.push_back(kWasmF64);
791 } else {
792 FAIL("Bad local variable definition");
793 }
794 current_function_builder_->EmitWithVarInt(kExprGetGlobal,
795 VarIndex(sinfo));
796 current_function_builder_->EmitSetLocal(info->index);
797 } else if (sinfo->type->IsA(stdlib_fround_)) {
798 SKIP('(');
799 bool negate = false;
800 if (Peek('-')) {
801 SKIP('-');
802 negate = true;
803 }
804 if (scanner_.IsDouble()) {
805 info->kind = VarKind::kLocal;
806 info->type = AsmType::Float();
807 info->index = static_cast<uint32_t>(params.size() + locals.size());
808 locals.push_back(kWasmF32);
809 double value = scanner_.AsDouble();
810 if (negate) {
811 value = -value;
812 }
813 byte code[] = {WASM_F32(value)};
814 current_function_builder_->EmitCode(code, sizeof(code));
815 current_function_builder_->EmitSetLocal(info->index);
816 scanner_.Next();
817 } else if (scanner_.IsUnsigned()) {
818 if (scanner_.AsUnsigned() > 0x7fffffff) {
819 FAIL("Numeric literal out of range");
820 }
821 info->kind = VarKind::kLocal;
822 info->type = AsmType::Float();
823 info->index = static_cast<uint32_t>(params.size() + locals.size());
824 locals.push_back(kWasmF32);
825 int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
826 if (negate) {
827 value = -value;
828 }
829 double fvalue = static_cast<double>(value);
830 byte code[] = {WASM_F32(fvalue)};
831 current_function_builder_->EmitCode(code, sizeof(code));
832 current_function_builder_->EmitSetLocal(info->index);
833 scanner_.Next();
834 } else {
835 FAIL("Expected variable initial value");
836 }
837 SKIP(')');
838 } else {
839 FAIL("expected fround or const global");
840 }
841 } else if (scanner_.IsDouble()) {
842 info->kind = VarKind::kLocal;
843 info->type = AsmType::Double();
844 info->index = static_cast<uint32_t>(params.size() + locals.size());
845 locals.push_back(kWasmF64);
846 double value = scanner_.AsDouble();
847 byte code[] = {WASM_F64(value)};
848 current_function_builder_->EmitCode(code, sizeof(code));
849 current_function_builder_->EmitSetLocal(info->index);
850 scanner_.Next();
851 } else if (scanner_.IsUnsigned()) {
852 info->kind = VarKind::kLocal;
853 info->type = AsmType::Int();
854 info->index = static_cast<uint32_t>(params.size() + locals.size());
855 locals.push_back(kWasmI32);
856 int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
857 current_function_builder_->EmitI32Const(value);
858 current_function_builder_->EmitSetLocal(info->index);
859 scanner_.Next();
860 } else {
861 FAIL("Expected variable initial value");
862 }
863 if (!Peek(',')) {
864 break;
865 }
866 scanner_.EnterLocalScope();
marja 2017/03/20 14:57:57 Ditto... except that this is even more confusing a
bradn 2017/03/24 03:58:17 Same as above.
867 SKIP(',');
868 scanner_.EnterGlobalScope();
869 }
870 SkipSemicolon();
871 }
872
873 function_temp_locals_offset_ = static_cast<uint32_t>(
874 params.size() + locals.size());
875 function_temp_locals_used_ = 0;
876
877 while (!failed_ && !Peek('}')) {
878 RECURSE(ValidateStatement());
879 }
880 SKIP('}');
881
882 if (return_type_ == nullptr) {
883 return_type_ = AsmType::Void();
884 }
885
886 // TODO(bradnelson): WasmModuleBuilder can't take this in the right order.
887 // We should fix that so we can use it instead.
888 FunctionSig* sig = ConvertSignature(return_type_, params);
889 if (sig == nullptr) {
890 FAIL("Invalid function signature in declaration");
891 }
892 current_function_builder_->SetSignature(sig);
893 for (auto local : locals) {
894 current_function_builder_->AddLocal(local);
895 }
896 // Add bonus temps.
897 for (int i = 0; i < function_temp_locals_used_; ++i) {
898 current_function_builder_->AddLocal(kWasmI32);
899 }
900
901 // End function
902 current_function_builder_->Emit(kExprEnd);
903
904 // Add in function type.
905 AsmType* function_type = AsmType::Function(zone(), return_type_);
906 for (auto t : params) {
907 function_type->AsFunctionType()->AddArgument(t);
908 }
909 function_info = GetVarInfo(function_name);
910 if (function_info->kind == VarKind::kUnused) {
911 function_info->kind = VarKind::kFunction;
912 function_info->index = current_function_builder_->func_index();
913 function_info->type = function_type;
914 } else {
915 if (function_info->kind != VarKind::kFunction) {
916 FAIL("Function name collides with variable");
917 }
918 // TODO(bradnelson): Should IsExactly be used here?
919 if (!function_info->type->IsA(AsmType::None()) &&
920 !function_type->IsA(function_info->type)) {
921 FAIL("Function definition doesn't match use");
922 }
923 }
924
925 scanner_.ResetLocals();
926 local_var_info_.clear();
927 }
marja 2017/03/20 14:28:47 This function is almost 300 lines; would it be pos
bradn 2017/03/24 03:58:18 Done.
928
929 // ValidateStatement
930 void AsmJsParser::ValidateStatement() {
931 call_coercion_ = nullptr;
932 if (Peek('{')) {
933 RECURSE(Block());
934 } else if (Peek(';')) {
935 RECURSE(EmptyStatement());
936 } else if (Peek(TOK(if))) {
937 RECURSE(IfStatement());
938 // clang-format off
939 } else if (Peek(TOK(return))) {
940 // clang-format on
941 RECURSE(ReturnStatement());
942 } else if (IterationStatement()) {
943 // Handled in IterationStatement.
944 } else if (Peek(TOK(break))) {
945 RECURSE(BreakStatement());
946 } else if (Peek(TOK(continue))) {
947 RECURSE(ContinueStatement());
948 } else if (Peek(TOK(switch))) {
949 RECURSE(SwitchStatement());
950 } else {
951 RECURSE(ExpressionStatement());
952 }
953 }
954
955 // 6.5.1 Block
956 void AsmJsParser::Block() {
957 bool wrap_block = pending_label_ != 0;
marja 2017/03/20 14:57:57 What's "wrap_block"?
bradn 2017/03/24 03:58:17 Renamed can_break_to_block i.e. this is deciding i
958 if (wrap_block) {
959 Begin(pending_label_);
960 }
961 pending_label_ = 0;
962 SKIP('{');
963 while (!failed_ && !Peek('}')) {
964 RECURSE(ValidateStatement());
965 }
966 SKIP('}');
967 if (wrap_block) {
968 End();
969 }
970 }
971
972 // 6.5.2 ExpressionStatement
973 void AsmJsParser::ExpressionStatement() {
974 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
975 scanner_.Next();
976 if (Peek(':')) {
977 scanner_.Rewind();
marja 2017/03/20 14:57:57 Does this construct make labels appear in the loca
bradn 2017/03/24 03:58:18 Added a comment. Surprisingly asm.js code in the w
978 RECURSE(LabelledStatement());
979 return;
980 }
981 scanner_.Rewind();
982 }
983 AsmType* ret;
984 RECURSE(ret = ValidateExpression());
985 if (!ret->IsA(AsmType::Void())) {
986 current_function_builder_->Emit(kExprDrop);
987 }
988 SkipSemicolon();
989 }
990
991 // 6.5.3 EmptyStatement
992 void AsmJsParser::EmptyStatement() { SKIP(';'); }
993
994 // 6.5.4 IfStatement
995 void AsmJsParser::IfStatement() {
996 SKIP(TOK(if));
997 SKIP('(');
998 RECURSE(Expression(AsmType::Int()));
999 SKIP(')');
1000 current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
1001 BareBegin();
1002 RECURSE(ValidateStatement());
1003 if (Peek(TOK(else))) {
1004 SKIP(TOK(else));
1005 current_function_builder_->Emit(kExprElse);
1006 RECURSE(ValidateStatement());
1007 }
1008 current_function_builder_->Emit(kExprEnd);
1009 BareEnd();
1010 }
1011
1012 // 6.5.5 ReturnStatement
1013 void AsmJsParser::ReturnStatement() {
1014 // clang-format off
1015 SKIP(TOK(return ));
1016 // clang-format on
1017 if (!Peek(';') && !Peek('}')) {
1018 AsmType* ret;
1019 RECURSE(ret = Expression(return_type_));
1020 if (ret->IsA(AsmType::Double())) {
marja 2017/03/21 13:31:44 This kind of check (checking the type and setting
bradn 2017/03/24 03:58:18 A bunch of these are close, but no patterned jumpe
1021 return_type_ = AsmType::Double();
1022 } else if (ret->IsA(AsmType::Float())) {
1023 return_type_ = AsmType::Float();
1024 } else if (ret->IsA(AsmType::Signed())) {
1025 return_type_ = AsmType::Signed();
1026 } else {
1027 FAIL("Invalid return type");
1028 }
1029 } else {
1030 return_type_ = AsmType::Void();
1031 }
1032 current_function_builder_->Emit(kExprReturn);
1033 SkipSemicolon();
1034 }
1035
1036 // 6.5.6 IterationStatement
1037 bool AsmJsParser::IterationStatement() {
1038 if (Peek(TOK(while))) {
1039 WhileStatement();
1040 } else if (Peek(TOK(do))) {
1041 DoStatement();
1042 } else if (Peek(TOK(for))) {
1043 ForStatement();
1044 } else {
1045 return false;
1046 }
1047 return true;
1048 }
1049
1050 // 6.5.6 IterationStatement - while
1051 void AsmJsParser::WhileStatement() {
1052 Begin(pending_label_);
1053 Loop(pending_label_);
marja 2017/03/21 13:31:43 I don't understand (based on looking at this for a
bradn 2017/03/24 03:58:18 Done.
1054 pending_label_ = 0;
1055 SKIP(TOK(while));
1056 SKIP('(');
1057 RECURSE(Expression(AsmType::Int()));
1058 SKIP(')');
1059 current_function_builder_->Emit(kExprI32Eqz);
marja 2017/03/21 13:31:43 Comment pls.
bradn 2017/03/24 03:58:18 Done.
1060 current_function_builder_->EmitWithU8(kExprBrIf, 1);
1061 RECURSE(ValidateStatement());
1062 current_function_builder_->EmitWithU8(kExprBr, 0);
1063 End();
1064 End();
1065 }
1066
1067 // 6.5.6 IterationStatement - do
1068 void AsmJsParser::DoStatement() {
1069 Begin(pending_label_);
1070 Loop();
1071 BareBegin(BlockKind::kLoop, pending_label_);
1072 current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
1073 pending_label_ = 0;
1074 SKIP(TOK(do));
1075 RECURSE(ValidateStatement());
1076 SKIP(TOK(while));
1077 current_function_builder_->Emit(kExprEnd);
1078 BareEnd();
1079 SKIP('(');
1080 RECURSE(Expression(AsmType::Int()));
1081 current_function_builder_->Emit(kExprI32Eqz);
marja 2017/03/21 13:31:43 Ditto.
bradn 2017/03/24 03:58:17 Done.
1082 current_function_builder_->EmitWithU8(kExprBrIf, 1);
1083 current_function_builder_->EmitWithU8(kExprBr, 0);
1084 SKIP(')');
1085 End();
1086 End();
1087 SkipSemicolon();
1088 }
1089
1090 // 6.5.6 IterationStatement - for
1091 void AsmJsParser::ForStatement() {
1092 SKIP(TOK(for));
1093 SKIP('(');
1094 if (!Peek(';')) {
1095 Expression(nullptr);
1096 }
1097 SKIP(';');
1098 Begin(pending_label_);
1099 Loop(pending_label_);
1100 pending_label_ = 0;
1101 if (!Peek(';')) {
1102 RECURSE(Expression(AsmType::Int()));
1103 current_function_builder_->Emit(kExprI32Eqz);
1104 current_function_builder_->EmitWithU8(kExprBrIf, 1);
1105 }
1106 SKIP(';');
1107 size_t increment_position = current_function_builder_->GetPosition();
1108 if (!Peek(')')) {
1109 RECURSE(Expression(nullptr));
1110 }
1111 std::vector<byte> increment_code;
1112 current_function_builder_->StashCode(&increment_code, increment_position);
1113 SKIP(')');
1114 RECURSE(ValidateStatement());
1115 current_function_builder_->EmitCode(
1116 increment_code.data(), static_cast<uint32_t>(increment_code.size()));
1117 current_function_builder_->EmitWithU8(kExprBr, 0);
1118 End();
1119 End();
1120 }
1121
1122 // 6.5.7 BreakStatement
1123 void AsmJsParser::BreakStatement() {
1124 SKIP(TOK(break));
1125 int depth;
1126 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
marja 2017/03/21 13:31:43 Why can it be Global here? Can this construct appe
bradn 2017/03/24 03:58:17 Currently reusing global / local identifiers for l
1127 depth = FindLabelDepth(scanner_.Token(), false);
1128 scanner_.Next();
1129 } else {
1130 depth = FindLabelDepth(0, false);
1131 }
1132 if (depth < 0) {
1133 FAIL("Illegal break");
1134 }
1135 current_function_builder_->Emit(kExprBr);
1136 current_function_builder_->EmitVarInt(depth);
1137 SkipSemicolon();
1138 }
1139
1140 // 6.5.8 ContinueStatement
1141 void AsmJsParser::ContinueStatement() {
1142 SKIP(TOK(continue));
1143 int depth;
1144 if (scanner_.IsGlobal() || scanner_.IsLocal()) {
1145 depth = FindLabelDepth(scanner_.Token(), true);
1146 scanner_.Next();
1147 } else {
1148 depth = FindLabelDepth(0, true);
marja 2017/03/21 13:31:44 Why is the "bool loop" true here and false in Brea
bradn 2017/03/24 03:58:18 Refactored for clarity, they share the same stack,
1149 }
1150 if (depth < 0) {
1151 FAIL("Illegal continue");
1152 }
1153 current_function_builder_->Emit(kExprBr);
marja 2017/03/21 13:31:43 This sounds suspicious. BreakStatement and Continu
bradn 2017/03/24 03:58:18 Br is the only unconditional construct in wasm. It
1154 current_function_builder_->EmitVarInt(depth);
1155 SkipSemicolon();
1156 }
1157
1158 // 6.5.9 LabelledStatement
1159 void AsmJsParser::LabelledStatement() {
1160 DCHECK(scanner_.IsGlobal() || scanner_.IsLocal());
1161 if (pending_label_ != 0) {
1162 FAIL("Double label unsupported");
1163 }
1164 pending_label_ = scanner_.Token();
1165 scanner_.Next();
1166 SKIP(':');
1167 RECURSE(ValidateStatement());
1168 }
1169
1170 // 6.5.10 SwitchStatement
1171 void AsmJsParser::SwitchStatement() {
1172 SKIP(TOK(switch));
1173 SKIP('(');
1174 AsmType* test;
1175 RECURSE(test = Expression(nullptr));
1176 if (!test->IsA(AsmType::Signed())) {
1177 FAIL("Expected signed for switch value");
1178 }
1179 SKIP(')');
1180 int32_t tmp = TempVariable(0);
1181 current_function_builder_->EmitSetLocal(tmp);
1182 Begin(pending_label_);
1183 pending_label_ = 0;
1184 // TODO(bradnelson): Make less weird.
1185 std::vector<int32_t> cases;
1186 GatherCases(&cases); // Skips { implicitly.
vogelheim 2017/03/17 18:01:29 Is the comment correct? GatherCases stores GetPosi
bradn 2017/03/24 03:58:18 The comment is correct, that is a little odd thoug
1187 size_t count = cases.size() + 1;
1188 for (size_t i = 0; i < count; ++i) {
1189 BareBegin(BlockKind::kOther);
1190 current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
1191 }
1192 int table_pos = 0;
1193 for (auto c : cases) {
1194 current_function_builder_->EmitGetLocal(tmp);
1195 current_function_builder_->EmitI32Const(c);
1196 current_function_builder_->Emit(kExprI32Eq);
1197 current_function_builder_->EmitWithVarInt(kExprBrIf, table_pos++);
1198 }
1199 current_function_builder_->EmitWithVarInt(kExprBr, table_pos++);
1200 while (!failed_ && Peek(TOK(case))) {
1201 current_function_builder_->Emit(kExprEnd);
1202 BareEnd();
1203 RECURSE(ValidateCase());
1204 }
1205 current_function_builder_->Emit(kExprEnd);
1206 BareEnd();
1207 if (Peek(TOK(default))) {
1208 RECURSE(ValidateDefault());
1209 }
1210 SKIP('}');
1211 End();
1212 }
1213
1214 // 6.6. ValidateCase
1215 void AsmJsParser::ValidateCase() {
1216 SKIP(TOK(case));
1217 bool negate = false;
1218 if (Peek('-')) {
1219 SKIP('-');
1220 negate = true;
1221 }
1222 if (!scanner_.IsUnsigned()) {
1223 FAIL("Expected numeric literal");
1224 }
1225 if ((negate && scanner_.AsUnsigned() > 0x80000000) ||
marja 2017/03/21 13:31:43 This logic is duplicated in several places -> can
bradn 2017/03/24 03:58:18 Should do. Don't see an easy pattern. Added a TODO
1226 (!negate && scanner_.AsUnsigned() > 0x7fffffff)) {
1227 FAIL("Numeric literal out of range");
1228 }
1229 int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
1230 if (negate) {
1231 value = -value;
1232 }
1233 scanner_.Next();
1234 SKIP(':');
1235 while (!failed_ && !Peek('}') && !Peek(TOK(case)) && !Peek(TOK(default))) {
1236 RECURSE(ValidateStatement());
1237 }
1238 }
1239
1240 // 6.7 ValidateDefault
1241 void AsmJsParser::ValidateDefault() {
1242 SKIP(TOK(default));
1243 SKIP(':');
1244 while (!failed_ && !Peek('}')) {
1245 RECURSE(ValidateStatement());
1246 }
1247 }
1248
1249 #undef FAILURE_RETURN
1250 #define FAILURE_RETURN AsmType::None()
1251
1252 // 6.8 ValidateExpression
1253 AsmType* AsmJsParser::ValidateExpression() {
1254 AsmType* ret;
1255 RECURSE(ret = Expression(nullptr));
1256 return ret;
1257 }
1258
1259 // 6.8.1 Expression
1260 AsmType* AsmJsParser::Expression(AsmType* expected) {
1261 AsmType* a;
1262 for (;;) {
1263 RECURSE(a = AssignmentExpression());
1264 if (Peek(',')) {
1265 if (a->IsA(AsmType::None())) {
1266 FAIL("Expected actual type");
1267 }
1268 if (!a->IsA(AsmType::Void())) {
1269 current_function_builder_->Emit(kExprDrop);
1270 }
1271 SKIP(',');
1272 continue;
1273 }
1274 break;
1275 }
1276 if (expected != nullptr && !a->IsA(expected)) {
1277 FAIL("Unexpected type");
1278 }
1279 return a;
1280 }
1281
1282 // 6.8.2 NumericLiteral
1283 AsmType* AsmJsParser::NumericLiteral() {
1284 call_coercion_ = nullptr;
1285 if (scanner_.Token() == AsmJsScanner::kDouble) {
1286 double value = scanner_.AsDouble();
1287 byte code[] = {WASM_F64(value)};
1288 current_function_builder_->EmitCode(code, sizeof(code));
1289 scanner_.Next();
1290 return AsmType::Double();
1291 } else if (scanner_.IsUnsigned()) {
1292 uint64_t value = scanner_.AsUnsigned();
1293 if (value <= 0x7fffffff) {
1294 current_function_builder_->EmitI32Const(static_cast<int32_t>(value));
1295 scanner_.Next();
1296 return AsmType::FixNum();
1297 } else if (value <= 0x100000000) {
vogelheim 2017/03/17 18:01:28 < instead <= ???
bradn 2017/03/24 03:58:18 Done.
1298 current_function_builder_->EmitI32Const(static_cast<int32_t>(value));
1299 scanner_.Next();
1300 return AsmType::Unsigned();
1301 } else {
1302 FAIL("Integer numeric literal out of range.");
1303 }
1304 } else {
1305 FAIL("Expected numeric literal.");
1306 }
1307 }
1308
1309 // 6.8.3 Identifier
1310 AsmType* AsmJsParser::Identifier() {
1311 call_coercion_ = nullptr;
marja 2017/03/21 13:31:43 What's call_coercion_?
bradn 2017/03/24 03:58:17 Added comment in header.
1312 if (scanner_.IsLocal()) {
1313 VarInfo* info = GetVarInfo(Consume());
1314 if (info->kind != VarKind::kLocal) {
1315 FAIL("Undefined local variable");
1316 }
1317 current_function_builder_->EmitGetLocal(info->index);
1318 return info->type;
1319 } else if (scanner_.IsGlobal()) {
1320 VarInfo* info = GetVarInfo(Consume());
1321 if (info->kind != VarKind::kGlobal) {
1322 FAIL("Undefined global variable");
1323 }
1324 current_function_builder_->EmitWithVarInt(kExprGetGlobal, VarIndex(info));
1325 return info->type;
1326 }
1327 UNREACHABLE();
1328 return nullptr;
1329 }
1330
1331 // 6.8.4 CallExpression
1332 AsmType* AsmJsParser::CallExpression() {
1333 AsmType* ret;
1334 if (scanner_.IsGlobal() &&
1335 GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
1336 ValidateFloatCoercion();
1337 return AsmType::Float();
1338 } else if (scanner_.IsGlobal() &&
1339 GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
1340 RECURSE(ret = MemberExpression());
1341 } else if (Peek('(')) {
1342 RECURSE(ret = ParenthesizedExpression());
1343 } else if (PeekCall()) {
1344 RECURSE(ret = ValidateCall());
1345 } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
1346 RECURSE(ret = Identifier());
1347 } else {
1348 RECURSE(ret = NumericLiteral());
1349 }
1350 return ret;
1351 }
1352
1353 // 6.8.5 MemberExpression
1354 AsmType* AsmJsParser::MemberExpression() {
1355 call_coercion_ = nullptr;
1356 int access_location = scanner_.GetPosition();
1357 ValidateHeapAccess();
1358 if (Peek('=')) {
1359 inside_heap_assignment_ = true;
1360 heap_assignment_location_ = access_location;
1361 return heap_access_type_->StoreType();
1362 } else {
1363 #define V(array_type, wasmload, wasmstore, type) \
1364 if (heap_access_type_->IsA(AsmType::array_type())) { \
1365 current_function_builder_->Emit(kExpr##type##AsmjsLoad##wasmload); \
1366 return heap_access_type_->LoadType(); \
1367 }
1368 STDLIB_ARRAY_TYPE_LIST(V)
1369 #undef V
1370 FAIL("Expected valid heap load");
1371 }
1372 }
1373
1374 // 6.8.6 AssignmentExpression
1375 AsmType* AsmJsParser::AssignmentExpression() {
1376 AsmType* ret;
1377 if (scanner_.IsGlobal() &&
1378 GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
1379 int access_location = scanner_.GetPosition();
1380 RECURSE(ret = ConditionalExpression());
1381 if (Peek('=')) {
1382 if (!inside_heap_assignment_ ||
1383 access_location != heap_assignment_location_) {
1384 FAIL("Invalid assignment target");
1385 }
1386 inside_heap_assignment_ = false;
1387 AsmType* heap_type = heap_access_type_;
1388 SKIP('=');
1389 AsmType* value;
1390 RECURSE(value = AssignmentExpression());
1391 if (!value->IsA(ret)) {
1392 FAIL("Illegal type stored to heap view");
1393 }
1394 if (heap_type->IsA(AsmType::Float32Array()) &&
1395 value->IsA(AsmType::Double())) {
1396 // Assignment to a float32 heap can be used to convert doubles.
1397 current_function_builder_->Emit(kExprF32ConvertF64);
1398 }
1399 ret = value;
1400 #define V(array_type, wasmload, wasmstore, type) \
1401 if (heap_type->IsA(AsmType::array_type())) { \
1402 current_function_builder_->Emit(kExpr##type##AsmjsStore##wasmstore); \
1403 return ret; \
1404 }
1405 STDLIB_ARRAY_TYPE_LIST(V)
1406 #undef V
1407 }
1408 } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
marja 2017/03/21 13:31:43 For cases like this, if the Scanner no longer know
bradn 2017/03/24 03:58:18 So this was a particularly tricky spot. The || was
1409 VarInfo* info = GetVarInfo(scanner_.Token());
1410 ret = info->type;
1411 scanner_.Next();
1412 if (Peek('=')) {
1413 SKIP('=');
1414 AsmType* value;
1415 RECURSE(value = AssignmentExpression());
1416 if (!value->IsA(ret)) {
1417 FAIL("Type mismatch in assignment");
1418 }
1419 if (info->kind == VarKind::kLocal) {
1420 current_function_builder_->EmitTeeLocal(info->index);
1421 } else if (info->kind == VarKind::kGlobal) {
1422 current_function_builder_->EmitWithVarUint(kExprSetGlobal,
1423 VarIndex(info));
1424 current_function_builder_->EmitWithVarUint(kExprGetGlobal,
1425 VarIndex(info));
1426 } else {
1427 UNREACHABLE();
1428 }
1429 return ret;
1430 }
1431 scanner_.Rewind();
1432 RECURSE(ret = ConditionalExpression());
1433 } else {
1434 RECURSE(ret = ConditionalExpression());
1435 }
1436 return ret;
1437 }
1438
1439 // 6.8.7 UnaryExpression
1440 AsmType* AsmJsParser::UnaryExpression() {
1441 AsmType* ret;
1442 if (Peek('-')) {
1443 SKIP('-');
1444 if (scanner_.IsUnsigned()) {
1445 uint64_t value = scanner_.AsUnsigned();
1446 // TODO(bradnelson): was supposed to be 0x7fffffff, check errata.
1447 if (value <= 0x80000000) {
1448 current_function_builder_->EmitI32Const(-static_cast<int32_t>(value));
1449 scanner_.Next();
1450 } else {
1451 FAIL("Integer numeric literal out of range.");
1452 }
1453 ret = AsmType::Signed();
1454 } else {
1455 RECURSE(ret = UnaryExpression());
1456 if (ret->IsA(AsmType::Int())) {
1457 int32_t tmp = TempVariable(0);
1458 current_function_builder_->EmitSetLocal(tmp);
1459 current_function_builder_->EmitI32Const(0);
1460 current_function_builder_->EmitGetLocal(tmp);
1461 current_function_builder_->Emit(kExprI32Sub);
1462 ret = AsmType::Intish();
1463 } else if (ret->IsA(AsmType::DoubleQ())) {
1464 current_function_builder_->Emit(kExprF64Neg);
1465 ret = AsmType::Double();
1466 } else if (ret->IsA(AsmType::FloatQ())) {
1467 current_function_builder_->Emit(kExprF32Neg);
1468 ret = AsmType::Floatish();
1469 } else {
1470 FAIL("expected int/double?/float?");
1471 }
1472 }
1473 } else if (Peek('+')) {
1474 SKIP('+');
1475 call_coercion_ = AsmType::Double();
1476 RECURSE(ret = UnaryExpression());
1477 // TODO(bradnelson): Generalize.
1478 if (ret->IsA(AsmType::Signed())) {
1479 current_function_builder_->Emit(kExprF64SConvertI32);
1480 ret = AsmType::Double();
1481 } else if (ret->IsA(AsmType::Unsigned())) {
1482 current_function_builder_->Emit(kExprF64UConvertI32);
1483 ret = AsmType::Double();
1484 } else if (ret->IsA(AsmType::DoubleQ())) {
1485 ret = AsmType::Double();
1486 } else if (ret->IsA(AsmType::FloatQ())) {
1487 current_function_builder_->Emit(kExprF64ConvertF32);
1488 ret = AsmType::Double();
1489 } else {
1490 FAIL("expected signed/unsigned/double?/float?");
1491 }
1492 } else if (Peek('!')) {
1493 SKIP('!');
1494 RECURSE(ret = UnaryExpression());
1495 if (!ret->IsA(AsmType::Int())) {
1496 FAIL("expected int");
1497 }
1498 current_function_builder_->Emit(kExprI32Eqz);
1499 } else if (Peek('~')) {
1500 SKIP('~');
1501 if (Peek('~')) {
1502 SKIP('~');
1503 RECURSE(ret = UnaryExpression());
1504 if (ret->IsA(AsmType::Double())) {
1505 current_function_builder_->Emit(kExprI32AsmjsSConvertF64);
1506 } else if (ret->IsA(AsmType::FloatQ())) {
1507 current_function_builder_->Emit(kExprI32AsmjsSConvertF32);
1508 } else {
1509 FAIL("expected double or float?");
1510 }
1511 ret = AsmType::Signed();
1512 } else {
1513 RECURSE(ret = UnaryExpression());
1514 if (!ret->IsA(AsmType::Intish())) {
1515 FAIL("operator ~ expects intish");
1516 }
1517 current_function_builder_->EmitI32Const(0xffffffff);
1518 current_function_builder_->Emit(kExprI32Xor);
1519 ret = AsmType::Signed();
1520 }
1521 } else {
1522 RECURSE(ret = CallExpression());
1523 }
1524 return ret;
1525 }
1526
1527 // 6.8.8 MultaplicativeExpression
1528 AsmType* AsmJsParser::MultiplicativeExpression() {
1529 if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < 0x100000) {
1530 int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
1531 scanner_.Next();
1532 if (Peek('*')) {
1533 SKIP('*');
1534 AsmType* a;
1535 RECURSE(a = UnaryExpression());
1536 if (!a->IsA(AsmType::Int())) {
1537 FAIL("Expected int");
1538 }
1539 current_function_builder_->EmitI32Const(value);
1540 current_function_builder_->Emit(kExprI32Mul);
1541 return AsmType::Intish();
1542 }
1543 scanner_.Rewind();
1544 } else if (Peek('-')) {
1545 SKIP('-');
1546 if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < 0x100000) {
1547 int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
1548 current_function_builder_->EmitI32Const(value);
1549 scanner_.Next();
1550 if (Peek('*')) {
1551 SKIP('*');
1552 AsmType* a;
1553 RECURSE(a = UnaryExpression());
1554 if (!a->IsA(AsmType::Int())) {
1555 FAIL("Expected int");
1556 }
1557 current_function_builder_->Emit(kExprI32Mul);
1558 return AsmType::Intish();
1559 }
1560 return AsmType::Signed();
1561 }
1562 scanner_.Rewind();
1563 }
1564 AsmType* a;
1565 RECURSE(a = UnaryExpression());
1566 for (;;) {
1567 if (Peek('*')) {
1568 SKIP('*');
1569 if (Peek('-')) {
1570 SKIP('-');
1571 if (scanner_.IsUnsigned()) {
1572 if (scanner_.AsUnsigned() >= 0x100000) {
1573 FAIL("Constant multiple out of range");
1574 }
1575 if (!a->IsA(AsmType::Int())) {
1576 FAIL("Integer multiply of expects int");
1577 }
1578 int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
1579 current_function_builder_->EmitI32Const(value);
1580 scanner_.Next();
1581 current_function_builder_->Emit(kExprI32Mul);
1582 return AsmType::Intish();
1583 }
1584 scanner_.Rewind();
1585 } else if (scanner_.IsUnsigned()) {
1586 if (scanner_.AsUnsigned() >= 0x100000) {
1587 FAIL("Constant multiple out of range");
1588 }
1589 if (!a->IsA(AsmType::Int())) {
1590 FAIL("Integer multiply of expects int");
1591 }
1592 current_function_builder_->EmitI32Const(
1593 static_cast<int32_t>(scanner_.AsUnsigned()));
1594 scanner_.Next();
1595 current_function_builder_->Emit(kExprI32Mul);
1596 return AsmType::Intish();
1597 }
1598 AsmType* b = UnaryExpression();
1599 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1600 current_function_builder_->Emit(kExprF64Mul);
1601 a = AsmType::Double();
1602 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1603 current_function_builder_->Emit(kExprF32Mul);
1604 a = AsmType::Floatish();
1605 } else {
1606 FAIL("expected doubles or floats");
1607 }
1608 } else if (Peek('/')) {
1609 SKIP('/');
1610 AsmType* b = MultiplicativeExpression();
1611 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1612 current_function_builder_->Emit(kExprF64Div);
1613 a = AsmType::Double();
1614 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1615 current_function_builder_->Emit(kExprF32Div);
1616 a = AsmType::Floatish();
1617 } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
1618 current_function_builder_->Emit(kExprI32AsmjsDivS);
1619 a = AsmType::Intish();
1620 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
1621 current_function_builder_->Emit(kExprI32AsmjsDivU);
1622 a = AsmType::Intish();
1623 } else {
1624 FAIL("expected doubles or floats");
1625 }
1626 } else if (Peek('%')) {
1627 SKIP('%');
1628 AsmType* b = MultiplicativeExpression();
1629 if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
1630 current_function_builder_->Emit(kExprF64Mod);
1631 a = AsmType::Double();
1632 } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
1633 current_function_builder_->Emit(kExprI32AsmjsRemS);
1634 a = AsmType::Intish();
1635 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
1636 current_function_builder_->Emit(kExprI32AsmjsRemU);
1637 a = AsmType::Intish();
1638 } else {
1639 FAIL("expected doubles or floats");
1640 }
1641 } else {
1642 break;
1643 }
1644 }
1645 return a;
1646 }
1647
1648 // 6.8.9 AdditiveExpression
1649 AsmType* AsmJsParser::AdditiveExpression() {
1650 AsmType* a = MultiplicativeExpression();
1651 int n = 0;
1652 for (;;) {
1653 if (Peek('+')) {
1654 SKIP('+');
1655 AsmType* b = MultiplicativeExpression();
1656 if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
1657 current_function_builder_->Emit(kExprF64Add);
1658 a = AsmType::Double();
1659 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1660 current_function_builder_->Emit(kExprF32Add);
1661 a = AsmType::Floatish();
1662 } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
1663 current_function_builder_->Emit(kExprI32Add);
1664 a = AsmType::Intish();
1665 n = 2;
1666 } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1667 // TODO(bradnelson): b should really only be Int.
1668 // specialize intish to capture count.
1669 ++n;
1670 if (n > (1 << 20)) {
1671 FAIL("more than 2^20 additive values");
1672 }
1673 current_function_builder_->Emit(kExprI32Add);
1674 } else {
1675 FAIL("illegal types for +");
1676 }
1677 } else if (Peek('-')) {
1678 SKIP('-');
1679 AsmType* b = MultiplicativeExpression();
1680 if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
1681 current_function_builder_->Emit(kExprF64Sub);
1682 a = AsmType::Double();
1683 } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
1684 current_function_builder_->Emit(kExprF32Sub);
1685 a = AsmType::Floatish();
1686 } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
1687 current_function_builder_->Emit(kExprI32Sub);
1688 a = AsmType::Intish();
1689 n = 2;
1690 } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1691 // TODO(bradnelson): b should really only be Int.
1692 // specialize intish to capture count.
1693 ++n;
1694 if (n > (1 << 20)) {
1695 FAIL("more than 2^20 additive values");
1696 }
1697 current_function_builder_->Emit(kExprI32Sub);
1698 } else {
1699 FAIL("illegal types for +");
1700 }
1701 } else {
1702 break;
1703 }
1704 }
1705 return a;
1706 }
1707
1708 // 6.8.10 ShiftExpression
1709 AsmType* AsmJsParser::ShiftExpression() {
1710 AsmType* a = nullptr;
1711 RECURSE(a = AdditiveExpression());
1712 for (;;) {
1713 switch (scanner_.Token()) {
1714 // TODO(bradnelson): Implement backtracking to avoid emitting code
1715 // for the x >>> 0 case (similar to what's there for |0).
1716 #define HANDLE_CASE(op, opcode, name, result) \
1717 case TOK(op): { \
1718 SKIP(TOK(op)); \
1719 AsmType* b = nullptr; \
1720 RECURSE(b = AdditiveExpression()); \
1721 if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
1722 FAIL("Expected intish for operator " #name "."); \
1723 } \
1724 current_function_builder_->Emit(kExpr##opcode); \
1725 a = AsmType::result(); \
1726 continue; \
1727 }
1728 HANDLE_CASE(SHL, I32Shl, "<<", Signed);
1729 HANDLE_CASE(SAR, I32ShrS, ">>", Signed);
1730 HANDLE_CASE(SHR, I32ShrU, ">>>", Unsigned);
1731 #undef HANDLE_CASE
1732 default:
1733 return a;
1734 }
1735 }
1736 }
1737
1738 // 6.8.11 RelationalExpression
1739 AsmType* AsmJsParser::RelationalExpression() {
1740 AsmType* a = nullptr;
1741 RECURSE(a = ShiftExpression());
1742 for (;;) {
1743 switch (scanner_.Token()) {
1744 #define HANDLE_CASE(op, sop, uop, dop, fop, name) \
1745 case op: { \
1746 SKIP(op); \
1747 AsmType* b = nullptr; \
1748 RECURSE(b = ShiftExpression()); \
1749 if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
1750 current_function_builder_->Emit(kExpr##sop); \
1751 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
1752 current_function_builder_->Emit(kExpr##uop); \
1753 } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
1754 current_function_builder_->Emit(kExpr##dop); \
1755 } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
1756 current_function_builder_->Emit(kExpr##fop); \
1757 } else { \
1758 FAIL("Expected signed, unsigned, double, or float for operator " #name \
1759 "."); \
1760 } \
1761 a = AsmType::Int(); \
1762 continue; \
1763 }
1764 HANDLE_CASE('<', I32LtS, I32LtU, F64Lt, F32Lt, "<");
1765 HANDLE_CASE(TOK(LE), I32LeS, I32LeU, F64Le, F32Le, "<=");
1766 HANDLE_CASE('>', I32GtS, I32GtU, F64Gt, F32Gt, ">");
1767 HANDLE_CASE(TOK(GE), I32GeS, I32GeU, F64Ge, F32Ge, ">=");
1768 #undef HANDLE_CASE
1769 default:
1770 return a;
1771 }
1772 }
1773 }
1774
1775 // 6.8.12 EqualityExpression
1776 AsmType* AsmJsParser::EqualityExpression() {
1777 AsmType* a = nullptr;
1778 RECURSE(a = RelationalExpression());
1779 for (;;) {
1780 switch (scanner_.Token()) {
1781 #define HANDLE_CASE(op, sop, uop, dop, fop, name) \
1782 case op: { \
1783 SKIP(op); \
1784 AsmType* b = nullptr; \
1785 RECURSE(b = RelationalExpression()); \
1786 if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
1787 current_function_builder_->Emit(kExpr##sop); \
1788 } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
1789 current_function_builder_->Emit(kExpr##uop); \
1790 } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
1791 current_function_builder_->Emit(kExpr##dop); \
1792 } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
1793 current_function_builder_->Emit(kExpr##fop); \
1794 } else { \
1795 FAIL("Expected signed, unsigned, double, or float for operator " #name \
1796 "."); \
1797 } \
1798 a = AsmType::Int(); \
1799 continue; \
1800 }
1801 HANDLE_CASE(TOK(EQ), I32Eq, I32Eq, F64Eq, F32Eq, "==");
1802 HANDLE_CASE(TOK(NE), I32Ne, I32Ne, F64Ne, F32Ne, "!=");
1803 #undef HANDLE_CASE
1804 default:
1805 return a;
1806 }
1807 }
1808 }
1809
1810 // 6.8.13 BitwiseANDExpression
1811 AsmType* AsmJsParser::BitwiseANDExpression() {
1812 AsmType* a = nullptr;
1813 RECURSE(a = EqualityExpression());
1814 while (Peek('&')) {
1815 SKIP('&');
1816 AsmType* b = nullptr;
1817 RECURSE(b = EqualityExpression());
1818 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1819 current_function_builder_->Emit(kExprI32And);
1820 a = AsmType::Signed();
1821 } else {
1822 FAIL("Expected intish for operator &.");
1823 }
1824 }
1825 return a;
1826 }
1827
1828 // 6.8.14 BitwiseXORExpression
1829 AsmType* AsmJsParser::BitwiseXORExpression() {
1830 AsmType* a = nullptr;
1831 RECURSE(a = BitwiseANDExpression());
1832 while (Peek('^')) {
1833 SKIP('^');
1834 AsmType* b = nullptr;
1835 RECURSE(b = BitwiseANDExpression());
1836 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1837 current_function_builder_->Emit(kExprI32Xor);
1838 a = AsmType::Signed();
1839 } else {
1840 FAIL("Expected intish for operator &.");
1841 }
1842 }
1843 return a;
1844 }
1845
1846 // 6.8.15 BitwiseORExpression
1847 AsmType* AsmJsParser::BitwiseORExpression() {
1848 AsmType* a = nullptr;
1849 RECURSE(a = BitwiseXORExpression());
1850 // }
1851 while (Peek('|')) {
1852 SKIP('|');
1853 // TODO(bradnelson): Make it prettier.
1854 AsmType* b = nullptr;
1855 bool zero = false;
1856 int old_pos;
1857 size_t old_code;
1858 if (scanner_.IsUnsigned() && scanner_.AsUnsigned() == 0) {
1859 scanner_.Next();
1860 old_pos = scanner_.GetPosition();
1861 old_code = current_function_builder_->GetPosition();
1862 scanner_.Rewind();
1863 zero = true;
1864 }
1865 RECURSE(b = BitwiseXORExpression());
1866 // Handle |0 specially.
1867 if (old_pos == scanner_.GetPosition()) {
1868 current_function_builder_->StashCode(nullptr, old_code);
1869 a = AsmType::Signed();
1870 continue;
1871 }
1872 if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
1873 current_function_builder_->Emit(kExprI32Ior);
1874 a = AsmType::Signed();
1875 } else {
1876 FAIL("Expected intish for operator |.");
1877 }
1878 }
1879 return a;
1880 }
1881
1882 // 6.8.16 ConditionalExpression
1883 AsmType* AsmJsParser::ConditionalExpression() {
1884 AsmType* test = nullptr;
1885 RECURSE(test = BitwiseORExpression());
1886 if (Peek('?')) {
1887 SKIP('?');
1888 if (!test->IsA(AsmType::Int())) {
1889 FAIL("Expected int in condition of ternary operator.");
1890 }
1891 current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
1892 size_t fixup = current_function_builder_->GetPosition() -
1893 1; // Assumes encoding knowledge.
1894 AsmType* cons = nullptr;
1895 RECURSE(cons = AssignmentExpression());
1896 current_function_builder_->Emit(kExprElse);
1897 SKIP(':');
1898 AsmType* alt = nullptr;
1899 RECURSE(alt = AssignmentExpression());
1900 current_function_builder_->Emit(kExprEnd);
1901 if (cons->IsA(AsmType::Int()) && alt->IsA(AsmType::Int())) {
1902 current_function_builder_->FixupByte(fixup, kLocalI32);
1903 return AsmType::Int();
1904 } else if (cons->IsA(AsmType::Double()) && alt->IsA(AsmType::Double())) {
1905 current_function_builder_->FixupByte(fixup, kLocalF64);
1906 return AsmType::Double();
1907 } else if (cons->IsA(AsmType::Float()) && alt->IsA(AsmType::Float())) {
1908 current_function_builder_->FixupByte(fixup, kLocalF32);
1909 return AsmType::Float();
1910 } else {
1911 FAIL("Type mismatch in ternary operator.");
1912 }
1913 } else {
1914 return test;
1915 }
1916 }
1917
1918 // 6.8.17 ParenthesiedExpression
1919 AsmType* AsmJsParser::ParenthesizedExpression() {
1920 call_coercion_ = nullptr;
1921 AsmType* ret;
1922 SKIP('(');
1923 RECURSE(ret = Expression(nullptr));
1924 SKIP(')');
1925 return ret;
1926 }
1927
1928 // 6.9 ValidateCall
1929 AsmType* AsmJsParser::ValidateCall() {
1930 AsmType* return_type = call_coercion_;
1931 call_coercion_ = nullptr;
1932 AsmJsScanner::token_t function_name = Consume();
1933 int32_t tmp = TempVariable(0);
1934 if (Peek('[')) {
1935 SKIP('[');
1936 RECURSE(EqualityExpression());
1937 SKIP('&');
1938 if (!scanner_.IsUnsigned()) {
1939 FAIL("Expected mask literal");
1940 }
1941 uint64_t value = scanner_.AsUnsigned();
1942 if (value > 0x7fffffff) {
1943 FAIL("Expected power of 2 mask");
1944 }
1945 if (!base::bits::IsPowerOfTwo32(static_cast<uint32_t>(1 + value))) {
1946 FAIL("Expected power of 2 mask");
1947 }
1948 scanner_.Next();
1949 current_function_builder_->EmitI32Const(static_cast<uint32_t>(value));
1950 current_function_builder_->Emit(kExprI32And);
1951 SKIP(']');
1952 VarInfo* function_info = GetVarInfo(function_name);
1953 if (function_info->kind == VarKind::kUnused) {
1954 function_info->kind = VarKind::kTable;
1955 function_info->mask = value;
1956 function_info->index =
1957 builder_->AllocateIndirectFunctions(static_cast<uint32_t>(value + 1));
1958 } else {
1959 if (function_info->kind != VarKind::kTable) {
1960 FAIL("Expected call table");
1961 }
1962 if (function_info->mask != value) {
1963 FAIL("Mask size mismatch");
1964 }
1965 }
1966 current_function_builder_->EmitI32Const(function_info->index);
1967 current_function_builder_->Emit(kExprI32Add);
1968 // We have to use a temporary for the correct order of evaluation.
1969 current_function_builder_->EmitSetLocal(tmp);
1970 }
1971 std::vector<AsmType*> param_types;
1972 ZoneVector<AsmType*> param_specific_types(zone());
1973 SKIP('(');
1974 while (!failed_ && !Peek(')')) {
1975 AsmType* t;
1976 RECURSE(t = AssignmentExpression());
1977 param_specific_types.push_back(t);
1978 if (t->IsA(AsmType::Int())) {
1979 param_types.push_back(AsmType::Int());
1980 } else if (t->IsA(AsmType::Float())) {
1981 param_types.push_back(AsmType::Float());
1982 } else if (t->IsA(AsmType::Double())) {
1983 param_types.push_back(AsmType::Double());
1984 } else {
1985 std::string a = t->Name();
1986 FAIL("Bad function argument type");
1987 }
1988 if (!Peek(')')) {
1989 SKIP(',');
1990 }
1991 }
1992 SKIP(')');
1993 // TODO(bradnelson): clarify how this binds, and why only float?
1994 if (Peek('|') &&
1995 (return_type == nullptr || return_type->IsA(AsmType::Float()))) {
1996 return_type = AsmType::Signed();
1997 } else if (return_type == nullptr) {
1998 return_type = AsmType::Void();
1999 }
2000 AsmType* function_type = AsmType::Function(zone(), return_type);
2001 for (auto t : param_types) {
2002 function_type->AsFunctionType()->AddArgument(t);
2003 }
2004
2005 FunctionSig* sig = ConvertSignature(return_type, param_types);
2006 if (sig == nullptr) {
2007 FAIL("Invalid function signature");
2008 }
2009 uint32_t signature_index = builder_->AddSignature(sig);
2010
2011 // TODO(bradnelson): Fix this to use a less error prone pattern.
2012 // Reload as table might have grown.
2013 VarInfo* function_info = GetVarInfo(function_name);
2014 if (function_info->kind == VarKind::kUnused) {
2015 function_info->kind = VarKind::kFunction;
2016 function_info->function_builder = builder_->AddFunction();
2017 function_info->index = function_info->function_builder->func_index();
2018 function_info->type = function_type;
2019 // TODO(bradnelson): Figure out the right debug scanner offset and
2020 // re-enable.
2021 // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
2022 // scanner_.GetPosition());
2023 current_function_builder_->Emit(kExprCallFunction);
2024 current_function_builder_->EmitDirectCallIndex(function_info->index);
2025 } else if (function_info->kind == VarKind::kImportedFunction) {
2026 for (auto t : param_specific_types) {
2027 if (!t->IsA(AsmType::Extern())) {
2028 FAIL("Imported function args must be type extern");
2029 }
2030 }
2031 if (return_type->IsA(AsmType::Float())) {
2032 FAIL("Imported function can't be called as float");
2033 }
2034 DCHECK(function_info->import != nullptr);
2035 // TODO(bradnelson): Factor out.
2036 uint32_t cache_index = function_info->import->cache.FindOrInsert(sig);
2037 uint32_t index;
2038 if (cache_index >= function_info->import->cache_index.size()) {
2039 index = builder_->AddImport(
2040 function_info->import->name.data(),
2041 static_cast<uint32_t>(function_info->import->name.size()), sig);
2042 function_info->import->cache_index.push_back(index);
2043 } else {
2044 index = function_info->import->cache_index[cache_index];
2045 }
2046 current_function_builder_->Emit(kExprCallFunction);
2047 current_function_builder_->EmitVarUint(index);
2048 } else if (function_info->type->IsA(AsmType::None())) {
2049 function_info->type = function_type;
2050 if (function_info->kind == VarKind::kTable) {
2051 current_function_builder_->EmitGetLocal(tmp);
2052 // TODO(bradnelson): Figure out the right debug scanner offset and
2053 // re-enable.
2054 // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition() ,
2055 // scanner_.GetPosition() );
2056 current_function_builder_->Emit(kExprCallIndirect);
2057 current_function_builder_->EmitVarUint(signature_index);
2058 current_function_builder_->EmitVarUint(0); // table index
2059 } else {
2060 // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition() ,
2061 // scanner_.GetPosition() );
2062 current_function_builder_->Emit(kExprCallFunction);
2063 current_function_builder_->EmitDirectCallIndex(function_info->index);
2064 }
2065 } else if (function_info->kind > VarKind::kImportedFunction) {
2066 AsmCallableType* callable = function_info->type->AsCallableType();
2067 if (!callable) {
2068 FAIL("Expected callable function");
2069 }
2070 // TODO(bradnelson): Refactor AsmType to not need this.
2071 if (callable->CanBeInvokedWith(return_type, param_specific_types)) {
2072 // Return type ok.
2073 } else if (return_type->IsA(AsmType::Void()) &&
2074 callable->CanBeInvokedWith(AsmType::Float(),
2075 param_specific_types)) {
2076 return_type = AsmType::Float();
2077 } else if (return_type->IsA(AsmType::Void()) &&
2078 callable->CanBeInvokedWith(AsmType::Double(),
2079 param_specific_types)) {
2080 return_type = AsmType::Double();
2081 } else if (return_type->IsA(AsmType::Void()) &&
2082 callable->CanBeInvokedWith(AsmType::Signed(),
2083 param_specific_types)) {
2084 return_type = AsmType::Signed();
2085 } else {
2086 FAIL("Function use doesn't match definition");
2087 }
2088 switch (function_info->kind) {
2089 #define V(name, Name, op, sig) \
2090 case VarKind::kMath##Name: \
2091 current_function_builder_->Emit(op); \
2092 break;
2093 STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V)
2094 #undef V
2095 #define V(name, Name, op, sig) \
2096 case VarKind::kMath##Name: \
2097 if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { \
2098 current_function_builder_->Emit(kExprF64##Name); \
2099 } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { \
2100 current_function_builder_->Emit(kExprF32##Name); \
2101 } else { \
2102 UNREACHABLE(); \
2103 } \
2104 break;
2105 STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
2106 #undef V
2107 case VarKind::kMathMin:
2108 case VarKind::kMathMax:
2109 if (param_specific_types[0]->IsA(AsmType::Double())) {
2110 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2111 if (function_info->kind == VarKind::kMathMin) {
2112 current_function_builder_->Emit(kExprF64Min);
2113 } else {
2114 current_function_builder_->Emit(kExprF64Max);
2115 }
2116 }
2117 } else if (param_specific_types[0]->IsA(AsmType::Float())) {
2118 // NOTE: Not technically part of the asm.js spec, but Firefox
2119 // accepts it.
2120 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2121 if (function_info->kind == VarKind::kMathMin) {
2122 current_function_builder_->Emit(kExprF32Min);
2123 } else {
2124 current_function_builder_->Emit(kExprF32Max);
2125 }
2126 }
2127 } else if (param_specific_types[0]->IsA(AsmType::Int())) {
2128 int32_t tmp_x = TempVariable(0);
2129 int32_t tmp_y = TempVariable(1);
2130 for (size_t i = 1; i < param_specific_types.size(); ++i) {
2131 current_function_builder_->EmitSetLocal(tmp_x);
2132 current_function_builder_->EmitTeeLocal(tmp_y);
2133 current_function_builder_->EmitGetLocal(tmp_x);
2134 if (function_info->kind == VarKind::kMathMin) {
2135 current_function_builder_->Emit(kExprI32GeS);
2136 } else {
2137 current_function_builder_->Emit(kExprI32LeS);
2138 }
2139 current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
2140 current_function_builder_->EmitGetLocal(tmp_x);
2141 current_function_builder_->Emit(kExprElse);
2142 current_function_builder_->EmitGetLocal(tmp_y);
2143 current_function_builder_->Emit(kExprEnd);
2144 }
2145 } else {
2146 UNREACHABLE();
2147 }
2148 break;
2149
2150 case VarKind::kMathAbs:
2151 if (param_specific_types[0]->IsA(AsmType::Signed())) {
2152 int32_t tmp = TempVariable(0);
2153 current_function_builder_->EmitTeeLocal(tmp);
2154 current_function_builder_->Emit(kExprI32Clz);
2155 current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
2156 current_function_builder_->EmitGetLocal(tmp);
2157 current_function_builder_->Emit(kExprElse);
2158 current_function_builder_->EmitI32Const(0);
2159 current_function_builder_->EmitGetLocal(tmp);
2160 current_function_builder_->Emit(kExprI32Sub);
2161 current_function_builder_->Emit(kExprEnd);
2162 } else if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
2163 current_function_builder_->Emit(kExprF64Abs);
2164 } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) {
2165 current_function_builder_->Emit(kExprF32Abs);
2166 } else {
2167 UNREACHABLE();
2168 }
2169 break;
2170
2171 case VarKind::kMathFround:
2172 if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
2173 current_function_builder_->Emit(kExprF32ConvertF64);
2174 } else {
2175 DCHECK(param_specific_types[0]->IsA(AsmType::FloatQ()));
2176 }
2177 break;
2178
2179 default:
2180 UNREACHABLE();
2181 }
2182 } else {
2183 if (function_info->kind != VarKind::kFunction &&
2184 function_info->kind != VarKind::kTable) {
2185 FAIL("Function name collides with variable");
2186 }
2187 AsmCallableType* callable = function_info->type->AsCallableType();
2188 if (!callable ||
2189 !callable->CanBeInvokedWith(return_type, param_specific_types)) {
2190 FAIL("Function use doesn't match definition");
2191 }
2192 if (function_info->kind == VarKind::kTable) {
2193 current_function_builder_->EmitGetLocal(tmp);
2194 // TODO(bradnelson): Figure out the right debug scanner offset and
2195 // re-enable.
2196 // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition() ,
2197 // scanner_.GetPosition() );
2198 current_function_builder_->Emit(kExprCallIndirect);
2199 current_function_builder_->EmitVarUint(signature_index);
2200 current_function_builder_->EmitVarUint(0); // table index
2201 } else {
2202 // TODO(bradnelson): Figure out the right debug scanner offset and
2203 // re-enable.
2204 // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition() ,
2205 // scanner_.GetPosition() );
2206 current_function_builder_->Emit(kExprCallFunction);
2207 current_function_builder_->EmitDirectCallIndex(function_info->index);
2208 }
2209 }
2210
2211 return return_type;
2212 }
2213
2214 #undef FAILURE_RETURN
2215 #define FAILURE_RETURN
2216
2217 // 6.9 ValidateCall - helper
2218 bool AsmJsParser::PeekCall() {
2219 if (!scanner_.IsGlobal()) {
2220 return false;
2221 }
2222 if (GetVarInfo(scanner_.Token())->kind == VarKind::kFunction) {
2223 return true;
2224 }
2225 if (GetVarInfo(scanner_.Token())->kind >= VarKind::kImportedFunction) {
2226 return true;
2227 }
2228 if (GetVarInfo(scanner_.Token())->kind == VarKind::kUnused ||
2229 GetVarInfo(scanner_.Token())->kind == VarKind::kTable) {
2230 scanner_.Next();
2231 if (Peek('(') || Peek('[')) {
2232 scanner_.Rewind();
2233 return true;
2234 }
2235 scanner_.Rewind();
2236 }
2237 return false;
2238 }
2239
2240 // 6.10 ValidateHeapAccess
2241 void AsmJsParser::ValidateHeapAccess() {
2242 VarInfo* info = GetVarInfo(Consume());
2243 int32_t size = info->type->ElementSizeInBytes();
2244 SKIP('[');
2245 if (scanner_.IsUnsigned()) {
2246 // TODO(bradnelson): Check more things.
2247 uint64_t offset = scanner_.AsUnsigned();
2248 if (offset > 0x7fffffff || offset * size > 0x7fffffff) {
2249 FAIL("Heap access out of range");
2250 }
2251 scanner_.Next();
2252 if (Peek(']')) {
2253 SKIP(']');
2254 current_function_builder_->EmitI32Const(
2255 static_cast<uint32_t>(offset * size));
2256 // NOTE: This has to happen here to work recursively.
2257 heap_access_type_ = info->type;
2258 return;
2259 } else {
2260 scanner_.Rewind();
2261 }
2262 }
2263 AsmType* index_type;
2264 if (info->type->IsA(AsmType::Int8Array()) ||
2265 info->type->IsA(AsmType::Uint8Array())) {
2266 RECURSE(index_type = Expression(nullptr));
2267 } else {
2268 RECURSE(index_type = AdditiveExpression());
2269 SKIP(TOK(SAR));
2270 if (!scanner_.IsUnsigned()) {
2271 FAIL("Expected shift of word size");
2272 }
2273 if (scanner_.AsUnsigned() > 3) {
2274 FAIL("Expected valid heap access shift");
2275 }
2276 if ((1 << scanner_.AsUnsigned()) != size) {
2277 FAIL("Expected heap access shift to match heap view");
2278 }
2279 scanner_.Next();
2280 // Mask bottom bits to match asm.js behavior.
2281 current_function_builder_->EmitI32Const(~(size - 1));
2282 current_function_builder_->Emit(kExprI32And);
2283 }
2284 if (!index_type->IsA(AsmType::Intish())) {
2285 FAIL("Expected intish index");
2286 }
2287 SKIP(']');
2288 // NOTE: This has to happen here to work recursively.
2289 heap_access_type_ = info->type;
2290 }
2291
2292 // 6.11 ValidateFloatCoercion
2293 void AsmJsParser::ValidateFloatCoercion() {
2294 if (!scanner_.IsGlobal() ||
2295 !GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
2296 FAIL("Expected fround");
2297 }
2298 scanner_.Next();
2299 SKIP('(');
2300 call_coercion_ = AsmType::Float();
2301 AsmType* ret;
2302 RECURSE(ret = ValidateExpression());
2303 if (ret->IsA(AsmType::Floatish())) {
2304 // Do nothing, as already a float.
2305 } else if (ret->IsA(AsmType::DoubleQ())) {
2306 current_function_builder_->Emit(kExprF32ConvertF64);
2307 } else if (ret->IsA(AsmType::Signed())) {
2308 current_function_builder_->Emit(kExprF32SConvertI32);
2309 } else if (ret->IsA(AsmType::Unsigned())) {
2310 current_function_builder_->Emit(kExprF32UConvertI32);
2311 } else {
2312 FAIL("Illegal conversion to float");
2313 }
2314 SKIP(')');
2315 }
2316
2317 void AsmJsParser::GatherCases(std::vector<int32_t>* cases) {
2318 int start = scanner_.GetPosition();
2319 int depth = 0;
2320 for (;;) {
2321 if (Peek('{')) {
2322 ++depth;
2323 } else if (Peek('}')) {
2324 --depth;
2325 if (depth <= 0) {
2326 break;
2327 }
2328 } else if (depth == 1 && Peek(TOK(case))) {
2329 scanner_.Next();
2330 int32_t value;
2331 if (Peek('-')) {
vogelheim 2017/03/17 18:01:29 This whole thing - in addition to being generally
bradn 2017/03/24 03:58:19 Not quite following your meaning. This probably do
2332 SKIP('-');
2333 if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() > 0x80000000) {
2334 break;
2335 }
2336 value = -static_cast<int32_t>(scanner_.AsUnsigned());
2337 } else {
2338 if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() > 0x7fffffff) {
2339 break;
2340 }
2341 value = static_cast<int32_t>(scanner_.AsUnsigned());
2342 }
2343 cases->push_back(value);
2344 } else if (Peek(AsmJsScanner::kEndOfInput)) {
2345 break;
2346 }
2347 scanner_.Next();
2348 }
2349 scanner_.Seek(start);
2350 }
2351
2352 } // namespace wasm
2353 } // namespace internal
2354 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698