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. |
| 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) |
| 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) \ |
| 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) \ |
| 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_); \ |
| 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 |
| 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) { |
| 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) { |
| 314 continue; |
| 315 } |
| 316 if (label == 0 || it->label == label) { |
| 317 return count; |
| 318 } |
| 319 } |
| 320 return -1; |
| 321 } |
| 322 |
| 323 // 6.1 ValidateModule |
| 324 void AsmJsParser::ValidateModule() { |
| 325 RECURSE(ValidateModuleParameters()); |
| 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) { |
| 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()) { |
| 416 DeclareGlobal(info, mut, AsmType::Double(), kWasmF64, |
| 417 WasmInitExpr(scanner_.AsDouble())); |
| 418 scanner_.Next(); |
| 419 } else if (scanner_.IsUnsigned()) { |
| 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) { |
| 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 } |
| 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('{')) { |
| 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"); |
| 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) { |
| 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) { |
| 664 function_info->kind = VarKind::kFunction; |
| 665 function_info->function_builder = builder_->AddFunction(); |
| 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(); |
| 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(); |
| 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(); |
| 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 } |
| 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; |
| 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(); |
| 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())) { |
| 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_); |
| 1054 pending_label_ = 0; |
| 1055 SKIP(TOK(while)); |
| 1056 SKIP('('); |
| 1057 RECURSE(Expression(AsmType::Int())); |
| 1058 SKIP(')'); |
| 1059 current_function_builder_->Emit(kExprI32Eqz); |
| 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); |
| 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()) { |
| 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); |
| 1149 } |
| 1150 if (depth < 0) { |
| 1151 FAIL("Illegal continue"); |
| 1152 } |
| 1153 current_function_builder_->Emit(kExprBr); |
| 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. |
| 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) || |
| 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) { |
| 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; |
| 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()) { |
| 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('-')) { |
| 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 |