| 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..3dbe68993bc904ebc1705f64456d56f554c022fe
|
| --- /dev/null
|
| +++ b/src/asmjs/asm-parser.cc
|
| @@ -0,0 +1,2354 @@
|
| +// 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. in MSVC.
|
| +#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 {
|
| +
|
| +#define ASSTRING1(x) #x
|
| +#define ASSTRING(x) ASSTRING1(x)
|
| +
|
| +#ifdef DEBUG
|
| +#define FAIL(msg) \
|
| + failed_ = true; \
|
| + failure_message_ = std::string(msg) + \
|
| + " token: " + scanner_.Name(scanner_.Token()) + \
|
| + " see: " + __FILE__ + ":" + ASSTRING(__LINE__); \
|
| + failure_location_ = scanner_.GetPosition(); \
|
| + return FAILURE_RETURN;
|
| +#else
|
| +#define FAIL(msg) \
|
| + failed_ = true; \
|
| + failure_message_ = msg; \
|
| + failure_location_ = scanner_.GetPosition(); \
|
| + return FAILURE_RETURN;
|
| +#endif
|
| +
|
| +#ifdef DEBUG
|
| +#define SKIP(token) \
|
| + do { \
|
| + if (scanner_.Token() != token) { \
|
| + FAIL(std::string("expected token ") + scanner_.Name(token) + \
|
| + " but found " + scanner_.Name(scanner_.Token())); \
|
| + } \
|
| + scanner_.Next(); \
|
| + } while (false);
|
| +#else
|
| +#define SKIP(token) \
|
| + do { \
|
| + if (scanner_.Token() != token) { \
|
| + FAIL("unexpected token"); \
|
| + } \
|
| + scanner_.Next(); \
|
| + } while (false);
|
| +#endif
|
| +
|
| +#define RECURSE(call) \
|
| + do { \
|
| + DCHECK(GetCurrentStackPosition() >= stack_limit_); \
|
| + call; \
|
| + if (GetCurrentStackPosition() < stack_limit_) { \
|
| + FAIL("Stack overflow while parsing asm.js module."); \
|
| + } \
|
| + if (failed_) return FAILURE_RETURN; \
|
| + } while (false);
|
| +
|
| +#define TOK(name) AsmJsScanner::kToken_##name
|
| +
|
| +AsmJsParser::AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
|
| + int start, int end)
|
| + : zone_(zone),
|
| + builder_(new (zone) WasmModuleBuilder(zone)),
|
| + return_type_(nullptr),
|
| + stack_limit_(isolate->stack_guard()->real_climit()),
|
| + failed_(false),
|
| + failure_location_(start),
|
| + inside_heap_assignment_(false),
|
| + heap_assignment_location_(0),
|
| + pending_label_(0) {
|
| + 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();
|
| +}
|
| +
|
| +#define FAILURE_RETURN
|
| +
|
| +bool AsmJsParser::Run() {
|
| + ValidateModule();
|
| + return !failed_;
|
| +}
|
| +
|
| +AsmJsParser::VarInfo::VarInfo()
|
| + : type(AsmType::None()),
|
| + index(0),
|
| + mask(0),
|
| + kind(VarKind::kUnused),
|
| + function_builder(nullptr),
|
| + mut(true),
|
| + import(nullptr) {}
|
| +
|
| +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 mut, VarInfo* info) {
|
| + if (mut) {
|
| + // 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 = builder_->AddGlobalImport(
|
| + name.data(), static_cast<int>(name.size()), vtype);
|
| + if (!mut) {
|
| + info->DeclareGlobalImport(type, gi.import_index);
|
| + }
|
| +}
|
| +
|
| +void AsmJsParser::VarInfo::DeclareGlobalImport(AsmType* type, uint32_t index) {
|
| + kind = VarKind::kGlobal;
|
| + this->type = type;
|
| + this->index = index;
|
| + mut = false;
|
| +}
|
| +
|
| +void AsmJsParser::VarInfo::DeclareStdlibFunc(VarKind kind, AsmType* type) {
|
| + this->kind = kind;
|
| + this->type = type;
|
| + index = 0; // unused
|
| + mut = false;
|
| +}
|
| +
|
| +void AsmJsParser::DeclareGlobal(VarInfo* info, bool mut, AsmType* type,
|
| + ValueType vtype, const WasmInitExpr& init) {
|
| + info->kind = VarKind::kGlobal;
|
| + info->type = type;
|
| + info->index = builder_->AddGlobal(vtype, false, true, init);
|
| + info->mut = mut;
|
| +}
|
| +
|
| +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 (Peek(';')) {
|
| + SKIP(';');
|
| + } 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::FindLabelDepth(AsmJsScanner::token_t label, bool loop) {
|
| + int count = 0;
|
| + for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
|
| + ++it, ++count) {
|
| + if (loop && it->kind != BlockKind::kLoop) {
|
| + continue;
|
| + }
|
| + if (!loop && it->kind != BlockKind::kRegular) {
|
| + continue;
|
| + }
|
| + if (label == 0 || it->label == label) {
|
| + return count;
|
| + }
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +// 6.1 ValidateModule
|
| +void AsmJsParser::ValidateModule() {
|
| + RECURSE(ValidateModuleParameters());
|
| + SKIP('{');
|
| + SKIP(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 = builder_->AddFunction();
|
| + 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() {
|
| + SKIP('(');
|
| + stdlib_name_ = 0;
|
| + foreign_name_ = 0;
|
| + heap_name_ = 0;
|
| + if (!Peek(')')) {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected stdlib parameter");
|
| + }
|
| + stdlib_name_ = Consume();
|
| + if (!Peek(')')) {
|
| + SKIP(',');
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected foreign parameter");
|
| + }
|
| + foreign_name_ = Consume();
|
| + if (!Peek(')')) {
|
| + SKIP(',');
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected heap parameter");
|
| + }
|
| + heap_name_ = Consume();
|
| + }
|
| + }
|
| + }
|
| + SKIP(')');
|
| +}
|
| +
|
| +// 6.1 ValidateModule - variables
|
| +void AsmJsParser::ValidateModuleVars() {
|
| + while (Peek(TOK(var)) || Peek(TOK(const))) {
|
| + bool mut = true;
|
| + if (Peek(TOK(var))) {
|
| + SKIP(TOK(var));
|
| + } else {
|
| + SKIP(TOK(const));
|
| + mut = false;
|
| + }
|
| + for (;;) {
|
| + RECURSE(ValidateModuleVar(mut));
|
| + if (Peek(',')) {
|
| + SKIP(',');
|
| + continue;
|
| + }
|
| + break;
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| +}
|
| +
|
| +// 6.1 ValidateModule - one variable
|
| +void AsmJsParser::ValidateModuleVar(bool mut) {
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected identifier");
|
| + }
|
| + VarInfo* info = GetVarInfo(Consume());
|
| + if (info->kind != VarKind::kUnused) {
|
| + FAIL("Redefinition of variable");
|
| + }
|
| + SKIP('=');
|
| + if (scanner_.IsDouble()) {
|
| + DeclareGlobal(info, mut, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(scanner_.AsDouble()));
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + uint64_t value = scanner_.AsUnsigned();
|
| + if (value > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + DeclareGlobal(info, mut, AsmType::Int(), kWasmI32,
|
| + WasmInitExpr(static_cast<int32_t>(value)));
|
| + scanner_.Next();
|
| + } else if (Peek('-')) {
|
| + SKIP('-');
|
| + if (scanner_.IsDouble()) {
|
| + DeclareGlobal(info, mut, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(-scanner_.AsDouble()));
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + uint64_t value = scanner_.AsUnsigned();
|
| + if (value > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + DeclareGlobal(info, mut, AsmType::Int(), kWasmI32,
|
| + WasmInitExpr(-static_cast<int32_t>(value)));
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + } else if (Peek('+')) {
|
| + SKIP('+');
|
| + SKIP(foreign_name_);
|
| + SKIP('.');
|
| + AddGlobalImport(scanner_.GetIdentifierString(), AsmType::Double(), kWasmF64,
|
| + mut, info);
|
| + scanner_.Next();
|
| + } else if (Peek(TOK(new))) {
|
| + SKIP(TOK(new));
|
| + SKIP(stdlib_name_);
|
| + SKIP('.');
|
| + switch (scanner_.Token()) {
|
| +#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;
|
| + }
|
| + scanner_.Next();
|
| + SKIP('(');
|
| + SKIP(heap_name_);
|
| + SKIP(')');
|
| + } else if (Peek(stdlib_name_)) {
|
| + SKIP(stdlib_name_);
|
| + SKIP('.');
|
| + RECURSE(ValidateModuleVarStdlib(info));
|
| + } else if (Peek(foreign_name_)) {
|
| + SKIP(foreign_name_);
|
| + SKIP('.');
|
| + std::string import_name = scanner_.GetIdentifierString();
|
| + scanner_.Next();
|
| + if (Peek('|')) {
|
| + SKIP('|');
|
| + if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() != 0) {
|
| + FAIL("Expected 0 in global signature");
|
| + }
|
| + scanner_.Next();
|
| + AddGlobalImport(import_name, AsmType::Int(), kWasmI32, mut, info);
|
| + return;
|
| + }
|
| + info->kind = VarKind::kImportedFunction;
|
| + function_import_info_.resize(function_import_info_.size() + 1);
|
| + info->import = &function_import_info_.back();
|
| + info->import->name = import_name;
|
| + } else if (scanner_.IsGlobal()) {
|
| + if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
|
| + FAIL("Expected fround");
|
| + }
|
| + SKIP('(');
|
| + bool negate = false;
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + negate = true;
|
| + }
|
| + if (scanner_.IsDouble()) {
|
| + double value = scanner_.AsDouble();
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + DeclareGlobal(info, mut, AsmType::Float(), kWasmF32,
|
| + WasmInitExpr(static_cast<float>(value)));
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + double value = scanner_.AsUnsigned();
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + DeclareGlobal(info, mut, AsmType::Float(), kWasmF32,
|
| + WasmInitExpr(static_cast<float>(value)));
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + SKIP(')');
|
| + } else {
|
| + FAIL("Bad variable declaration");
|
| + }
|
| +}
|
| +
|
| +// 6.1 ValidateModule - one variable
|
| +// 9 - Standard Library
|
| +void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
|
| + if (Peek(TOK(Math))) {
|
| + SKIP(TOK(Math));
|
| + SKIP('.');
|
| + 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 (Peek(TOK(Infinity))) {
|
| + SKIP(TOK(Infinity));
|
| + DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
|
| + WasmInitExpr(std::numeric_limits<double>::infinity()));
|
| + } else if (Peek(TOK(NaN))) {
|
| + SKIP(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
|
| + SKIP(TOK(return));
|
| + // clang format on
|
| + if (Peek('{')) {
|
| + SKIP('{');
|
| + for (;;) {
|
| + std::string name = scanner_.GetIdentifierString();
|
| + if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
|
| + FAIL("Illegal export name");
|
| + }
|
| + Consume();
|
| + SKIP(':');
|
| + 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 (Peek(',')) {
|
| + SKIP(',');
|
| + if (!Peek('}')) {
|
| + continue;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + SKIP('}');
|
| + } 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() {
|
| + SKIP(TOK(var));
|
| + if (!scanner_.IsGlobal()) {
|
| + FAIL("Expected table name");
|
| + }
|
| + VarInfo* table_info = GetVarInfo(Consume());
|
| + if (table_info->kind != VarKind::kTable) {
|
| + FAIL("Unused function table");
|
| + }
|
| + // TODO(bradnelson): Check for double use of export name.
|
| + SKIP('=');
|
| + SKIP('[');
|
| + 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 (count >= table_info->mask + 1) {
|
| + FAIL("Exceeded function table size");
|
| + }
|
| + builder_->SetIndirectFunction(
|
| + static_cast<uint32_t>(table_info->index + count), info->index);
|
| + ++count;
|
| + if (Peek(',')) {
|
| + SKIP(',');
|
| + if (!Peek(']')) {
|
| + continue;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + SKIP(']');
|
| + if (count != table_info->mask + 1) {
|
| + FAIL("Function table size does not match uses");
|
| + }
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.4 ValidateFunction
|
| +void AsmJsParser::ValidateFunction() {
|
| + int start_position = scanner_.GetPosition();
|
| + SKIP(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 = 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();
|
| + }
|
| + 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);
|
| +
|
| + scanner_.EnterLocalScope();
|
| + SKIP('(');
|
| + std::vector<AsmJsScanner::token_t> function_parameters;
|
| + while (!failed_ && !Peek(')')) {
|
| + if (!scanner_.IsLocal()) {
|
| + FAIL("Expected parameter name");
|
| + }
|
| + function_parameters.push_back(Consume());
|
| + if (!Peek(')')) {
|
| + SKIP(',');
|
| + }
|
| + }
|
| + SKIP(')');
|
| + std::vector<AsmType*> params;
|
| + std::vector<ValueType> locals;
|
| + scanner_.EnterGlobalScope();
|
| + SKIP('{');
|
| + // 5.1 Parameter Type Annotations
|
| + for (auto p : function_parameters) {
|
| + SKIP(p);
|
| + SKIP('=');
|
| + VarInfo* info = GetVarInfo(p);
|
| + if (info->kind != VarKind::kUnused) {
|
| + FAIL("Duplicate parameter name");
|
| + }
|
| + if (Peek(p)) {
|
| + SKIP(p);
|
| + SKIP('|');
|
| + if (!Peek(AsmJsScanner::kUnsigned) || scanner_.AsUnsigned() != 0) {
|
| + FAIL("Bad integer parameter annotation.");
|
| + }
|
| + scanner_.Next();
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(params.size());
|
| + params.push_back(AsmType::Int());
|
| + } else if (Peek('+')) {
|
| + SKIP('+');
|
| + SKIP(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");
|
| + }
|
| + SKIP('(');
|
| + SKIP(p);
|
| + SKIP(')');
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(params.size());
|
| + params.push_back(AsmType::Float());
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| + // Local Variables.
|
| + while (Peek(TOK(var))) {
|
| + scanner_.EnterLocalScope();
|
| + SKIP(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.
|
| + SKIP('=');
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + if (scanner_.IsDouble()) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Double();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmF64);
|
| + double value = -scanner_.AsDouble();
|
| + byte code[] = {WASM_F64(value)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + if (scanner_.AsUnsigned() > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmI32);
|
| + int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
|
| + current_function_builder_->EmitI32Const(value);
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + } else if (scanner_.IsGlobal()) {
|
| + VarInfo* sinfo = GetVarInfo(Consume());
|
| + if (sinfo->kind == VarKind::kGlobal) {
|
| + if (sinfo->mut) {
|
| + FAIL("Initializing from global requires const variable");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = sinfo->type;
|
| + info->index = static_cast<uint32_t>(params.size() + 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_)) {
|
| + SKIP('(');
|
| + bool negate = false;
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + negate = true;
|
| + }
|
| + if (scanner_.IsDouble()) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmF32);
|
| + double value = scanner_.AsDouble();
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + byte code[] = {WASM_F32(value)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + if (scanner_.AsUnsigned() > 0x7fffffff) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Float();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmF32);
|
| + int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
|
| + 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);
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + SKIP(')');
|
| + } else {
|
| + FAIL("expected fround or const global");
|
| + }
|
| + } else if (scanner_.IsDouble()) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Double();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmF64);
|
| + double value = scanner_.AsDouble();
|
| + byte code[] = {WASM_F64(value)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + scanner_.Next();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + info->kind = VarKind::kLocal;
|
| + info->type = AsmType::Int();
|
| + info->index = static_cast<uint32_t>(params.size() + locals.size());
|
| + locals.push_back(kWasmI32);
|
| + int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
|
| + current_function_builder_->EmitI32Const(value);
|
| + current_function_builder_->EmitSetLocal(info->index);
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Expected variable initial value");
|
| + }
|
| + if (!Peek(',')) {
|
| + break;
|
| + }
|
| + scanner_.EnterLocalScope();
|
| + SKIP(',');
|
| + scanner_.EnterGlobalScope();
|
| + }
|
| + SkipSemicolon();
|
| + }
|
| +
|
| + function_temp_locals_offset_ = static_cast<uint32_t>(
|
| + params.size() + locals.size());
|
| + function_temp_locals_used_ = 0;
|
| +
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| + SKIP('}');
|
| +
|
| + 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();
|
| +}
|
| +
|
| +// 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 wrap_block = pending_label_ != 0;
|
| + if (wrap_block) {
|
| + Begin(pending_label_);
|
| + }
|
| + pending_label_ = 0;
|
| + SKIP('{');
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| + SKIP('}');
|
| + if (wrap_block) {
|
| + End();
|
| + }
|
| +}
|
| +
|
| +// 6.5.2 ExpressionStatement
|
| +void AsmJsParser::ExpressionStatement() {
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + 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() { SKIP(';'); }
|
| +
|
| +// 6.5.4 IfStatement
|
| +void AsmJsParser::IfStatement() {
|
| + SKIP(TOK(if));
|
| + SKIP('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + SKIP(')');
|
| + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
| + BareBegin();
|
| + RECURSE(ValidateStatement());
|
| + if (Peek(TOK(else))) {
|
| + SKIP(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
|
| + SKIP(TOK(return ));
|
| + // clang-format on
|
| + if (!Peek(';') && !Peek('}')) {
|
| + 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() {
|
| + Begin(pending_label_);
|
| + Loop(pending_label_);
|
| + pending_label_ = 0;
|
| + SKIP(TOK(while));
|
| + SKIP('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + SKIP(')');
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + RECURSE(ValidateStatement());
|
| + current_function_builder_->EmitWithU8(kExprBr, 0);
|
| + End();
|
| + End();
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement - do
|
| +void AsmJsParser::DoStatement() {
|
| + Begin(pending_label_);
|
| + Loop();
|
| + BareBegin(BlockKind::kLoop, pending_label_);
|
| + current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
|
| + pending_label_ = 0;
|
| + SKIP(TOK(do));
|
| + RECURSE(ValidateStatement());
|
| + SKIP(TOK(while));
|
| + current_function_builder_->Emit(kExprEnd);
|
| + BareEnd();
|
| + SKIP('(');
|
| + RECURSE(Expression(AsmType::Int()));
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + current_function_builder_->EmitWithU8(kExprBr, 0);
|
| + SKIP(')');
|
| + End();
|
| + End();
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.6 IterationStatement - for
|
| +void AsmJsParser::ForStatement() {
|
| + SKIP(TOK(for));
|
| + SKIP('(');
|
| + if (!Peek(';')) {
|
| + Expression(nullptr);
|
| + }
|
| + SKIP(';');
|
| + Begin(pending_label_);
|
| + Loop(pending_label_);
|
| + pending_label_ = 0;
|
| + if (!Peek(';')) {
|
| + RECURSE(Expression(AsmType::Int()));
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + current_function_builder_->EmitWithU8(kExprBrIf, 1);
|
| + }
|
| + SKIP(';');
|
| + 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);
|
| + SKIP(')');
|
| + RECURSE(ValidateStatement());
|
| + 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() {
|
| + SKIP(TOK(break));
|
| + int depth;
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + depth = FindLabelDepth(scanner_.Token(), false);
|
| + scanner_.Next();
|
| + } else {
|
| + depth = FindLabelDepth(0, false);
|
| + }
|
| + if (depth < 0) {
|
| + FAIL("Illegal break");
|
| + }
|
| + current_function_builder_->Emit(kExprBr);
|
| + current_function_builder_->EmitVarInt(depth);
|
| + SkipSemicolon();
|
| +}
|
| +
|
| +// 6.5.8 ContinueStatement
|
| +void AsmJsParser::ContinueStatement() {
|
| + SKIP(TOK(continue));
|
| + int depth;
|
| + if (scanner_.IsGlobal() || scanner_.IsLocal()) {
|
| + depth = FindLabelDepth(scanner_.Token(), true);
|
| + scanner_.Next();
|
| + } else {
|
| + depth = FindLabelDepth(0, true);
|
| + }
|
| + 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());
|
| + if (pending_label_ != 0) {
|
| + FAIL("Double label unsupported");
|
| + }
|
| + pending_label_ = scanner_.Token();
|
| + scanner_.Next();
|
| + SKIP(':');
|
| + RECURSE(ValidateStatement());
|
| +}
|
| +
|
| +// 6.5.10 SwitchStatement
|
| +void AsmJsParser::SwitchStatement() {
|
| + SKIP(TOK(switch));
|
| + SKIP('(');
|
| + AsmType* test;
|
| + RECURSE(test = Expression(nullptr));
|
| + if (!test->IsA(AsmType::Signed())) {
|
| + FAIL("Expected signed for switch value");
|
| + }
|
| + SKIP(')');
|
| + 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());
|
| + }
|
| + SKIP('}');
|
| + End();
|
| +}
|
| +
|
| +// 6.6. ValidateCase
|
| +void AsmJsParser::ValidateCase() {
|
| + SKIP(TOK(case));
|
| + bool negate = false;
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + negate = true;
|
| + }
|
| + if (!scanner_.IsUnsigned()) {
|
| + FAIL("Expected numeric literal");
|
| + }
|
| + if ((negate && scanner_.AsUnsigned() > 0x80000000) ||
|
| + (!negate && scanner_.AsUnsigned() > 0x7fffffff)) {
|
| + FAIL("Numeric literal out of range");
|
| + }
|
| + int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
|
| + if (negate) {
|
| + value = -value;
|
| + }
|
| + scanner_.Next();
|
| + SKIP(':');
|
| + while (!failed_ && !Peek('}') && !Peek(TOK(case)) && !Peek(TOK(default))) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| +}
|
| +
|
| +// 6.7 ValidateDefault
|
| +void AsmJsParser::ValidateDefault() {
|
| + SKIP(TOK(default));
|
| + SKIP(':');
|
| + while (!failed_ && !Peek('}')) {
|
| + RECURSE(ValidateStatement());
|
| + }
|
| +}
|
| +
|
| +#undef FAILURE_RETURN
|
| +#define FAILURE_RETURN AsmType::None()
|
| +
|
| +// 6.8 ValidateExpression
|
| +AsmType* AsmJsParser::ValidateExpression() {
|
| + AsmType* ret;
|
| + RECURSE(ret = Expression(nullptr));
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.1 Expression
|
| +AsmType* AsmJsParser::Expression(AsmType* expected) {
|
| + AsmType* a;
|
| + for (;;) {
|
| + RECURSE(a = AssignmentExpression());
|
| + if (Peek(',')) {
|
| + if (a->IsA(AsmType::None())) {
|
| + FAIL("Expected actual type");
|
| + }
|
| + if (!a->IsA(AsmType::Void())) {
|
| + current_function_builder_->Emit(kExprDrop);
|
| + }
|
| + SKIP(',');
|
| + continue;
|
| + }
|
| + break;
|
| + }
|
| + if (expected != nullptr && !a->IsA(expected)) {
|
| + FAIL("Unexpected type");
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.2 NumericLiteral
|
| +AsmType* AsmJsParser::NumericLiteral() {
|
| + call_coercion_ = nullptr;
|
| + if (scanner_.Token() == AsmJsScanner::kDouble) {
|
| + double value = scanner_.AsDouble();
|
| + byte code[] = {WASM_F64(value)};
|
| + current_function_builder_->EmitCode(code, sizeof(code));
|
| + scanner_.Next();
|
| + return AsmType::Double();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + uint64_t value = scanner_.AsUnsigned();
|
| + if (value <= 0x7fffffff) {
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(value));
|
| + scanner_.Next();
|
| + return AsmType::FixNum();
|
| + } else if (value <= 0x100000000) {
|
| + current_function_builder_->EmitI32Const(static_cast<int32_t>(value));
|
| + scanner_.Next();
|
| + return AsmType::Unsigned();
|
| + } else {
|
| + FAIL("Integer numeric literal out of range.");
|
| + }
|
| + } else {
|
| + FAIL("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) {
|
| + FAIL("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) {
|
| + FAIL("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())) {
|
| + RECURSE(ret = MemberExpression());
|
| + } else if (Peek('(')) {
|
| + RECURSE(ret = ParenthesizedExpression());
|
| + } else if (PeekCall()) {
|
| + RECURSE(ret = ValidateCall());
|
| + } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
|
| + RECURSE(ret = Identifier());
|
| + } else {
|
| + RECURSE(ret = NumericLiteral());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.5 MemberExpression
|
| +AsmType* AsmJsParser::MemberExpression() {
|
| + call_coercion_ = nullptr;
|
| + int access_location = scanner_.GetPosition();
|
| + ValidateHeapAccess();
|
| + if (Peek('=')) {
|
| + inside_heap_assignment_ = true;
|
| + heap_assignment_location_ = access_location;
|
| + 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
|
| + FAIL("Expected valid heap load");
|
| + }
|
| +}
|
| +
|
| +// 6.8.6 AssignmentExpression
|
| +AsmType* AsmJsParser::AssignmentExpression() {
|
| + AsmType* ret;
|
| + if (scanner_.IsGlobal() &&
|
| + GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
|
| + int access_location = scanner_.GetPosition();
|
| + RECURSE(ret = ConditionalExpression());
|
| + if (Peek('=')) {
|
| + if (!inside_heap_assignment_ ||
|
| + access_location != heap_assignment_location_) {
|
| + FAIL("Invalid assignment target");
|
| + }
|
| + inside_heap_assignment_ = false;
|
| + AsmType* heap_type = heap_access_type_;
|
| + SKIP('=');
|
| + AsmType* value;
|
| + RECURSE(value = AssignmentExpression());
|
| + if (!value->IsA(ret)) {
|
| + FAIL("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()) {
|
| + VarInfo* info = GetVarInfo(scanner_.Token());
|
| + ret = info->type;
|
| + scanner_.Next();
|
| + if (Peek('=')) {
|
| + SKIP('=');
|
| + AsmType* value;
|
| + RECURSE(value = AssignmentExpression());
|
| + if (!value->IsA(ret)) {
|
| + FAIL("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();
|
| + RECURSE(ret = ConditionalExpression());
|
| + } else {
|
| + RECURSE(ret = ConditionalExpression());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.7 UnaryExpression
|
| +AsmType* AsmJsParser::UnaryExpression() {
|
| + AsmType* ret;
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + if (scanner_.IsUnsigned()) {
|
| + uint64_t value = scanner_.AsUnsigned();
|
| + // TODO(bradnelson): was supposed to be 0x7fffffff, check errata.
|
| + if (value <= 0x80000000) {
|
| + current_function_builder_->EmitI32Const(-static_cast<int32_t>(value));
|
| + scanner_.Next();
|
| + } else {
|
| + FAIL("Integer numeric literal out of range.");
|
| + }
|
| + ret = AsmType::Signed();
|
| + } else {
|
| + RECURSE(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 {
|
| + FAIL("expected int/double?/float?");
|
| + }
|
| + }
|
| + } else if (Peek('+')) {
|
| + SKIP('+');
|
| + call_coercion_ = AsmType::Double();
|
| + RECURSE(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 {
|
| + FAIL("expected signed/unsigned/double?/float?");
|
| + }
|
| + } else if (Peek('!')) {
|
| + SKIP('!');
|
| + RECURSE(ret = UnaryExpression());
|
| + if (!ret->IsA(AsmType::Int())) {
|
| + FAIL("expected int");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Eqz);
|
| + } else if (Peek('~')) {
|
| + SKIP('~');
|
| + if (Peek('~')) {
|
| + SKIP('~');
|
| + RECURSE(ret = UnaryExpression());
|
| + if (ret->IsA(AsmType::Double())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsSConvertF64);
|
| + } else if (ret->IsA(AsmType::FloatQ())) {
|
| + current_function_builder_->Emit(kExprI32AsmjsSConvertF32);
|
| + } else {
|
| + FAIL("expected double or float?");
|
| + }
|
| + ret = AsmType::Signed();
|
| + } else {
|
| + RECURSE(ret = UnaryExpression());
|
| + if (!ret->IsA(AsmType::Intish())) {
|
| + FAIL("operator ~ expects intish");
|
| + }
|
| + current_function_builder_->EmitI32Const(0xffffffff);
|
| + current_function_builder_->Emit(kExprI32Xor);
|
| + ret = AsmType::Signed();
|
| + }
|
| + } else {
|
| + RECURSE(ret = CallExpression());
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +// 6.8.8 MultaplicativeExpression
|
| +AsmType* AsmJsParser::MultiplicativeExpression() {
|
| + if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < 0x100000) {
|
| + int32_t value = static_cast<int32_t>(scanner_.AsUnsigned());
|
| + scanner_.Next();
|
| + if (Peek('*')) {
|
| + SKIP('*');
|
| + AsmType* a;
|
| + RECURSE(a = UnaryExpression());
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAIL("Expected int");
|
| + }
|
| + current_function_builder_->EmitI32Const(value);
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + scanner_.Rewind();
|
| + } else if (Peek('-')) {
|
| + SKIP('-');
|
| + if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < 0x100000) {
|
| + int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
|
| + current_function_builder_->EmitI32Const(value);
|
| + scanner_.Next();
|
| + if (Peek('*')) {
|
| + SKIP('*');
|
| + AsmType* a;
|
| + RECURSE(a = UnaryExpression());
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAIL("Expected int");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + return AsmType::Signed();
|
| + }
|
| + scanner_.Rewind();
|
| + }
|
| + AsmType* a;
|
| + RECURSE(a = UnaryExpression());
|
| + for (;;) {
|
| + if (Peek('*')) {
|
| + SKIP('*');
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + if (scanner_.IsUnsigned()) {
|
| + if (scanner_.AsUnsigned() >= 0x100000) {
|
| + FAIL("Constant multiple out of range");
|
| + }
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAIL("Integer multiply of expects int");
|
| + }
|
| + int32_t value = -static_cast<int32_t>(scanner_.AsUnsigned());
|
| + current_function_builder_->EmitI32Const(value);
|
| + scanner_.Next();
|
| + current_function_builder_->Emit(kExprI32Mul);
|
| + return AsmType::Intish();
|
| + }
|
| + scanner_.Rewind();
|
| + } else if (scanner_.IsUnsigned()) {
|
| + if (scanner_.AsUnsigned() >= 0x100000) {
|
| + FAIL("Constant multiple out of range");
|
| + }
|
| + if (!a->IsA(AsmType::Int())) {
|
| + FAIL("Integer multiply of expects int");
|
| + }
|
| + current_function_builder_->EmitI32Const(
|
| + static_cast<int32_t>(scanner_.AsUnsigned()));
|
| + scanner_.Next();
|
| + 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 {
|
| + FAIL("expected doubles or floats");
|
| + }
|
| + } else if (Peek('/')) {
|
| + SKIP('/');
|
| + 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 {
|
| + FAIL("expected doubles or floats");
|
| + }
|
| + } else if (Peek('%')) {
|
| + SKIP('%');
|
| + 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 {
|
| + FAIL("expected doubles or floats");
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.9 AdditiveExpression
|
| +AsmType* AsmJsParser::AdditiveExpression() {
|
| + AsmType* a = MultiplicativeExpression();
|
| + int n = 0;
|
| + for (;;) {
|
| + if (Peek('+')) {
|
| + SKIP('+');
|
| + 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)) {
|
| + FAIL("more than 2^20 additive values");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Add);
|
| + } else {
|
| + FAIL("illegal types for +");
|
| + }
|
| + } else if (Peek('-')) {
|
| + SKIP('-');
|
| + 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)) {
|
| + FAIL("more than 2^20 additive values");
|
| + }
|
| + current_function_builder_->Emit(kExprI32Sub);
|
| + } else {
|
| + FAIL("illegal types for +");
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.10 ShiftExpression
|
| +AsmType* AsmJsParser::ShiftExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSE(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): { \
|
| + SKIP(TOK(op)); \
|
| + AsmType* b = nullptr; \
|
| + RECURSE(b = AdditiveExpression()); \
|
| + if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
|
| + FAIL("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;
|
| + RECURSE(a = ShiftExpression());
|
| + for (;;) {
|
| + switch (scanner_.Token()) {
|
| +#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
|
| + case op: { \
|
| + SKIP(op); \
|
| + AsmType* b = nullptr; \
|
| + RECURSE(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 { \
|
| + FAIL("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;
|
| + RECURSE(a = RelationalExpression());
|
| + for (;;) {
|
| + switch (scanner_.Token()) {
|
| +#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
|
| + case op: { \
|
| + SKIP(op); \
|
| + AsmType* b = nullptr; \
|
| + RECURSE(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 { \
|
| + FAIL("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;
|
| + RECURSE(a = EqualityExpression());
|
| + while (Peek('&')) {
|
| + SKIP('&');
|
| + AsmType* b = nullptr;
|
| + RECURSE(b = EqualityExpression());
|
| + if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + current_function_builder_->Emit(kExprI32And);
|
| + a = AsmType::Signed();
|
| + } else {
|
| + FAIL("Expected intish for operator &.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.14 BitwiseXORExpression
|
| +AsmType* AsmJsParser::BitwiseXORExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSE(a = BitwiseANDExpression());
|
| + while (Peek('^')) {
|
| + SKIP('^');
|
| + AsmType* b = nullptr;
|
| + RECURSE(b = BitwiseANDExpression());
|
| + if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
|
| + current_function_builder_->Emit(kExprI32Xor);
|
| + a = AsmType::Signed();
|
| + } else {
|
| + FAIL("Expected intish for operator &.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.15 BitwiseORExpression
|
| +AsmType* AsmJsParser::BitwiseORExpression() {
|
| + AsmType* a = nullptr;
|
| + RECURSE(a = BitwiseXORExpression());
|
| + // }
|
| + while (Peek('|')) {
|
| + SKIP('|');
|
| + // TODO(bradnelson): Make it prettier.
|
| + AsmType* b = nullptr;
|
| + bool zero = false;
|
| + int old_pos;
|
| + size_t old_code;
|
| + if (scanner_.IsUnsigned() && scanner_.AsUnsigned() == 0) {
|
| + scanner_.Next();
|
| + old_pos = scanner_.GetPosition();
|
| + old_code = current_function_builder_->GetPosition();
|
| + scanner_.Rewind();
|
| + zero = true;
|
| + }
|
| + RECURSE(b = BitwiseXORExpression());
|
| + // Handle |0 specially.
|
| + if (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 {
|
| + FAIL("Expected intish for operator |.");
|
| + }
|
| + }
|
| + return a;
|
| +}
|
| +
|
| +// 6.8.16 ConditionalExpression
|
| +AsmType* AsmJsParser::ConditionalExpression() {
|
| + AsmType* test = nullptr;
|
| + RECURSE(test = BitwiseORExpression());
|
| + if (Peek('?')) {
|
| + SKIP('?');
|
| + if (!test->IsA(AsmType::Int())) {
|
| + FAIL("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;
|
| + RECURSE(cons = AssignmentExpression());
|
| + current_function_builder_->Emit(kExprElse);
|
| + SKIP(':');
|
| + AsmType* alt = nullptr;
|
| + RECURSE(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 {
|
| + FAIL("Type mismatch in ternary operator.");
|
| + }
|
| + } else {
|
| + return test;
|
| + }
|
| +}
|
| +
|
| +// 6.8.17 ParenthesiedExpression
|
| +AsmType* AsmJsParser::ParenthesizedExpression() {
|
| + call_coercion_ = nullptr;
|
| + AsmType* ret;
|
| + SKIP('(');
|
| + RECURSE(ret = Expression(nullptr));
|
| + SKIP(')');
|
| + 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 (Peek('[')) {
|
| + SKIP('[');
|
| + RECURSE(EqualityExpression());
|
| + SKIP('&');
|
| + if (!scanner_.IsUnsigned()) {
|
| + FAIL("Expected mask literal");
|
| + }
|
| + uint64_t value = scanner_.AsUnsigned();
|
| + if (value > 0x7fffffff) {
|
| + FAIL("Expected power of 2 mask");
|
| + }
|
| + if (!base::bits::IsPowerOfTwo32(static_cast<uint32_t>(1 + value))) {
|
| + FAIL("Expected power of 2 mask");
|
| + }
|
| + scanner_.Next();
|
| + current_function_builder_->EmitI32Const(static_cast<uint32_t>(value));
|
| + current_function_builder_->Emit(kExprI32And);
|
| + SKIP(']');
|
| + VarInfo* function_info = GetVarInfo(function_name);
|
| + if (function_info->kind == VarKind::kUnused) {
|
| + function_info->kind = VarKind::kTable;
|
| + function_info->mask = value;
|
| + function_info->index =
|
| + builder_->AllocateIndirectFunctions(static_cast<uint32_t>(value + 1));
|
| + } else {
|
| + if (function_info->kind != VarKind::kTable) {
|
| + FAIL("Expected call table");
|
| + }
|
| + if (function_info->mask != value) {
|
| + FAIL("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());
|
| + SKIP('(');
|
| + while (!failed_ && !Peek(')')) {
|
| + AsmType* t;
|
| + RECURSE(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();
|
| + FAIL("Bad function argument type");
|
| + }
|
| + if (!Peek(')')) {
|
| + SKIP(',');
|
| + }
|
| + }
|
| + SKIP(')');
|
| + // 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) {
|
| + FAIL("Invalid function signature");
|
| + }
|
| + uint32_t signature_index = 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 = 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())) {
|
| + FAIL("Imported function args must be type extern");
|
| + }
|
| + }
|
| + if (return_type->IsA(AsmType::Float())) {
|
| + FAIL("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 = 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) {
|
| + FAIL("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 {
|
| + FAIL("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) {
|
| + FAIL("Function name collides with variable");
|
| + }
|
| + AsmCallableType* callable = function_info->type->AsCallableType();
|
| + if (!callable ||
|
| + !callable->CanBeInvokedWith(return_type, param_specific_types)) {
|
| + FAIL("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;
|
| +}
|
| +
|
| +#undef FAILURE_RETURN
|
| +#define FAILURE_RETURN
|
| +
|
| +// 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();
|
| + SKIP('[');
|
| + if (scanner_.IsUnsigned()) {
|
| + // TODO(bradnelson): Check more things.
|
| + uint64_t offset = scanner_.AsUnsigned();
|
| + if (offset > 0x7fffffff || offset * size > 0x7fffffff) {
|
| + FAIL("Heap access out of range");
|
| + }
|
| + scanner_.Next();
|
| + if (Peek(']')) {
|
| + SKIP(']');
|
| + 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());
|
| + SKIP(TOK(SAR));
|
| + if (!scanner_.IsUnsigned()) {
|
| + FAIL("Expected shift of word size");
|
| + }
|
| + if (scanner_.AsUnsigned() > 3) {
|
| + FAIL("Expected valid heap access shift");
|
| + }
|
| + if ((1 << scanner_.AsUnsigned()) != size) {
|
| + FAIL("Expected heap access shift to match heap view");
|
| + }
|
| + scanner_.Next();
|
| + // 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");
|
| + }
|
| + SKIP(']');
|
| + // 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();
|
| + SKIP('(');
|
| + 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");
|
| + }
|
| + SKIP(')');
|
| +}
|
| +
|
| +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;
|
| + if (Peek('-')) {
|
| + SKIP('-');
|
| + if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() > 0x80000000) {
|
| + break;
|
| + }
|
| + value = -static_cast<int32_t>(scanner_.AsUnsigned());
|
| + } else {
|
| + if (!scanner_.IsUnsigned() || scanner_.AsUnsigned() > 0x7fffffff) {
|
| + break;
|
| + }
|
| + value = static_cast<int32_t>(scanner_.AsUnsigned());
|
| + }
|
| + cases->push_back(value);
|
| + } else if (Peek(AsmJsScanner::kEndOfInput)) {
|
| + break;
|
| + }
|
| + scanner_.Next();
|
| + }
|
| + scanner_.Seek(start);
|
| +}
|
| +
|
| +} // namespace wasm
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|