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