OLD | NEW |
---|---|
(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 | |
OLD | NEW |