| Index: src/asmjs/asm-parser.cc
|
| diff --git a/src/asmjs/asm-parser.cc b/src/asmjs/asm-parser.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..59745c191de58d175f74820421d266403568142e
|
| --- /dev/null
|
| +++ b/src/asmjs/asm-parser.cc
|
| @@ -0,0 +1,2375 @@
|
| +// Copyright 2017 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/asmjs/asm-parser.h"
|
| +
|
| +// Required to get M_E etc. for MSVC.
|
| +// References from STDLIB_MATH_VALUE_LIST in asm-names.h
|
| +#if defined(_WIN32)
|
| +#define _USE_MATH_DEFINES
|
| +#endif
|
| +#include <math.h>
|
| +#include <string.h>
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "src/asmjs/asm-types.h"
|
| +#include "src/objects-inl.h"
|
| +#include "src/objects.h"
|
| +#include "src/parsing/scanner-character-streams.h"
|
| +#include "src/wasm/wasm-macro-gen.h"
|
| +#include "src/wasm/wasm-opcodes.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace wasm {
|
| +
|
| +#ifdef DEBUG
|
| +#define FAIL_AND_RETURN(ret, msg) \
|
| + failed_ = true; \
|
| + failure_message_ = std::string(msg) + \
|
| + " token: " + scanner_.Name(scanner_.Token()) + \
|
| + " see: " + __FILE__ + ":" + std::to_string(__LINE__); \
|
| + failure_location_ = scanner_.GetPosition(); \
|
| + return ret;
|
| +#else
|
| +#define FAIL_AND_RETURN(ret, msg) \
|
| + failed_ = true; \
|
| + failure_message_ = msg; \
|
| + failure_location_ = scanner_.GetPosition(); \
|
| + return ret;
|
| +#endif
|
| +
|
| +#define FAIL(msg) FAIL_AND_RETURN(, msg)
|
| +#define FAILn(msg) FAIL_AND_RETURN(nullptr, msg)
|
| +#define FAILf(msg) FAIL_AND_RETURN(false, msg)
|
| +
|
| +#ifdef DEBUG
|
| +#define EXPECT_TOKEN_OR_RETURN(ret, token) \
|
| + do { \
|
| + if (scanner_.Token() != token) { \
|
| + FAIL_AND_RETURN(ret, std::string("expected token ") + \
|
| + scanner_.Name(token) + " but found " + \
|
| + scanner_.Name(scanner_.Token())); \
|
| + } \
|
| + scanner_.Next(); \
|
| + } while (false);
|
| +#else
|
| +#define EXPECT_TOKEN_OR_RETURN(ret, token) \
|
| + do { \
|
| + if (scanner_.Token() != token) { \
|
| + FAIL_AND_RETURN(ret, "unexpected token"); \
|
| + } \
|
| + scanner_.Next(); \
|
| + } while (false);
|
| +#endif
|
| +
|
| +#define EXPECT_TOKEN(token) EXPECT_TOKEN_OR_RETURN(, token)
|
| +#define EXPECT_TOKENn(token) EXPECT_TOKEN_OR_RETURN(nullptr, token)
|
| +#define EXPECT_TOKENf(token) EXPECT_TOKEN_OR_RETURN(false, token)
|
| +
|
| +#define RECURSE_OR_RETURN(ret, call) \
|
| + do { \
|
| + DCHECK(GetCurrentStackPosition() >= stack_limit_); \
|
| + DCHECK(!failed_); \
|
| + call; \
|
| + if (GetCurrentStackPosition() < stack_limit_) { \
|
| + FAIL_AND_RETURN(ret, "Stack overflow while parsing asm.js module."); \
|
| + } \
|
| + if (failed_) return ret; \
|
| + } while (false);
|
| +
|
| +#define RECURSE(call) RECURSE_OR_RETURN(, call)
|
| +#define RECURSEn(call) RECURSE_OR_RETURN(nullptr, call)
|
| +#define RECURSEf(call) RECURSE_OR_RETURN(false, call)
|
| +
|
| +#define TOK(name) AsmJsScanner::kToken_##name
|
| +
|
| +AsmJsParser::AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
|
| + int start, int end)
|
| + : zone_(zone),
|
| + module_builder_(new (zone) WasmModuleBuilder(zone)),
|
| + return_type_(nullptr),
|
| + stack_limit_(isolate->stack_guard()->real_climit()),
|
| + global_var_info_(zone),
|
| + local_var_info_(zone),
|
| + failed_(false),
|
| + failure_location_(start),
|
| + stdlib_name_(kTokenNone),
|
| + foreign_name_(kTokenNone),
|
| + heap_name_(kTokenNone),
|
| + inside_heap_assignment_(false),
|
| + heap_access_type_(nullptr),
|
| + block_stack_(zone),
|
| + pending_label_(0),
|
| + global_imports_(zone) {
|
| + InitializeStdlibTypes();
|
| + Handle<String> source(String::cast(script->source()), isolate);
|
| + std::unique_ptr<Utf16CharacterStream> stream(
|
| + ScannerStream::For(source, start, end));
|
| + scanner_.SetStream(std::move(stream));
|
| +}
|
| +
|
| +void AsmJsParser::InitializeStdlibTypes() {
|
| + auto* d = AsmType::Double();
|
| + auto* dq = AsmType::DoubleQ();
|
| + stdlib_dq2d_ = AsmType::Function(zone(), d);
|
| + stdlib_dq2d_->AsFunctionType()->AddArgument(dq);
|
| +
|
| + stdlib_dqdq2d_ = AsmType::Function(zone(), d);
|
| + stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
|
| + stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
|
| +
|
| + auto* f = AsmType::Float();
|
| + auto* fq = AsmType::FloatQ();
|
| + stdlib_fq2f_ = AsmType::Function(zone(), f);
|
| + stdlib_fq2f_->AsFunctionType()->AddArgument(fq);
|
| +
|
| + auto* s = AsmType::Signed();
|
| + auto* s2s = AsmType::Function(zone(), s);
|
| + s2s->AsFunctionType()->AddArgument(s);
|
| +
|
| + auto* i = AsmType::Int();
|
| + stdlib_i2s_ = AsmType::Function(zone_, s);
|
| + stdlib_i2s_->AsFunctionType()->AddArgument(i);
|
| +
|
| + stdlib_ii2s_ = AsmType::Function(zone(), s);
|
| + stdlib_ii2s_->AsFunctionType()->AddArgument(i);
|
| + stdlib_ii2s_->AsFunctionType()->AddArgument(i);
|
| +
|
| + auto* minmax_d = AsmType::MinMaxType(zone(), d, d);
|
| + // *VIOLATION* The float variant is not part of the spec, but firefox accepts
|
| + // it.
|
| + auto* minmax_f = AsmType::MinMaxType(zone(), f, f);
|
| + auto* minmax_i = AsmType::MinMaxType(zone(), s, i);
|
| + stdlib_minmax_ = AsmType::OverloadedFunction(zone());
|
| + stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_i);
|
| + stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f);
|
| + stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d);
|
| +
|
| + stdlib_abs_ = AsmType::OverloadedFunction(zone());
|
| + stdlib_abs_->AsOverloadedFunctionType()->AddOverload(s2s);
|
| + stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
|
| + stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
|
| +
|
| + stdlib_ceil_like_ = AsmType::OverloadedFunction(zone());
|
| + stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
|
| + stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
|
| +
|
| + stdlib_fround_ = AsmType::FroundType(zone());
|
| +}
|
| +
|
| +FunctionSig* AsmJsParser::ConvertSignature(
|
| + AsmType* return_type, const std::vector<AsmType*>& params) {
|
| + FunctionSig::Builder sig_builder(
|
| + zone(), !return_type->IsA(AsmType::Void()) ? 1 : 0, params.size());
|
| + for (auto param : params) {
|
| + if (param->IsA(AsmType::Double())) {
|
| + sig_builder.AddParam(kWasmF64);
|
| + } else if (param->IsA(AsmType::Float())) {
|
| + sig_builder.AddParam(kWasmF32);
|
| + } else if (param->IsA(AsmType::Int())) {
|
| + sig_builder.AddParam(kWasmI32);
|
| + } else {
|
| + return nullptr;
|
| + }
|
| + }
|
| + if (!return_type->IsA(AsmType::Void())) {
|
| + if (return_type->IsA(AsmType::Double())) {
|
| + sig_builder.AddReturn(kWasmF64);
|
| + } else if (return_type->IsA(AsmType::Float())) {
|
| + sig_builder.AddReturn(kWasmF32);
|
| + } else if (return_type->IsA(AsmType::Signed())) {
|
| + sig_builder.AddReturn(kWasmI32);
|
| + } else {
|
| + return 0;
|
| + }
|
| + }
|
| + return sig_builder.Build();
|
| +}
|
| +
|
| +bool AsmJsParser::Run() {
|
| + ValidateModule();
|
| + return !failed_;
|
| +}
|
| +
|
| +AsmJsParser::VarInfo::VarInfo()
|
| + : type(AsmType::None()),
|
| + function_builder(nullptr),
|
| + import(nullptr),
|
| + mask(-1),
|
| + index(0),
|
| + kind(VarKind::kUnused),
|
| + mutable_variable(true),
|
| + function_defined(false) {}
|
| +
|
| +wasm::AsmJsParser::VarInfo* AsmJsParser::GetVarInfo(
|
| + AsmJsScanner::token_t token) {
|
| + if (AsmJsScanner::IsGlobal(token)) {
|
| + size_t old = global_var_info_.size();
|
| + size_t index = AsmJsScanner::GlobalIndex(token);
|
| + size_t sz = std::max(old, index + 1);
|
| + if (sz != old) {
|
| + global_var_info_.resize(sz);
|
| + }
|
| + return &global_var_info_[index];
|
| + } else if (AsmJsScanner::IsLocal(token)) {
|
| + size_t old = local_var_info_.size();
|
| + size_t index = AsmJsScanner::LocalIndex(token);
|
| + size_t sz = std::max(old, index + 1);
|
| + if (sz != old) {
|
| + local_var_info_.resize(sz);
|
| + }
|
| + return &local_var_info_[index];
|
| + }
|
| + UNREACHABLE();
|
| + return nullptr;
|
| +}
|
| +
|
| +uint32_t AsmJsParser::VarIndex(VarInfo* info) {
|
| + if (info->import != nullptr) {
|
| + return info->index;
|
| + } else {
|
| + return info->index + static_cast<uint32_t>(global_imports_.size());
|
| + }
|
| +}
|
| +
|
| +void AsmJsParser::AddGlobalImport(std::string name, AsmType* type,
|
| + ValueType vtype, bool mutable_variable,
|
| + VarInfo* info) {
|
| + if (mutable_variable) {
|
| + // Allocate a separate variable for the import.
|
| + DeclareGlobal(info, true, type, vtype);
|
| + // Record the need to initialize the global from the import.
|
| + global_imports_.push_back({name, 0, info->index, true});
|
| + } else {
|
| + // Just use the import directly.
|
| + global_imports_.push_back({name, 0, info->index, false});
|
| + }
|
| + GlobalImport& gi = global_imports_.back();
|
| + // TODO(bradnelson): Reuse parse buffer memory / make wasm-module-builder
|
| + // managed the memory for the import name (currently have to keep our
|
| + // own memory for it).
|
| + gi.import_index = module_builder_->AddGlobalImport(
|
| + name.data(), static_cast<int>(name.size()), vtype);
|
| + if (!mutable_variable) {
|
| + info->DeclareGlobalImport(type, gi.import_index);
|
| + }
|
| +}
|
| +
|
| +void AsmJsParser::VarInfo::DeclareGlobalImport(AsmType* type, uint32_t index) {
|
| + kind = VarKind::kGlobal;
|
| + this->type = type;
|
| + this->index = index;
|
| + mutable_variable = false;
|
| +}
|
| +
|
| +void AsmJsParser::VarInfo::DeclareStdlibFunc(VarKind kind, AsmType* type) {
|
| + this->kind = kind;
|
| + this->type = type;
|
| + index = 0; // unused
|
| + mutable_variable = false;
|
| +}
|
| +
|
| +void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
|
| + AsmType* type, ValueType vtype,
|
| + const WasmInitExpr& init) {
|
| + info->kind = VarKind::kGlobal;
|
| + info->type = type;
|
| + info->index = module_builder_->AddGlobal(vtype, false, true, init);
|
| + info->mutable_variable = mutable_variable;
|
| +}
|
| +
|
| +int32_t AsmJsParser::TempVariable(int i) {
|
| + if (i + 1 > function_temp_locals_used_) {
|
| + function_temp_locals_used_ = i + 1;
|
| + }
|
| + return function_temp_locals_offset_ + i;
|
| +}
|
| +
|
| +void AsmJsParser::SkipSemicolon() {
|
| + if (Check(';')) {
|
| + // Had a semicolon.
|
| + } else if (!Peek('}') && !scanner_.IsPrecededByNewline()) {
|
| + FAIL("Expected ;");
|
| + }
|
| +}
|
| +
|
| +void AsmJsParser::Begin(AsmJsScanner::token_t label) {
|
| + BareBegin(BlockKind::kRegular, label);
|
| + current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
|
| +}
|
| +
|
| +void AsmJsParser::Loop(AsmJsScanner::token_t label) {
|
| + BareBegin(BlockKind::kLoop, label);
|
| + current_function_builder_->EmitWithU8(kExprLoop, kLocalVoid);
|
| +}
|
| +
|
| +void AsmJsParser::End() {
|
| + BareEnd();
|
| + current_function_builder_->Emit(kExprEnd);
|
| +}
|
| +
|
| +void AsmJsParser::BareBegin(BlockKind kind, AsmJsScanner::token_t label) {
|
| + BlockInfo info;
|
| + info.kind = kind;
|
| + info.label = label;
|
| + block_stack_.push_back(info);
|
| +}
|
| +
|
| +void AsmJsParser::BareEnd() {
|
| + DCHECK(block_stack_.size() > 0);
|
| + block_stack_.pop_back();
|
| +}
|
| +
|
| +int AsmJsParser::FindContinueLabelDepth(AsmJsScanner::token_t label) {
|
| + int count = 0;
|
| + for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
|
| + ++it, ++count) {
|
| + if (it->kind == BlockKind::kLoop &&
|
| + (label == kTokenNone || it->label == label)) {
|
| + return count;
|
| + }
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +int AsmJsParser::FindBreakLabelDepth(AsmJsScanner::token_t label) {
|
| + int count = 0;
|
| + for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
|
| + ++it, ++count) {
|
| + if (it->kind == BlockKind::kRegular &&
|
| + (label == kTokenNone || it->label == label)) {
|
| + return count;
|
| + }
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +// 6.1 ValidateModule
|
| +void AsmJsParser::ValidateModule() {
|
| + RECURSE(ValidateModuleParameters());
|
| + EXPECT_TOKEN('{');
|
| + EXPECT_TOKEN(TOK(UseAsm));
|
| + SkipSemicolon();
|
| + RECURSE(ValidateModuleVars());
|
| + while (Peek(TOK(function))) {
|
| + RECURSE(ValidateFunction());
|
| + }
|
| + while (Peek(TOK(var))) {
|
| + RECURSE(ValidateFunctionTable());
|
| + }
|
| + RECURSE(ValidateExport());
|
| +
|
| + // Add start function to init things.
|
| + WasmFunctionBuilder* start = module_builder_->AddFunction();
|
| + module_builder_->MarkStartFunction(start);
|
| + for (auto global_import : global_imports_) {
|
| + if (global_import.needs_init) {
|
| + start->EmitWithVarInt(kExprGetGlobal, global_import.import_index);
|
| + start->EmitWithVarInt(kExprSetGlobal,
|
| + static_cast<uint32_t>(global_import.global_index +
|
| + global_imports_.size()));
|
| + }
|
| + }
|
| + start->Emit(kExprEnd);
|
| + FunctionSig::Builder b(zone(), 0, 0);
|
| + start->SetSignature(b.Build());
|
| +}
|
| +
|
| +// 6.1 ValidateModule - parameters
|
| +void AsmJsParser::ValidateModuleParameters() {
|
| + EXPECT_TOKEN('(');
|
| + stdlib_name_ = 0;
|
| + foreign_name_ = 0;
|
| + heap_name_ = 0;
|
| + if (!Peek(')')) {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected stdlib parameter");
|
| + }
|
| + stdlib_name_ = Consume();
|
| + if (!Peek(')')) {
|
| + EXPECT_TOKEN(',');
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected foreign parameter");
|
| + }
|
| + foreign_name_ = Consume();
|
| + if (!Peek(')')) {
|
| + EXPECT_TOKEN(',');
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected heap parameter");
|
| + }
|
| + heap_name_ = Consume();
|
| + }
|
| + }
|
| + }
|
| + EXPECT_TOKEN(')');
|
| +}
|
| +
|
| +// 6.1 ValidateModule - variables
|
| +void AsmJsParser::ValidateModuleVars() {
|
| + while (Peek(TOK(var)) || Peek(TOK(const))) {
|
| + bool mutable_variable = true;
|
| + if (Check(TOK(var))) {
|
| + // Had a var.
|
| + } else {
|
| + EXPECT_TOKEN(TOK(const));
|
| + mutable_variable = false;
|
| + }
|
| + for (;;) {
|
| + RECURSE(ValidateModuleVar(mutable_variable));
|
| + if (Check(',')) {
|
| + continue;
|
| + }
|
| + break;
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| +}
|
| +
|
| +// 6.1 ValidateModule - one variable
|
| +void AsmJsParser::ValidateModuleVar(bool mutable_variable) {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected identifier");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kUnused) {
|
| + FAIL("Redefinition of variable");
|
| + }
|
| + EXPECT_TOKEN('=');
|
| + double dvalue = 0.0;
|
| + uint64_t uvalue = 0;
|
| + if (CheckForDouble(&dvalue)) {
|
| + DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(dvalue));
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + DeclareGlobal(info, mutable_variable, AsmType::Int(), kWasmI32,
|
| + WasmInitExpr(static_cast<int32_t>(uvalue)));
|
| + } else if (Check('-')) {
|
| + if (CheckForDouble(&dvalue)) {
|
| + DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(-dvalue));
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + DeclareGlobal(info, mutable_variable, AsmType::Int(), kWasmI32,
|
| + WasmInitExpr(-static_cast<int32_t>(uvalue)));
|
| + } else {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + } else if (Check(TOK(new))) {
|
| + RECURSE(ValidateModuleVarNewStdlib(info));
|
| + } else if (Check(stdlib_name_)) {
|
| + EXPECT_TOKEN('.');
|
| + RECURSE(ValidateModuleVarStdlib(info));
|
| + } else if (ValidateModuleVarImport(info, mutable_variable)) {
|
| + // Handled inside.
|
| + } else if (scanner_.IsGlobal()) {
|
| + RECURSE(ValidateModuleVarFloat(info, mutable_variable));
|
| + } else {
|
| + FAIL("Bad variable declaration");
|
| + }
|
| +}
|
| +
|
| +// 6.1 ValidateModule - global float declaration
|
| +void AsmJsParser::ValidateModuleVarFloat(VarInfo* info, bool mutable_variable) {
|
| + if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
|
| + FAIL("Expected fround");
|
| + }
|
| + EXPECT_TOKEN('(');
|
| + bool negate = false;
|
| + if (Check('-')) {
|
| + negate = true;
|
| + }
|
| + double dvalue = 0.0;
|
| + uint64_t uvalue = 0;
|
| + if (CheckForDouble(&dvalue)) {
|
| + if (negate) {
|
| + dvalue = -dvalue;
|
| + }
|
| + DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
|
| + WasmInitExpr(static_cast<float>(dvalue)));
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + dvalue = uvalue;
|
| + if (negate) {
|
| + dvalue = -dvalue;
|
| + }
|
| + DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
|
| + WasmInitExpr(static_cast<float>(dvalue)));
|
| + } else {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + EXPECT_TOKEN(')');
|
| +}
|
| +
|
| +// 6.1 ValidateModule - foreign imports
|
| +bool AsmJsParser::ValidateModuleVarImport(VarInfo* info,
|
| + bool mutable_variable) {
|
| + if (Check('+')) {
|
| + EXPECT_TOKENf(foreign_name_);
|
| + EXPECT_TOKENf('.');
|
| + AddGlobalImport(scanner_.GetIdentifierString(), AsmType::Double(), kWasmF64,
|
| + mutable_variable, info);
|
| + scanner_.Next();
|
| + return true;
|
| + } else if (Check(foreign_name_)) {
|
| + EXPECT_TOKENf('.');
|
| + std::string import_name = scanner_.GetIdentifierString();
|
| + scanner_.Next();
|
| + if (Check('|')) {
|
| + if (!CheckForZero()) {
|
| + FAILf("Expected |0 type annotation for foreign integer import");
|
| + }
|
| + AddGlobalImport(import_name, AsmType::Int(), kWasmI32, mutable_variable,
|
| + info);
|
| + return true;
|
| + }
|
| + info->kind = VarKind::kImportedFunction;
|
| + function_import_info_.resize(function_import_info_.size() + 1);
|
| + info->import = &function_import_info_.back();
|
| + info->import->name = import_name;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// 6.1 ValidateModule - one variable
|
| +// 9 - Standard Library - heap types
|
| +void AsmJsParser::ValidateModuleVarNewStdlib(VarInfo* info) {
|
| + EXPECT_TOKEN(stdlib_name_);
|
| + EXPECT_TOKEN('.');
|
| + switch (Consume()) {
|
| +#define V(name, _junk1, _junk2, _junk3) \
|
| + case TOK(name): \
|
| + info->DeclareStdlibFunc(VarKind::kSpecial, AsmType::name()); \
|
| + break;
|
| + STDLIB_ARRAY_TYPE_LIST(V)
|
| +#undef V
|
| + default:
|
| + FAIL("Expected ArrayBuffer view");
|
| + break;
|
| + }
|
| + EXPECT_TOKEN('(');
|
| + EXPECT_TOKEN(heap_name_);
|
| + EXPECT_TOKEN(')');
|
| +}
|
| +
|
| +// 6.1 ValidateModule - one variable
|
| +// 9 - Standard Library
|
| +void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
|
| + if (Check(TOK(Math))) {
|
| + EXPECT_TOKEN('.');
|
| + switch (Consume()) {
|
| +#define V(name) \
|
| + case TOK(name): \
|
| + DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
|
| + WasmInitExpr(M_##name)); \
|
| + break;
|
| + STDLIB_MATH_VALUE_LIST(V)
|
| +#undef V
|
| +#define V(name, Name, op, sig) \
|
| + case TOK(name): \
|
| + info->DeclareStdlibFunc(VarKind::kMath##Name, stdlib_##sig##_); \
|
| + stdlib_uses_.insert(AsmTyper::kMath##Name); \
|
| + break;
|
| + STDLIB_MATH_FUNCTION_LIST(V)
|
| +#undef V
|
| + default:
|
| + FAIL("Invalid member of stdlib.Math");
|
| + }
|
| + } else if (Check(TOK(Infinity))) {
|
| + DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(std::numeric_limits<double>::infinity()));
|
| + } else if (Check(TOK(NaN))) {
|
| + DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
|
| + } else {
|
| + FAIL("Invalid member of stdlib");
|
| + }
|
| +}
|
| +
|
| +// 6.2 ValidateExport
|
| +void AsmJsParser::ValidateExport() {
|
| + // clang-format off
|
| + EXPECT_TOKEN(TOK(return));
|
| + // clang format on
|
| + if (Check('{')) {
|
| + for (;;) {
|
| + std::string name = scanner_.GetIdentifierString();
|
| + if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
|
| + FAIL("Illegal export name");
|
| + }
|
| + Consume();
|
| + EXPECT_TOKEN(':');
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected function name");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kFunction) {
|
| + FAIL("Expected function");
|
| + }
|
| + info->function_builder->ExportAs(
|
| + {name.c_str(), static_cast<int>(name.size())});
|
| + if (Check(',')) {
|
| + if (!Peek('}')) {
|
| + continue;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + EXPECT_TOKEN('}');
|
| + } else {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Single function export must be a function name");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kFunction) {
|
| + FAIL("Single function export must be a function");
|
| + }
|
| + const char* single_function_name = "__single_function__";
|
| + info->function_builder->ExportAs(CStrVector(single_function_name));
|
| + }
|
| +}
|
| +
|
| +// 6.3 ValidateFunctionTable
|
| +void AsmJsParser::ValidateFunctionTable() {
|
| + EXPECT_TOKEN(TOK(var));
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected table name");
|
| + }
|
| + VarInfo* table_info = GetVarInfo(Consume());
|
| + // TODO(bradnelson): Check for double use of export name.
|
| + EXPECT_TOKEN('=');
|
| + EXPECT_TOKEN('[');
|
| + uint64_t count = 0;
|
| + for (;;) {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected function name");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kFunction) {
|
| + FAIL("Expected function");
|
| + }
|
| + if (table_info->kind == VarKind::kTable) {
|
| + DCHECK_GE(table_info->mask, 0);
|
| + if (count >= static_cast<uint64_t>(table_info->mask) + 1) {
|
| + FAIL("Exceeded function table size");
|
| + }
|
| + // Only store the function into a table if we used the table somewhere
|
| + // (i.e. tables are first seen at their use sites and allocated there).
|
| + module_builder_->SetIndirectFunction(
|
| + static_cast<uint32_t>(table_info->index + count), info->index);
|
| + }
|
| + ++count;
|
| + if (Check(',')) {
|
| + if (!Peek(']')) {
|
| + continue;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + EXPECT_TOKEN(']');
|
| + if (table_info->kind == VarKind::kTable &&
|
| + count != static_cast<uint64_t>(table_info->mask) + 1) {
|
| + FAIL("Function table size does not match uses");
|
| + }
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.4 ValidateFunction
|
| +void AsmJsParser::ValidateFunction() {
|
| + int start_position = scanner_.GetPosition();
|
| + EXPECT_TOKEN(TOK(function));
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected function name");
|
| + }
|
| +
|
| + std::string function_name_raw = scanner_.GetIdentifierString();
|
| + AsmJsScanner::token_t function_name = Consume();
|
| + VarInfo* function_info = GetVarInfo(function_name);
|
| + if (function_info->kind == VarKind::kUnused) {
|
| + function_info->kind = VarKind::kFunction;
|
| + function_info->function_builder = module_builder_->AddFunction();
|
| + function_info->function_builder->SetName(
|
| + {function_name_raw.c_str(),
|
| + static_cast<int>(function_name_raw.size())});
|
| + function_info->index = function_info->function_builder->func_index();
|
| + function_info->function_defined = true;
|
| + } else if (function_info->function_defined) {
|
| + FAIL("Function redefined");
|
| + }
|
| + current_function_builder_ = function_info->function_builder;
|
| + return_type_ = nullptr;
|
| +
|
| + // Record start of the function, used as position for the stack check.
|
| + current_function_builder_->SetAsmFunctionStartPosition(start_position);
|
| +
|
| + std::vector<AsmType*> params;
|
| + ValidateFunctionParams(¶ms);
|
| + std::vector<ValueType> locals;
|
| + ValidateFunctionLocals(params.size(), &locals);
|
| +
|
| + function_temp_locals_offset_ = static_cast<uint32_t>(
|
| + params.size() + locals.size());
|
| + function_temp_locals_used_ = 0;
|
| +
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| + EXPECT_TOKEN('}');
|
| +
|
| + if (return_type_ == nullptr) {
|
| + return_type_ = AsmType::Void();
|
| + }
|
| +
|
| + // TODO(bradnelson): WasmModuleBuilder can't take this in the right order.
|
| + // We should fix that so we can use it instead.
|
| + FunctionSig* sig = ConvertSignature(return_type_, params);
|
| + if (sig == nullptr) {
|
| + FAIL("Invalid function signature in declaration");
|
| + }
|
| + current_function_builder_->SetSignature(sig);
|
| + for (auto local : locals) {
|
| + current_function_builder_->AddLocal(local);
|
| + }
|
| + // Add bonus temps.
|
| + for (int i = 0; i < function_temp_locals_used_; ++i) {
|
| + current_function_builder_->AddLocal(kWasmI32);
|
| + }
|
| +
|
| + // End function
|
| + current_function_builder_->Emit(kExprEnd);
|
| +
|
| + // Add in function type.
|
| + AsmType* function_type = AsmType::Function(zone(), return_type_);
|
| + for (auto t : params) {
|
| + function_type->AsFunctionType()->AddArgument(t);
|
| + }
|
| + function_info = GetVarInfo(function_name);
|
| + if (function_info->kind == VarKind::kUnused) {
|
| + function_info->kind = VarKind::kFunction;
|
| + function_info->index = current_function_builder_->func_index();
|
| + function_info->type = function_type;
|
| + } else {
|
| + if (function_info->kind != VarKind::kFunction) {
|
| + FAIL("Function name collides with variable");
|
| + }
|
| + // TODO(bradnelson): Should IsExactly be used here?
|
| + if (!function_info->type->IsA(AsmType::None()) &&
|
| + !function_type->IsA(function_info->type)) {
|
| + FAIL("Function definition doesn't match use");
|
| + }
|
| + }
|
| +
|
| + scanner_.ResetLocals();
|
| + local_var_info_.clear();
|
| +}
|
| +
|
| +// 6.4 ValidateFunction
|
| +void AsmJsParser::ValidateFunctionParams(std::vector<AsmType*>* params) {
|
| + // TODO(bradnelson): Do this differently so that the scanner doesn't need to
|
| + // have a state transition that needs knowledge of how the scanner works
|
| + // inside.
|
| + scanner_.EnterLocalScope();
|
| + EXPECT_TOKEN('(');
|
| + std::vector<AsmJsScanner::token_t> function_parameters;
|
| + while (!failed_ && !Peek(')')) {
|
| + if (!scanner_.IsLocal()) {
|
| + FAIL("Expected parameter name");
|
| + }
|
| + function_parameters.push_back(Consume());
|
| + if (!Peek(')')) {
|
| + EXPECT_TOKEN(',');
|
| + }
|
| + }
|
| + EXPECT_TOKEN(')');
|
| + scanner_.EnterGlobalScope();
|
| + EXPECT_TOKEN('{');
|
| + // 5.1 Parameter Type Annotations
|
| + for (auto p : function_parameters) {
|
| + EXPECT_TOKEN(p);
|
| + EXPECT_TOKEN('=');
|
| + VarInfo* info = GetVarInfo(p);
|
| + if (info->kind != VarKind::kUnused) {
|
| + FAIL("Duplicate parameter name");
|
| + }
|
| + if (Check(p)) {
|
| + EXPECT_TOKEN('|');
|
| + if (!CheckForZero()) {
|
| + FAIL("Bad integer parameter annotation.");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(params->size());
|
| + params->push_back(AsmType::Int());
|
| + } else if (Check('+')) {
|
| + EXPECT_TOKEN(p);
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Double();
|
| + info->index = static_cast<uint32_t>(params->size());
|
| + params->push_back(AsmType::Double());
|
| + } else {
|
| + if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
|
| + FAIL("Expected fround");
|
| + }
|
| + EXPECT_TOKEN('(');
|
| + EXPECT_TOKEN(p);
|
| + EXPECT_TOKEN(')');
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(params->size());
|
| + params->push_back(AsmType::Float());
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| +}
|
| +
|
| +// 6.4 ValidateFunction - locals
|
| +void AsmJsParser::ValidateFunctionLocals(
|
| + size_t param_count, std::vector<ValueType>* locals) {
|
| + // Local Variables.
|
| + while (Peek(TOK(var))) {
|
| + scanner_.EnterLocalScope();
|
| + EXPECT_TOKEN(TOK(var));
|
| + scanner_.EnterGlobalScope();
|
| + for (;;) {
|
| + if (!scanner_.IsLocal()) {
|
| + FAIL("Expected local variable identifier");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kUnused) {
|
| + FAIL("Duplicate local variable name");
|
| + }
|
| + // Store types.
|
| + EXPECT_TOKEN('=');
|
| + double dvalue = 0.0;
|
| + uint64_t uvalue = 0;
|
| + if (Check('-')) {
|
| + if (CheckForDouble(&dvalue)) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Double();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmF64);
|
| + byte code[] = {WASM_F64(dvalue)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmI32);
|
| + int32_t value = -static_cast<int32_t>(uvalue);
|
| + current_function_builder_->EmitI32Const(value);
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + } else if (scanner_.IsGlobal()) {
|
| + VarInfo* sinfo = GetVarInfo(Consume());
|
| + if (sinfo->kind == VarKind::kGlobal) {
|
| + if (sinfo->mutable_variable) {
|
| + FAIL("Initializing from global requires const variable");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = sinfo->type;
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + if (sinfo->type->IsA(AsmType::Int())) {
|
| + locals->push_back(kWasmI32);
|
| + } else if (sinfo->type->IsA(AsmType::Float())) {
|
| + locals->push_back(kWasmF32);
|
| + } else if (sinfo->type->IsA(AsmType::Double())) {
|
| + locals->push_back(kWasmF64);
|
| + } else {
|
| + FAIL("Bad local variable definition");
|
| + }
|
| + current_function_builder_->EmitWithVarInt(kExprGetGlobal,
|
| + VarIndex(sinfo));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else if (sinfo->type->IsA(stdlib_fround_)) {
|
| + EXPECT_TOKEN('(');
|
| + bool negate = false;
|
| + if (Check('-')) {
|
| + negate = true;
|
| + }
|
| + double dvalue = 0.0;
|
| + if (CheckForDouble(&dvalue)) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmF32);
|
| + if (negate) {
|
| + dvalue = -dvalue;
|
| + }
|
| + byte code[] = {WASM_F32(dvalue)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmF32);
|
| + int32_t value = static_cast<int32_t>(uvalue);
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + double fvalue = static_cast<double>(value);
|
| + byte code[] = {WASM_F32(fvalue)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + EXPECT_TOKEN(')');
|
| + } else {
|
| + FAIL("expected fround or const global");
|
| + }
|
| + } else if (CheckForDouble(&dvalue)) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Double();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmF64);
|
| + byte code[] = {WASM_F64(dvalue)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(param_count + locals->size());
|
| + locals->push_back(kWasmI32);
|
| + int32_t value = static_cast<int32_t>(uvalue);
|
| + current_function_builder_->EmitI32Const(value);
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + if (!Peek(',')) {
|
| + break;
|
| + }
|
| + scanner_.EnterLocalScope();
|
| + EXPECT_TOKEN(',');
|
| + scanner_.EnterGlobalScope();
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| +}
|
| +
|
| +// ValidateStatement
|
| +void AsmJsParser::ValidateStatement() {
|
| + call_coercion_ = nullptr;
|
| + if (Peek('{')) {
|
| + RECURSE(Block());
|
| + } else if (Peek(';')) {
|
| + RECURSE(EmptyStatement());
|
| + } else if (Peek(TOK(if))) {
|
| + RECURSE(IfStatement());
|
| + // clang-format off
|
| + } else if (Peek(TOK(return))) {
|
| + // clang-format on
|
| + RECURSE(ReturnStatement());
|
| + } else if (IterationStatement()) {
|
| + // Handled in IterationStatement.
|
| + } else if (Peek(TOK(break))) {
|
| + RECURSE(BreakStatement());
|
| + } else if (Peek(TOK(continue))) {
|
| + RECURSE(ContinueStatement());
|
| + } else if (Peek(TOK(switch))) {
|
| + RECURSE(SwitchStatement());
|
| + } else {
|
| + RECURSE(ExpressionStatement());
|
| + }
|
| +}
|
| +
|
| +// 6.5.1 Block
|
| +void AsmJsParser::Block() {
|
| + bool can_break_to_block = pending_label_ != 0;
|
| + if (can_break_to_block) {
|
| + Begin(pending_label_);
|
| + }
|
| + pending_label_ = 0;
|
| + EXPECT_TOKEN('{');
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| + EXPECT_TOKEN('}');
|
| + if (can_break_to_block) {
|
| + End();
|
| + }
|
| +}
|
| +
|
| +// 6.5.2 ExpressionStatement
|
| +void AsmJsParser::ExpressionStatement() {
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + // NOTE: Both global or local identifiers can also be used as labels.
|
| + scanner_.Next();
|
| + if (Peek(':')) {
|
| + scanner_.Rewind();
|
| + RECURSE(LabelledStatement());
|
| + return;
|
| + }
|
| + scanner_.Rewind();
|
| + }
|
| + AsmType* ret;
|
| + RECURSE(ret = ValidateExpression());
|
| + if (!ret->IsA(AsmType::Void())) {
|
| + current_function_builder_->Emit(kExprDrop);
|
| + }
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.3 EmptyStatement
|
| +void AsmJsParser::EmptyStatement() { EXPECT_TOKEN(';'); }
|
| +
|
| +// 6.5.4 IfStatement
|
| +void AsmJsParser::IfStatement() {
|
| + EXPECT_TOKEN(TOK(if));
|
| + EXPECT_TOKEN('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + EXPECT_TOKEN(')');
|
| + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
| + BareBegin();
|
| + RECURSE(ValidateStatement());
|
| + if (Check(TOK(else))) {
|
| + current_function_builder_->Emit(kExprElse);
|
| + RECURSE(ValidateStatement());
|
| + }
|
| + current_function_builder_->Emit(kExprEnd);
|
| + BareEnd();
|
| +}
|
| +
|
| +// 6.5.5 ReturnStatement
|
| +void AsmJsParser::ReturnStatement() {
|
| + // clang-format off
|
| + EXPECT_TOKEN(TOK(return ));
|
| + // clang-format on
|
| + if (!Peek(';') && !Peek('}')) {
|
| + // TODO(bradnelson): See if this can be factored out.
|
| + AsmType* ret;
|
| + RECURSE(ret = Expression(return_type_));
|
| + if (ret->IsA(AsmType::Double())) {
|
| + return_type_ = AsmType::Double();
|
| + } else if (ret->IsA(AsmType::Float())) {
|
| + return_type_ = AsmType::Float();
|
| + } else if (ret->IsA(AsmType::Signed())) {
|
| + return_type_ = AsmType::Signed();
|
| + } else {
|
| + FAIL("Invalid return type");
|
| + }
|
| + } else {
|
| + return_type_ = AsmType::Void();
|
| + }
|
| + current_function_builder_->Emit(kExprReturn);
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement
|
| +bool AsmJsParser::IterationStatement() {
|
| + if (Peek(TOK(while))) {
|
| + WhileStatement();
|
| + } else if (Peek(TOK(do))) {
|
| + DoStatement();
|
| + } else if (Peek(TOK(for))) {
|
| + ForStatement();
|
| + } else {
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement - while
|
| +void AsmJsParser::WhileStatement() {
|
| + // a: block {
|
| + Begin(pending_label_);
|
| + // b: loop {
|
| + Loop(pending_label_);
|
| + pending_label_ = 0;
|
| + EXPECT_TOKEN(TOK(while));
|
| + EXPECT_TOKEN('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + EXPECT_TOKEN(')');
|
| + // if (!CONDITION) break a;
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + // BODY
|
| + RECURSE(ValidateStatement());
|
| + // continue b;
|
| + current_function_builder_->EmitWithU8(kExprBr, 0);
|
| + End();
|
| + // }
|
| + // }
|
| + End();
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement - do
|
| +void AsmJsParser::DoStatement() {
|
| + // a: block {
|
| + Begin(pending_label_);
|
| + // b: loop {
|
| + Loop();
|
| + // c: block { // but treated like loop so continue works
|
| + BareBegin(BlockKind::kLoop, pending_label_);
|
| + current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
|
| + pending_label_ = 0;
|
| + EXPECT_TOKEN(TOK(do));
|
| + // BODY
|
| + RECURSE(ValidateStatement());
|
| + EXPECT_TOKEN(TOK(while));
|
| + End();
|
| + // }
|
| + EXPECT_TOKEN('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + // if (CONDITION) break a;
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + // continue b;
|
| + current_function_builder_->EmitWithU8(kExprBr, 0);
|
| + EXPECT_TOKEN(')');
|
| + // }
|
| + End();
|
| + // }
|
| + End();
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement - for
|
| +void AsmJsParser::ForStatement() {
|
| + EXPECT_TOKEN(TOK(for));
|
| + EXPECT_TOKEN('(');
|
| + if (!Peek(';')) {
|
| + Expression(nullptr);
|
| + }
|
| + EXPECT_TOKEN(';');
|
| + // a: block {
|
| + Begin(pending_label_);
|
| + // b: loop {
|
| + Loop(pending_label_);
|
| + pending_label_ = 0;
|
| + if (!Peek(';')) {
|
| + // if (CONDITION) break a;
|
| + RECURSE(Expression(AsmType::Int()));
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + }
|
| + EXPECT_TOKEN(';');
|
| + // Stash away INCREMENT
|
| + size_t increment_position = current_function_builder_->GetPosition();
|
| + if (!Peek(')')) {
|
| + RECURSE(Expression(nullptr));
|
| + }
|
| + std::vector<byte> increment_code;
|
| + current_function_builder_->StashCode(&increment_code, increment_position);
|
| + EXPECT_TOKEN(')');
|
| + // BODY
|
| + RECURSE(ValidateStatement());
|
| + // INCREMENT
|
| + current_function_builder_->EmitCode(
|
| + increment_code.data(), static_cast<uint32_t>(increment_code.size()));
|
| + current_function_builder_->EmitWithU8(kExprBr, 0);
|
| + // }
|
| + End();
|
| + // }
|
| + End();
|
| +}
|
| +
|
| +// 6.5.7 BreakStatement
|
| +void AsmJsParser::BreakStatement() {
|
| + EXPECT_TOKEN(TOK(break));
|
| + AsmJsScanner::token_t label_name = kTokenNone;
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + // NOTE: Currently using globals/locals for labels too.
|
| + label_name = Consume();
|
| + }
|
| + int depth = FindBreakLabelDepth(label_name);
|
| + if (depth < 0) {
|
| + FAIL("Illegal break");
|
| + }
|
| + current_function_builder_->Emit(kExprBr);
|
| + current_function_builder_->EmitVarInt(depth);
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.8 ContinueStatement
|
| +void AsmJsParser::ContinueStatement() {
|
| + EXPECT_TOKEN(TOK(continue));
|
| + AsmJsScanner::token_t label_name = kTokenNone;
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + // NOTE: Currently using globals/locals for labels too.
|
| + label_name = Consume();
|
| + }
|
| + int depth = FindContinueLabelDepth(label_name);
|
| + if (depth < 0) {
|
| + FAIL("Illegal continue");
|
| + }
|
| + current_function_builder_->Emit(kExprBr);
|
| + current_function_builder_->EmitVarInt(depth);
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.9 LabelledStatement
|
| +void AsmJsParser::LabelledStatement() {
|
| + DCHECK(scanner_.IsGlobal() || scanner_.IsLocal());
|
| + // NOTE: Currently using globals/locals for labels too.
|
| + if (pending_label_ != 0) {
|
| + FAIL("Double label unsupported");
|
| + }
|
| + pending_label_ = scanner_.Token();
|
| + scanner_.Next();
|
| + EXPECT_TOKEN(':');
|
| + RECURSE(ValidateStatement());
|
| +}
|
| +
|
| +// 6.5.10 SwitchStatement
|
| +void AsmJsParser::SwitchStatement() {
|
| + EXPECT_TOKEN(TOK(switch));
|
| + EXPECT_TOKEN('(');
|
| + AsmType* test;
|
| + RECURSE(test = Expression(nullptr));
|
| + if (!test->IsA(AsmType::Signed())) {
|
| + FAIL("Expected signed for switch value");
|
| + }
|
| + EXPECT_TOKEN(')');
|
| + int32_t tmp = TempVariable(0);
|
| + current_function_builder_->EmitSetLocal(tmp);
|
| + Begin(pending_label_);
|
| + pending_label_ = 0;
|
| + // TODO(bradnelson): Make less weird.
|
| + std::vector<int32_t> cases;
|
| + GatherCases(&cases); // Skips { implicitly.
|
| + size_t count = cases.size() + 1;
|
| + for (size_t i = 0; i < count; ++i) {
|
| + BareBegin(BlockKind::kOther);
|
| + current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
|
| + }
|
| + int table_pos = 0;
|
| + for (auto c : cases) {
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + current_function_builder_->EmitI32Const(c);
|
| + current_function_builder_->Emit(kExprI32Eq);
|
| + current_function_builder_->EmitWithVarInt(kExprBrIf, table_pos++);
|
| + }
|
| + current_function_builder_->EmitWithVarInt(kExprBr, table_pos++);
|
| + while (!failed_ && Peek(TOK(case))) {
|
| + current_function_builder_->Emit(kExprEnd);
|
| + BareEnd();
|
| + RECURSE(ValidateCase());
|
| + }
|
| + current_function_builder_->Emit(kExprEnd);
|
| + BareEnd();
|
| + if (Peek(TOK(default))) {
|
| + RECURSE(ValidateDefault());
|
| + }
|
| + EXPECT_TOKEN('}');
|
| + End();
|
| +}
|
| +
|
| +// 6.6. ValidateCase
|
| +void AsmJsParser::ValidateCase() {
|
| + EXPECT_TOKEN(TOK(case));
|
| + bool negate = false;
|
| + if (Check('-')) {
|
| + negate = true;
|
| + }
|
| + uint64_t uvalue;
|
| + if (!CheckForUnsigned(&uvalue)) {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + // TODO(bradnelson): Share negation plumbing.
|
| + if ((negate && uvalue > 0x80000000) || (!negate && uvalue > 0x7fffffff)) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + int32_t value = static_cast<int32_t>(uvalue);
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + EXPECT_TOKEN(':');
|
| + while (!failed_ && !Peek('}') && !Peek(TOK(case)) && !Peek(TOK(default))) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| +}
|
| +
|
| +// 6.7 ValidateDefault
|
| +void AsmJsParser::ValidateDefault() {
|
| + EXPECT_TOKEN(TOK(default));
|
| + EXPECT_TOKEN(':');
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| +}
|
| +
|
| +// 6.8 ValidateExpression
|
| +AsmType* AsmJsParser::ValidateExpression() {
|
| + AsmType* ret;
|
| + RECURSEn(ret = Expression(nullptr));
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.1 Expression
|
| +AsmType* AsmJsParser::Expression(AsmType* expected) {
|
| + AsmType* a;
|
| + for (;;) {
|
| + RECURSEn(a = AssignmentExpression());
|
| + if (Peek(',')) {
|
| + if (a->IsA(AsmType::None())) {
|
| + FAILn("Expected actual type");
|
| + }
|
| + if (!a->IsA(AsmType::Void())) {
|
| + current_function_builder_->Emit(kExprDrop);
|
| + }
|
| + EXPECT_TOKENn(',');
|
| + continue;
|
| + }
|
| + break;
|
| + }
|
| + if (expected != nullptr && !a->IsA(expected)) {
|
| + FAILn("Unexpected type");
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.2 NumericLiteral
|
| +AsmType* AsmJsParser::NumericLiteral() {
|
| + call_coercion_ = nullptr;
|
| + double dvalue = 0.0;
|
| + uint64_t uvalue = 0;
|
| + if (CheckForDouble(&dvalue)) {
|
| + byte code[] = {WASM_F64(dvalue)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + return AsmType::Double();
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue <= 0x7fffffff) {
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
|
| + return AsmType::FixNum();
|
| + } else if (uvalue <= 0xffffffff) {
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
|
| + return AsmType::Unsigned();
|
| + } else {
|
| + FAILn("Integer numeric literal out of range.");
|
| + }
|
| + } else {
|
| + FAILn("Expected numeric literal.");
|
| + }
|
| +}
|
| +
|
| +// 6.8.3 Identifier
|
| +AsmType* AsmJsParser::Identifier() {
|
| + call_coercion_ = nullptr;
|
| + if (scanner_.IsLocal()) {
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kLocal) {
|
| + FAILn("Undefined local variable");
|
| + }
|
| + current_function_builder_->EmitGetLocal(info->index);
|
| + return info->type;
|
| + } else if (scanner_.IsGlobal()) {
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kGlobal) {
|
| + FAILn("Undefined global variable");
|
| + }
|
| + current_function_builder_->EmitWithVarInt(kExprGetGlobal, VarIndex(info));
|
| + return info->type;
|
| + }
|
| + UNREACHABLE();
|
| + return nullptr;
|
| +}
|
| +
|
| +// 6.8.4 CallExpression
|
| +AsmType* AsmJsParser::CallExpression() {
|
| + AsmType* ret;
|
| + if (scanner_.IsGlobal() &&
|
| + GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
|
| + ValidateFloatCoercion();
|
| + return AsmType::Float();
|
| + } else if (scanner_.IsGlobal() &&
|
| + GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
|
| + RECURSEn(ret = MemberExpression());
|
| + } else if (Peek('(')) {
|
| + RECURSEn(ret = ParenthesizedExpression());
|
| + } else if (PeekCall()) {
|
| + RECURSEn(ret = ValidateCall());
|
| + } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
|
| + RECURSEn(ret = Identifier());
|
| + } else {
|
| + RECURSEn(ret = NumericLiteral());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.5 MemberExpression
|
| +AsmType* AsmJsParser::MemberExpression() {
|
| + call_coercion_ = nullptr;
|
| + ValidateHeapAccess();
|
| + if (Peek('=')) {
|
| + inside_heap_assignment_ = true;
|
| + return heap_access_type_->StoreType();
|
| + } else {
|
| +#define V(array_type, wasmload, wasmstore, type) \
|
| + if (heap_access_type_->IsA(AsmType::array_type())) { \
|
| + current_function_builder_->Emit(kExpr##type##AsmjsLoad##wasmload); \
|
| + return heap_access_type_->LoadType(); \
|
| + }
|
| + STDLIB_ARRAY_TYPE_LIST(V)
|
| +#undef V
|
| + FAILn("Expected valid heap load");
|
| + }
|
| +}
|
| +
|
| +// 6.8.6 AssignmentExpression
|
| +AsmType* AsmJsParser::AssignmentExpression() {
|
| + AsmType* ret;
|
| + if (scanner_.IsGlobal() &&
|
| + GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
|
| + RECURSEn(ret = ConditionalExpression());
|
| + if (Peek('=')) {
|
| + if (!inside_heap_assignment_) {
|
| + FAILn("Invalid assignment target");
|
| + }
|
| + inside_heap_assignment_ = false;
|
| + AsmType* heap_type = heap_access_type_;
|
| + EXPECT_TOKENn('=');
|
| + AsmType* value;
|
| + RECURSEn(value = AssignmentExpression());
|
| + if (!value->IsA(ret)) {
|
| + FAILn("Illegal type stored to heap view");
|
| + }
|
| + if (heap_type->IsA(AsmType::Float32Array()) &&
|
| + value->IsA(AsmType::Double())) {
|
| + // Assignment to a float32 heap can be used to convert doubles.
|
| + current_function_builder_->Emit(kExprF32ConvertF64);
|
| + }
|
| + ret = value;
|
| +#define V(array_type, wasmload, wasmstore, type) \
|
| + if (heap_type->IsA(AsmType::array_type())) { \
|
| + current_function_builder_->Emit(kExpr##type##AsmjsStore##wasmstore); \
|
| + return ret; \
|
| + }
|
| + STDLIB_ARRAY_TYPE_LIST(V)
|
| +#undef V
|
| + }
|
| + } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
|
| + bool is_local = scanner_.IsLocal();
|
| + VarInfo* info = GetVarInfo(scanner_.Token());
|
| + USE(is_local);
|
| + ret = info->type;
|
| + scanner_.Next();
|
| + if (Check('=')) {
|
| + // NOTE: Before this point, this might have been VarKind::kUndefined,
|
| + // as it might be a label.
|
| + DCHECK(is_local ? info->kind == VarKind::kLocal
|
| + : info->kind == VarKind::kGlobal);
|
| + AsmType* value;
|
| + RECURSEn(value = AssignmentExpression());
|
| + if (!value->IsA(ret)) {
|
| + FAILn("Type mismatch in assignment");
|
| + }
|
| + if (info->kind == VarKind::kLocal) {
|
| + current_function_builder_->EmitTeeLocal(info->index);
|
| + } else if (info->kind == VarKind::kGlobal) {
|
| + current_function_builder_->EmitWithVarUint(kExprSetGlobal,
|
| + VarIndex(info));
|
| + current_function_builder_->EmitWithVarUint(kExprGetGlobal,
|
| + VarIndex(info));
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| + return ret;
|
| + }
|
| + scanner_.Rewind();
|
| + RECURSEn(ret = ConditionalExpression());
|
| + } else {
|
| + RECURSEn(ret = ConditionalExpression());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.7 UnaryExpression
|
| +AsmType* AsmJsParser::UnaryExpression() {
|
| + AsmType* ret;
|
| + if (Check('-')) {
|
| + uint64_t uvalue;
|
| + if (CheckForUnsigned(&uvalue)) {
|
| + // TODO(bradnelson): was supposed to be 0x7fffffff, check errata.
|
| + if (uvalue <= 0x80000000) {
|
| + current_function_builder_->EmitI32Const(-static_cast<int32_t>(uvalue));
|
| + } else {
|
| + FAILn("Integer numeric literal out of range.");
|
| + }
|
| + ret = AsmType::Signed();
|
| + } else {
|
| + RECURSEn(ret = UnaryExpression());
|
| + if (ret->IsA(AsmType::Int())) {
|
| + int32_t tmp = TempVariable(0);
|
| + current_function_builder_->EmitSetLocal(tmp);
|
| + current_function_builder_->EmitI32Const(0);
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + current_function_builder_->Emit(kExprI32Sub);
|
| + ret = AsmType::Intish();
|
| + } else if (ret->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF64Neg);
|
| + ret = AsmType::Double();
|
| + } else if (ret->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Neg);
|
| + ret = AsmType::Floatish();
|
| + } else {
|
| + FAILn("expected int/double?/float?");
|
| + }
|
| + }
|
| + } else if (Check('+')) {
|
| + call_coercion_ = AsmType::Double();
|
| + RECURSEn(ret = UnaryExpression());
|
| + // TODO(bradnelson): Generalize.
|
| + if (ret->IsA(AsmType::Signed())) {
|
| + current_function_builder_->Emit(kExprF64SConvertI32);
|
| + ret = AsmType::Double();
|
| + } else if (ret->IsA(AsmType::Unsigned())) {
|
| + current_function_builder_->Emit(kExprF64UConvertI32);
|
| + ret = AsmType::Double();
|
| + } else if (ret->IsA(AsmType::DoubleQ())) {
|
| + ret = AsmType::Double();
|
| + } else if (ret->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF64ConvertF32);
|
| + ret = AsmType::Double();
|
| + } else {
|
| + FAILn("expected signed/unsigned/double?/float?");
|
| + }
|
| + } else if (Check('!')) {
|
| + RECURSEn(ret = UnaryExpression());
|
| + if (!ret->IsA(AsmType::Int())) {
|
| + FAILn("expected int");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + } else if (Check('~')) {
|
| + if (Check('~')) {
|
| + RECURSEn(ret = UnaryExpression());
|
| + if (ret->IsA(AsmType::Double())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsSConvertF64);
|
| + } else if (ret->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsSConvertF32);
|
| + } else {
|
| + FAILn("expected double or float?");
|
| + }
|
| + ret = AsmType::Signed();
|
| + } else {
|
| + RECURSEn(ret = UnaryExpression());
|
| + if (!ret->IsA(AsmType::Intish())) {
|
| + FAILn("operator ~ expects intish");
|
| + }
|
| + current_function_builder_->EmitI32Const(0xffffffff);
|
| + current_function_builder_->Emit(kExprI32Xor);
|
| + ret = AsmType::Signed();
|
| + }
|
| + } else {
|
| + RECURSEn(ret = CallExpression());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.8 MultaplicativeExpression
|
| +AsmType* AsmJsParser::MultiplicativeExpression() {
|
| + uint64_t uvalue;
|
| + if (CheckForUnsignedBelow(0x100000, &uvalue)) {
|
| + if (Check('*')) {
|
| + AsmType* a;
|
| + RECURSEn(a = UnaryExpression());
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAILn("Expected int");
|
| + }
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + scanner_.Rewind();
|
| + } else if (Check('-')) {
|
| + if (CheckForUnsignedBelow(0x100000, &uvalue)) {
|
| + current_function_builder_->EmitI32Const(-static_cast<int32_t>(uvalue));
|
| + if (Check('*')) {
|
| + AsmType* a;
|
| + RECURSEn(a = UnaryExpression());
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAILn("Expected int");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + return AsmType::Signed();
|
| + }
|
| + scanner_.Rewind();
|
| + }
|
| + AsmType* a;
|
| + RECURSEn(a = UnaryExpression());
|
| + for (;;) {
|
| + if (Check('*')) {
|
| + uint64_t uvalue;
|
| + if (Check('-')) {
|
| + if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue >= 0x100000) {
|
| + FAILn("Constant multiple out of range");
|
| + }
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAILn("Integer multiply of expects int");
|
| + }
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + scanner_.Rewind();
|
| + } else if (CheckForUnsigned(&uvalue)) {
|
| + if (uvalue >= 0x100000) {
|
| + FAILn("Constant multiple out of range");
|
| + }
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAILn("Integer multiply of expects int");
|
| + }
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + AsmType* b = UnaryExpression();
|
| + if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF64Mul);
|
| + a = AsmType::Double();
|
| + } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Mul);
|
| + a = AsmType::Floatish();
|
| + } else {
|
| + FAILn("expected doubles or floats");
|
| + }
|
| + } else if (Check('/')) {
|
| + AsmType* b = MultiplicativeExpression();
|
| + if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF64Div);
|
| + a = AsmType::Double();
|
| + } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Div);
|
| + a = AsmType::Floatish();
|
| + } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsDivS);
|
| + a = AsmType::Intish();
|
| + } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsDivU);
|
| + a = AsmType::Intish();
|
| + } else {
|
| + FAILn("expected doubles or floats");
|
| + }
|
| + } else if (Check('%')) {
|
| + AsmType* b = MultiplicativeExpression();
|
| + if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF64Mod);
|
| + a = AsmType::Double();
|
| + } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsRemS);
|
| + a = AsmType::Intish();
|
| + } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsRemU);
|
| + a = AsmType::Intish();
|
| + } else {
|
| + FAILn("expected doubles or floats");
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.9 AdditiveExpression
|
| +AsmType* AsmJsParser::AdditiveExpression() {
|
| + AsmType* a = MultiplicativeExpression();
|
| + int n = 0;
|
| + for (;;) {
|
| + if (Check('+')) {
|
| + AsmType* b = MultiplicativeExpression();
|
| + if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
|
| + current_function_builder_->Emit(kExprF64Add);
|
| + a = AsmType::Double();
|
| + } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Add);
|
| + a = AsmType::Floatish();
|
| + } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
|
| + current_function_builder_->Emit(kExprI32Add);
|
| + a = AsmType::Intish();
|
| + n = 2;
|
| + } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + // TODO(bradnelson): b should really only be Int.
|
| + // specialize intish to capture count.
|
| + ++n;
|
| + if (n > (1 << 20)) {
|
| + FAILn("more than 2^20 additive values");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Add);
|
| + } else {
|
| + FAILn("illegal types for +");
|
| + }
|
| + } else if (Check('-')) {
|
| + AsmType* b = MultiplicativeExpression();
|
| + if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
|
| + current_function_builder_->Emit(kExprF64Sub);
|
| + a = AsmType::Double();
|
| + } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Sub);
|
| + a = AsmType::Floatish();
|
| + } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
|
| + current_function_builder_->Emit(kExprI32Sub);
|
| + a = AsmType::Intish();
|
| + n = 2;
|
| + } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + // TODO(bradnelson): b should really only be Int.
|
| + // specialize intish to capture count.
|
| + ++n;
|
| + if (n > (1 << 20)) {
|
| + FAILn("more than 2^20 additive values");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Sub);
|
| + } else {
|
| + FAILn("illegal types for +");
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.10 ShiftExpression
|
| +AsmType* AsmJsParser::ShiftExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = AdditiveExpression());
|
| + for (;;) {
|
| + switch (scanner_.Token()) {
|
| +// TODO(bradnelson): Implement backtracking to avoid emitting code
|
| +// for the x >>> 0 case (similar to what's there for |0).
|
| +#define HANDLE_CASE(op, opcode, name, result) \
|
| + case TOK(op): { \
|
| + EXPECT_TOKENn(TOK(op)); \
|
| + AsmType* b = nullptr; \
|
| + RECURSEn(b = AdditiveExpression()); \
|
| + if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
|
| + FAILn("Expected intish for operator " #name "."); \
|
| + } \
|
| + current_function_builder_->Emit(kExpr##opcode); \
|
| + a = AsmType::result(); \
|
| + continue; \
|
| + }
|
| + HANDLE_CASE(SHL, I32Shl, "<<", Signed);
|
| + HANDLE_CASE(SAR, I32ShrS, ">>", Signed);
|
| + HANDLE_CASE(SHR, I32ShrU, ">>>", Unsigned);
|
| +#undef HANDLE_CASE
|
| + default:
|
| + return a;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// 6.8.11 RelationalExpression
|
| +AsmType* AsmJsParser::RelationalExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = ShiftExpression());
|
| + for (;;) {
|
| + switch (scanner_.Token()) {
|
| +#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
|
| + case op: { \
|
| + EXPECT_TOKENn(op); \
|
| + AsmType* b = nullptr; \
|
| + RECURSEn(b = ShiftExpression()); \
|
| + if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
|
| + current_function_builder_->Emit(kExpr##sop); \
|
| + } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
|
| + current_function_builder_->Emit(kExpr##uop); \
|
| + } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
|
| + current_function_builder_->Emit(kExpr##dop); \
|
| + } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
|
| + current_function_builder_->Emit(kExpr##fop); \
|
| + } else { \
|
| + FAILn("Expected signed, unsigned, double, or float for operator " #name \
|
| + "."); \
|
| + } \
|
| + a = AsmType::Int(); \
|
| + continue; \
|
| + }
|
| + HANDLE_CASE('<', I32LtS, I32LtU, F64Lt, F32Lt, "<");
|
| + HANDLE_CASE(TOK(LE), I32LeS, I32LeU, F64Le, F32Le, "<=");
|
| + HANDLE_CASE('>', I32GtS, I32GtU, F64Gt, F32Gt, ">");
|
| + HANDLE_CASE(TOK(GE), I32GeS, I32GeU, F64Ge, F32Ge, ">=");
|
| +#undef HANDLE_CASE
|
| + default:
|
| + return a;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// 6.8.12 EqualityExpression
|
| +AsmType* AsmJsParser::EqualityExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = RelationalExpression());
|
| + for (;;) {
|
| + switch (scanner_.Token()) {
|
| +#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
|
| + case op: { \
|
| + EXPECT_TOKENn(op); \
|
| + AsmType* b = nullptr; \
|
| + RECURSEn(b = RelationalExpression()); \
|
| + if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
|
| + current_function_builder_->Emit(kExpr##sop); \
|
| + } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
|
| + current_function_builder_->Emit(kExpr##uop); \
|
| + } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
|
| + current_function_builder_->Emit(kExpr##dop); \
|
| + } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
|
| + current_function_builder_->Emit(kExpr##fop); \
|
| + } else { \
|
| + FAILn("Expected signed, unsigned, double, or float for operator " #name \
|
| + "."); \
|
| + } \
|
| + a = AsmType::Int(); \
|
| + continue; \
|
| + }
|
| + HANDLE_CASE(TOK(EQ), I32Eq, I32Eq, F64Eq, F32Eq, "==");
|
| + HANDLE_CASE(TOK(NE), I32Ne, I32Ne, F64Ne, F32Ne, "!=");
|
| +#undef HANDLE_CASE
|
| + default:
|
| + return a;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// 6.8.13 BitwiseANDExpression
|
| +AsmType* AsmJsParser::BitwiseANDExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = EqualityExpression());
|
| + while (Check('&')) {
|
| + AsmType* b = nullptr;
|
| + RECURSEn(b = EqualityExpression());
|
| + if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + current_function_builder_->Emit(kExprI32And);
|
| + a = AsmType::Signed();
|
| + } else {
|
| + FAILn("Expected intish for operator &.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.14 BitwiseXORExpression
|
| +AsmType* AsmJsParser::BitwiseXORExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = BitwiseANDExpression());
|
| + while (Check('^')) {
|
| + AsmType* b = nullptr;
|
| + RECURSEn(b = BitwiseANDExpression());
|
| + if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + current_function_builder_->Emit(kExprI32Xor);
|
| + a = AsmType::Signed();
|
| + } else {
|
| + FAILn("Expected intish for operator &.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.15 BitwiseORExpression
|
| +AsmType* AsmJsParser::BitwiseORExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSEn(a = BitwiseXORExpression());
|
| + while (Check('|')) {
|
| + // TODO(bradnelson): Make it prettier.
|
| + AsmType* b = nullptr;
|
| + bool zero = false;
|
| + int old_pos;
|
| + size_t old_code;
|
| + if (CheckForZero()) {
|
| + old_pos = scanner_.GetPosition();
|
| + old_code = current_function_builder_->GetPosition();
|
| + scanner_.Rewind();
|
| + zero = true;
|
| + }
|
| + RECURSEn(b = BitwiseXORExpression());
|
| + // Handle |0 specially.
|
| + if (zero && old_pos == scanner_.GetPosition()) {
|
| + current_function_builder_->StashCode(nullptr, old_code);
|
| + a = AsmType::Signed();
|
| + continue;
|
| + }
|
| + if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + current_function_builder_->Emit(kExprI32Ior);
|
| + a = AsmType::Signed();
|
| + } else {
|
| + FAILn("Expected intish for operator |.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.16 ConditionalExpression
|
| +AsmType* AsmJsParser::ConditionalExpression() {
|
| + AsmType* test = nullptr;
|
| + RECURSEn(test = BitwiseORExpression());
|
| + if (Check('?')) {
|
| + if (!test->IsA(AsmType::Int())) {
|
| + FAILn("Expected int in condition of ternary operator.");
|
| + }
|
| + current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
| + size_t fixup = current_function_builder_->GetPosition() -
|
| + 1; // Assumes encoding knowledge.
|
| + AsmType* cons = nullptr;
|
| + RECURSEn(cons = AssignmentExpression());
|
| + current_function_builder_->Emit(kExprElse);
|
| + EXPECT_TOKENn(':');
|
| + AsmType* alt = nullptr;
|
| + RECURSEn(alt = AssignmentExpression());
|
| + current_function_builder_->Emit(kExprEnd);
|
| + if (cons->IsA(AsmType::Int()) && alt->IsA(AsmType::Int())) {
|
| + current_function_builder_->FixupByte(fixup, kLocalI32);
|
| + return AsmType::Int();
|
| + } else if (cons->IsA(AsmType::Double()) && alt->IsA(AsmType::Double())) {
|
| + current_function_builder_->FixupByte(fixup, kLocalF64);
|
| + return AsmType::Double();
|
| + } else if (cons->IsA(AsmType::Float()) && alt->IsA(AsmType::Float())) {
|
| + current_function_builder_->FixupByte(fixup, kLocalF32);
|
| + return AsmType::Float();
|
| + } else {
|
| + FAILn("Type mismatch in ternary operator.");
|
| + }
|
| + } else {
|
| + return test;
|
| + }
|
| +}
|
| +
|
| +// 6.8.17 ParenthesiedExpression
|
| +AsmType* AsmJsParser::ParenthesizedExpression() {
|
| + call_coercion_ = nullptr;
|
| + AsmType* ret;
|
| + EXPECT_TOKENn('(');
|
| + RECURSEn(ret = Expression(nullptr));
|
| + EXPECT_TOKENn(')');
|
| + return ret;
|
| +}
|
| +
|
| +// 6.9 ValidateCall
|
| +AsmType* AsmJsParser::ValidateCall() {
|
| + AsmType* return_type = call_coercion_;
|
| + call_coercion_ = nullptr;
|
| + AsmJsScanner::token_t function_name = Consume();
|
| + int32_t tmp = TempVariable(0);
|
| + if (Check('[')) {
|
| + RECURSEn(EqualityExpression());
|
| + EXPECT_TOKENn('&');
|
| + uint64_t mask = 0;
|
| + if (!CheckForUnsigned(&mask)) {
|
| + FAILn("Expected mask literal");
|
| + }
|
| + if (mask > 0x7fffffff) {
|
| + FAILn("Expected power of 2 mask");
|
| + }
|
| + if (!base::bits::IsPowerOfTwo32(static_cast<uint32_t>(1 + mask))) {
|
| + FAILn("Expected power of 2 mask");
|
| + }
|
| + current_function_builder_->EmitI32Const(static_cast<uint32_t>(mask));
|
| + current_function_builder_->Emit(kExprI32And);
|
| + EXPECT_TOKENn(']');
|
| + VarInfo* function_info = GetVarInfo(function_name);
|
| + if (function_info->kind == VarKind::kUnused) {
|
| + function_info->kind = VarKind::kTable;
|
| + function_info->mask = static_cast<int32_t>(mask);
|
| + function_info->index = module_builder_->AllocateIndirectFunctions(
|
| + static_cast<uint32_t>(mask + 1));
|
| + } else {
|
| + if (function_info->kind != VarKind::kTable) {
|
| + FAILn("Expected call table");
|
| + }
|
| + if (function_info->mask != static_cast<int32_t>(mask)) {
|
| + FAILn("Mask size mismatch");
|
| + }
|
| + }
|
| + current_function_builder_->EmitI32Const(function_info->index);
|
| + current_function_builder_->Emit(kExprI32Add);
|
| + // We have to use a temporary for the correct order of evaluation.
|
| + current_function_builder_->EmitSetLocal(tmp);
|
| + }
|
| + std::vector<AsmType*> param_types;
|
| + ZoneVector<AsmType*> param_specific_types(zone());
|
| + EXPECT_TOKENn('(');
|
| + while (!failed_ && !Peek(')')) {
|
| + AsmType* t;
|
| + RECURSEn(t = AssignmentExpression());
|
| + param_specific_types.push_back(t);
|
| + if (t->IsA(AsmType::Int())) {
|
| + param_types.push_back(AsmType::Int());
|
| + } else if (t->IsA(AsmType::Float())) {
|
| + param_types.push_back(AsmType::Float());
|
| + } else if (t->IsA(AsmType::Double())) {
|
| + param_types.push_back(AsmType::Double());
|
| + } else {
|
| + std::string a = t->Name();
|
| + FAILn("Bad function argument type");
|
| + }
|
| + if (!Peek(')')) {
|
| + EXPECT_TOKENn(',');
|
| + }
|
| + }
|
| + EXPECT_TOKENn(')');
|
| + // TODO(bradnelson): clarify how this binds, and why only float?
|
| + if (Peek('|') &&
|
| + (return_type == nullptr || return_type->IsA(AsmType::Float()))) {
|
| + return_type = AsmType::Signed();
|
| + } else if (return_type == nullptr) {
|
| + return_type = AsmType::Void();
|
| + }
|
| + AsmType* function_type = AsmType::Function(zone(), return_type);
|
| + for (auto t : param_types) {
|
| + function_type->AsFunctionType()->AddArgument(t);
|
| + }
|
| +
|
| + FunctionSig* sig = ConvertSignature(return_type, param_types);
|
| + if (sig == nullptr) {
|
| + FAILn("Invalid function signature");
|
| + }
|
| + uint32_t signature_index = module_builder_->AddSignature(sig);
|
| +
|
| + // TODO(bradnelson): Fix this to use a less error prone pattern.
|
| + // Reload as table might have grown.
|
| + VarInfo* function_info = GetVarInfo(function_name);
|
| + if (function_info->kind == VarKind::kUnused) {
|
| + function_info->kind = VarKind::kFunction;
|
| + function_info->function_builder = module_builder_->AddFunction();
|
| + function_info->index = function_info->function_builder->func_index();
|
| + function_info->type = function_type;
|
| + // TODO(bradnelson): Figure out the right debug scanner offset and
|
| + // re-enable.
|
| + // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
|
| + // scanner_.GetPosition());
|
| + current_function_builder_->Emit(kExprCallFunction);
|
| + current_function_builder_->EmitDirectCallIndex(function_info->index);
|
| + } else if (function_info->kind == VarKind::kImportedFunction) {
|
| + for (auto t : param_specific_types) {
|
| + if (!t->IsA(AsmType::Extern())) {
|
| + FAILn("Imported function args must be type extern");
|
| + }
|
| + }
|
| + if (return_type->IsA(AsmType::Float())) {
|
| + FAILn("Imported function can't be called as float");
|
| + }
|
| + DCHECK(function_info->import != nullptr);
|
| + // TODO(bradnelson): Factor out.
|
| + uint32_t cache_index = function_info->import->cache.FindOrInsert(sig);
|
| + uint32_t index;
|
| + if (cache_index >= function_info->import->cache_index.size()) {
|
| + index = module_builder_->AddImport(
|
| + function_info->import->name.data(),
|
| + static_cast<uint32_t>(function_info->import->name.size()), sig);
|
| + function_info->import->cache_index.push_back(index);
|
| + } else {
|
| + index = function_info->import->cache_index[cache_index];
|
| + }
|
| + current_function_builder_->Emit(kExprCallFunction);
|
| + current_function_builder_->EmitVarUint(index);
|
| + } else if (function_info->type->IsA(AsmType::None())) {
|
| + function_info->type = function_type;
|
| + if (function_info->kind == VarKind::kTable) {
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + // TODO(bradnelson): Figure out the right debug scanner offset and
|
| + // re-enable.
|
| + // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
|
| + // scanner_.GetPosition());
|
| + current_function_builder_->Emit(kExprCallIndirect);
|
| + current_function_builder_->EmitVarUint(signature_index);
|
| + current_function_builder_->EmitVarUint(0); // table index
|
| + } else {
|
| + // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
|
| + // scanner_.GetPosition());
|
| + current_function_builder_->Emit(kExprCallFunction);
|
| + current_function_builder_->EmitDirectCallIndex(function_info->index);
|
| + }
|
| + } else if (function_info->kind > VarKind::kImportedFunction) {
|
| + AsmCallableType* callable = function_info->type->AsCallableType();
|
| + if (!callable) {
|
| + FAILn("Expected callable function");
|
| + }
|
| + // TODO(bradnelson): Refactor AsmType to not need this.
|
| + if (callable->CanBeInvokedWith(return_type, param_specific_types)) {
|
| + // Return type ok.
|
| + } else if (return_type->IsA(AsmType::Void()) &&
|
| + callable->CanBeInvokedWith(AsmType::Float(),
|
| + param_specific_types)) {
|
| + return_type = AsmType::Float();
|
| + } else if (return_type->IsA(AsmType::Void()) &&
|
| + callable->CanBeInvokedWith(AsmType::Double(),
|
| + param_specific_types)) {
|
| + return_type = AsmType::Double();
|
| + } else if (return_type->IsA(AsmType::Void()) &&
|
| + callable->CanBeInvokedWith(AsmType::Signed(),
|
| + param_specific_types)) {
|
| + return_type = AsmType::Signed();
|
| + } else {
|
| + FAILn("Function use doesn't match definition");
|
| + }
|
| + switch (function_info->kind) {
|
| +#define V(name, Name, op, sig) \
|
| + case VarKind::kMath##Name: \
|
| + current_function_builder_->Emit(op); \
|
| + break;
|
| + STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V)
|
| +#undef V
|
| +#define V(name, Name, op, sig) \
|
| + case VarKind::kMath##Name: \
|
| + if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { \
|
| + current_function_builder_->Emit(kExprF64##Name); \
|
| + } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { \
|
| + current_function_builder_->Emit(kExprF32##Name); \
|
| + } else { \
|
| + UNREACHABLE(); \
|
| + } \
|
| + break;
|
| + STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
|
| +#undef V
|
| + case VarKind::kMathMin:
|
| + case VarKind::kMathMax:
|
| + if (param_specific_types[0]->IsA(AsmType::Double())) {
|
| + for (size_t i = 1; i < param_specific_types.size(); ++i) {
|
| + if (function_info->kind == VarKind::kMathMin) {
|
| + current_function_builder_->Emit(kExprF64Min);
|
| + } else {
|
| + current_function_builder_->Emit(kExprF64Max);
|
| + }
|
| + }
|
| + } else if (param_specific_types[0]->IsA(AsmType::Float())) {
|
| + // NOTE: Not technically part of the asm.js spec, but Firefox
|
| + // accepts it.
|
| + for (size_t i = 1; i < param_specific_types.size(); ++i) {
|
| + if (function_info->kind == VarKind::kMathMin) {
|
| + current_function_builder_->Emit(kExprF32Min);
|
| + } else {
|
| + current_function_builder_->Emit(kExprF32Max);
|
| + }
|
| + }
|
| + } else if (param_specific_types[0]->IsA(AsmType::Int())) {
|
| + int32_t tmp_x = TempVariable(0);
|
| + int32_t tmp_y = TempVariable(1);
|
| + for (size_t i = 1; i < param_specific_types.size(); ++i) {
|
| + current_function_builder_->EmitSetLocal(tmp_x);
|
| + current_function_builder_->EmitTeeLocal(tmp_y);
|
| + current_function_builder_->EmitGetLocal(tmp_x);
|
| + if (function_info->kind == VarKind::kMathMin) {
|
| + current_function_builder_->Emit(kExprI32GeS);
|
| + } else {
|
| + current_function_builder_->Emit(kExprI32LeS);
|
| + }
|
| + current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
| + current_function_builder_->EmitGetLocal(tmp_x);
|
| + current_function_builder_->Emit(kExprElse);
|
| + current_function_builder_->EmitGetLocal(tmp_y);
|
| + current_function_builder_->Emit(kExprEnd);
|
| + }
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| + break;
|
| +
|
| + case VarKind::kMathAbs:
|
| + if (param_specific_types[0]->IsA(AsmType::Signed())) {
|
| + int32_t tmp = TempVariable(0);
|
| + current_function_builder_->EmitTeeLocal(tmp);
|
| + current_function_builder_->Emit(kExprI32Clz);
|
| + current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + current_function_builder_->Emit(kExprElse);
|
| + current_function_builder_->EmitI32Const(0);
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + current_function_builder_->Emit(kExprI32Sub);
|
| + current_function_builder_->Emit(kExprEnd);
|
| + } else if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF64Abs);
|
| + } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprF32Abs);
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| + break;
|
| +
|
| + case VarKind::kMathFround:
|
| + if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF32ConvertF64);
|
| + } else {
|
| + DCHECK(param_specific_types[0]->IsA(AsmType::FloatQ()));
|
| + }
|
| + break;
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + } else {
|
| + if (function_info->kind != VarKind::kFunction &&
|
| + function_info->kind != VarKind::kTable) {
|
| + FAILn("Function name collides with variable");
|
| + }
|
| + AsmCallableType* callable = function_info->type->AsCallableType();
|
| + if (!callable ||
|
| + !callable->CanBeInvokedWith(return_type, param_specific_types)) {
|
| + FAILn("Function use doesn't match definition");
|
| + }
|
| + if (function_info->kind == VarKind::kTable) {
|
| + current_function_builder_->EmitGetLocal(tmp);
|
| + // TODO(bradnelson): Figure out the right debug scanner offset and
|
| + // re-enable.
|
| + // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
|
| + // scanner_.GetPosition());
|
| + current_function_builder_->Emit(kExprCallIndirect);
|
| + current_function_builder_->EmitVarUint(signature_index);
|
| + current_function_builder_->EmitVarUint(0); // table index
|
| + } else {
|
| + // TODO(bradnelson): Figure out the right debug scanner offset and
|
| + // re-enable.
|
| + // current_function_builder_->AddAsmWasmOffset(scanner_.GetPosition(),
|
| + // scanner_.GetPosition());
|
| + current_function_builder_->Emit(kExprCallFunction);
|
| + current_function_builder_->EmitDirectCallIndex(function_info->index);
|
| + }
|
| + }
|
| +
|
| + return return_type;
|
| +}
|
| +
|
| +// 6.9 ValidateCall - helper
|
| +bool AsmJsParser::PeekCall() {
|
| + if (!scanner_.IsGlobal()) {
|
| + return false;
|
| + }
|
| + if (GetVarInfo(scanner_.Token())->kind == VarKind::kFunction) {
|
| + return true;
|
| + }
|
| + if (GetVarInfo(scanner_.Token())->kind >= VarKind::kImportedFunction) {
|
| + return true;
|
| + }
|
| + if (GetVarInfo(scanner_.Token())->kind == VarKind::kUnused ||
|
| + GetVarInfo(scanner_.Token())->kind == VarKind::kTable) {
|
| + scanner_.Next();
|
| + if (Peek('(') || Peek('[')) {
|
| + scanner_.Rewind();
|
| + return true;
|
| + }
|
| + scanner_.Rewind();
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// 6.10 ValidateHeapAccess
|
| +void AsmJsParser::ValidateHeapAccess() {
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + int32_t size = info->type->ElementSizeInBytes();
|
| + EXPECT_TOKEN('[');
|
| + uint64_t offset;
|
| + if (CheckForUnsigned(&offset)) {
|
| + // TODO(bradnelson): Check more things.
|
| + if (offset > 0x7fffffff || offset * size > 0x7fffffff) {
|
| + FAIL("Heap access out of range");
|
| + }
|
| + if (Check(']')) {
|
| + current_function_builder_->EmitI32Const(
|
| + static_cast<uint32_t>(offset * size));
|
| + // NOTE: This has to happen here to work recursively.
|
| + heap_access_type_ = info->type;
|
| + return;
|
| + } else {
|
| + scanner_.Rewind();
|
| + }
|
| + }
|
| + AsmType* index_type;
|
| + if (info->type->IsA(AsmType::Int8Array()) ||
|
| + info->type->IsA(AsmType::Uint8Array())) {
|
| + RECURSE(index_type = Expression(nullptr));
|
| + } else {
|
| + RECURSE(index_type = AdditiveExpression());
|
| + EXPECT_TOKEN(TOK(SAR));
|
| + uint64_t shift;
|
| + if (!CheckForUnsigned(&shift)) {
|
| + FAIL("Expected shift of word size");
|
| + }
|
| + if (shift > 3) {
|
| + FAIL("Expected valid heap access shift");
|
| + }
|
| + if ((1 << shift) != size) {
|
| + FAIL("Expected heap access shift to match heap view");
|
| + }
|
| + // Mask bottom bits to match asm.js behavior.
|
| + current_function_builder_->EmitI32Const(~(size - 1));
|
| + current_function_builder_->Emit(kExprI32And);
|
| + }
|
| + if (!index_type->IsA(AsmType::Intish())) {
|
| + FAIL("Expected intish index");
|
| + }
|
| + EXPECT_TOKEN(']');
|
| + // NOTE: This has to happen here to work recursively.
|
| + heap_access_type_ = info->type;
|
| +}
|
| +
|
| +// 6.11 ValidateFloatCoercion
|
| +void AsmJsParser::ValidateFloatCoercion() {
|
| + if (!scanner_.IsGlobal() ||
|
| + !GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
|
| + FAIL("Expected fround");
|
| + }
|
| + scanner_.Next();
|
| + EXPECT_TOKEN('(');
|
| + call_coercion_ = AsmType::Float();
|
| + AsmType* ret;
|
| + RECURSE(ret = ValidateExpression());
|
| + if (ret->IsA(AsmType::Floatish())) {
|
| + // Do nothing, as already a float.
|
| + } else if (ret->IsA(AsmType::DoubleQ())) {
|
| + current_function_builder_->Emit(kExprF32ConvertF64);
|
| + } else if (ret->IsA(AsmType::Signed())) {
|
| + current_function_builder_->Emit(kExprF32SConvertI32);
|
| + } else if (ret->IsA(AsmType::Unsigned())) {
|
| + current_function_builder_->Emit(kExprF32UConvertI32);
|
| + } else {
|
| + FAIL("Illegal conversion to float");
|
| + }
|
| + EXPECT_TOKEN(')');
|
| +}
|
| +
|
| +void AsmJsParser::GatherCases(std::vector<int32_t>* cases) {
|
| + int start = scanner_.GetPosition();
|
| + int depth = 0;
|
| + for (;;) {
|
| + if (Peek('{')) {
|
| + ++depth;
|
| + } else if (Peek('}')) {
|
| + --depth;
|
| + if (depth <= 0) {
|
| + break;
|
| + }
|
| + } else if (depth == 1 && Peek(TOK(case))) {
|
| + scanner_.Next();
|
| + int32_t value;
|
| + uint64_t uvalue;
|
| + if (Check('-')) {
|
| + if (!CheckForUnsigned(&uvalue)) {
|
| + break;
|
| + }
|
| + value = -static_cast<int32_t>(uvalue);
|
| + } else {
|
| + if (!CheckForUnsigned(&uvalue)) {
|
| + break;
|
| + }
|
| + value = static_cast<int32_t>(uvalue);
|
| + }
|
| + cases->push_back(value);
|
| + } else if (Peek(AsmJsScanner::kEndOfInput)) {
|
| + break;
|
| + }
|
| + scanner_.Next();
|
| + }
|
| + scanner_.Seek(start);
|
| +}
|
| +
|
| +} // namespace wasm
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|