Chromium Code Reviews| 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 |