summaryrefslogtreecommitdiff
path: root/deps/v8/src/asmjs/asm-parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/asmjs/asm-parser.cc')
-rw-r--r--deps/v8/src/asmjs/asm-parser.cc463
1 files changed, 237 insertions, 226 deletions
diff --git a/deps/v8/src/asmjs/asm-parser.cc b/deps/v8/src/asmjs/asm-parser.cc
index c18f7d1bf2..51b8f7bbc2 100644
--- a/deps/v8/src/asmjs/asm-parser.cc
+++ b/deps/v8/src/asmjs/asm-parser.cc
@@ -4,21 +4,17 @@
#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-js.h"
#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/parsing/scanner.h"
#include "src/wasm/wasm-opcodes.h"
namespace v8 {
@@ -29,23 +25,22 @@ namespace wasm {
#define FAIL_AND_RETURN(ret, msg) \
failed_ = true; \
failure_message_ = msg; \
- failure_location_ = scanner_.GetPosition(); \
+ failure_location_ = static_cast<int>(scanner_.Position()); \
if (FLAG_trace_asm_parser) { \
PrintF("[asm.js failure: %s, token: '%s', see: %s:%d]\n", msg, \
scanner_.Name(scanner_.Token()).c_str(), __FILE__, __LINE__); \
} \
return ret;
#else
-#define FAIL_AND_RETURN(ret, msg) \
- failed_ = true; \
- failure_message_ = msg; \
- failure_location_ = scanner_.GetPosition(); \
+#define FAIL_AND_RETURN(ret, msg) \
+ failed_ = true; \
+ failure_message_ = msg; \
+ failure_location_ = static_cast<int>(scanner_.Position()); \
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)
#define EXPECT_TOKEN_OR_RETURN(ret, token) \
do { \
@@ -57,7 +52,6 @@ namespace wasm {
#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 { \
@@ -71,7 +65,6 @@ namespace wasm {
#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
@@ -91,6 +84,8 @@ AsmJsParser::AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
inside_heap_assignment_(false),
heap_access_type_(nullptr),
block_stack_(zone),
+ call_coercion_(nullptr),
+ call_coercion_deferred_(nullptr),
pending_label_(0),
global_imports_(zone) {
InitializeStdlibTypes();
@@ -161,7 +156,7 @@ FunctionSig* AsmJsParser::ConvertSignature(
} else if (param->IsA(AsmType::Int())) {
sig_builder.AddParam(kWasmI32);
} else {
- return nullptr;
+ UNREACHABLE();
}
}
if (!return_type->IsA(AsmType::Void())) {
@@ -172,7 +167,7 @@ FunctionSig* AsmJsParser::ConvertSignature(
} else if (return_type->IsA(AsmType::Signed())) {
sig_builder.AddReturn(kWasmI32);
} else {
- return 0;
+ UNREACHABLE();
}
}
return sig_builder.Build();
@@ -200,16 +195,6 @@ class AsmJsParser::TemporaryVariableScope {
int local_depth_;
};
-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)) {
@@ -234,52 +219,20 @@ wasm::AsmJsParser::VarInfo* AsmJsParser::GetVarInfo(
}
uint32_t AsmJsParser::VarIndex(VarInfo* info) {
- if (info->import != nullptr) {
- return info->index;
- } else {
- return info->index + static_cast<uint32_t>(global_imports_.size());
- }
+ DCHECK(info->kind == VarKind::kGlobal);
+ return info->index + static_cast<uint32_t>(global_imports_.size());
}
-void AsmJsParser::AddGlobalImport(std::string name, AsmType* type,
+void AsmJsParser::AddGlobalImport(Vector<const char> name, AsmType* type,
ValueType vtype, bool mutable_variable,
VarInfo* info) {
- // TODO(bradnelson): Refactor memory management here.
- // AsmModuleBuilder should really own import names.
- char* name_data = zone()->NewArray<char>(name.size());
- memcpy(name_data, name.data(), name.size());
- 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_data, name.size(), 0, info->index, true});
- } else {
- // Just use the import directly.
- global_imports_.push_back({name_data, name.size(), 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;
-}
+ // Allocate a separate variable for the import.
+ // TODO(mstarzinger): Consider using the imported global directly instead of
+ // allocating a separate global variable for immutable (i.e. const) imports.
+ DeclareGlobal(info, mutable_variable, type, vtype);
-void AsmJsParser::VarInfo::DeclareStdlibFunc(VarKind kind, AsmType* type) {
- this->kind = kind;
- this->type = type;
- index = 0; // unused
- mutable_variable = false;
+ // Record the need to initialize the global from the import.
+ global_imports_.push_back({name, vtype, info});
}
void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
@@ -291,6 +244,14 @@ void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
info->mutable_variable = mutable_variable;
}
+void AsmJsParser::DeclareStdlibFunc(VarInfo* info, VarKind kind,
+ AsmType* type) {
+ info->kind = kind;
+ info->type = type;
+ info->index = 0; // unused
+ info->mutable_variable = false;
+}
+
uint32_t AsmJsParser::TempVariable(int index) {
if (index + 1 > function_temp_locals_used_) {
function_temp_locals_used_ = index + 1;
@@ -298,6 +259,13 @@ uint32_t AsmJsParser::TempVariable(int index) {
return function_temp_locals_offset_ + index;
}
+Vector<const char> AsmJsParser::CopyCurrentIdentifierString() {
+ const std::string& str = scanner_.GetIdentifierString();
+ char* buffer = zone()->NewArray<char>(str.size());
+ str.copy(buffer, str.size());
+ return Vector<const char>(buffer, static_cast<int>(str.size()));
+}
+
void AsmJsParser::SkipSemicolon() {
if (Check(';')) {
// Had a semicolon.
@@ -385,13 +353,11 @@ void AsmJsParser::ValidateModule() {
// 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()));
- }
+ for (auto& global_import : global_imports_) {
+ uint32_t import_index = module_builder_->AddGlobalImport(
+ global_import.import_name, global_import.value_type);
+ start->EmitWithI32V(kExprGetGlobal, import_index);
+ start->EmitWithI32V(kExprSetGlobal, VarIndex(global_import.var_info));
}
start->Emit(kExprEnd);
FunctionSig::Builder b(zone(), 0, 0);
@@ -459,7 +425,7 @@ void AsmJsParser::ValidateModuleVar(bool mutable_variable) {
}
EXPECT_TOKEN('=');
double dvalue = 0.0;
- uint64_t uvalue = 0;
+ uint32_t uvalue = 0;
if (CheckForDouble(&dvalue)) {
DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
WasmInitExpr(dvalue));
@@ -489,8 +455,8 @@ void AsmJsParser::ValidateModuleVar(bool mutable_variable) {
} else if (Check(stdlib_name_)) {
EXPECT_TOKEN('.');
RECURSE(ValidateModuleVarStdlib(info));
- } else if (ValidateModuleVarImport(info, mutable_variable)) {
- // Handled inside.
+ } else if (Peek(foreign_name_) || Peek('+')) {
+ RECURSE(ValidateModuleVarImport(info, mutable_variable));
} else if (scanner_.IsGlobal()) {
RECURSE(ValidateModuleVarFromGlobal(info, mutable_variable));
} else {
@@ -526,7 +492,7 @@ void AsmJsParser::ValidateModuleVarFromGlobal(VarInfo* info,
negate = true;
}
double dvalue = 0.0;
- uint64_t uvalue = 0;
+ uint32_t uvalue = 0;
if (CheckForDouble(&dvalue)) {
if (negate) {
dvalue = -dvalue;
@@ -547,38 +513,31 @@ void AsmJsParser::ValidateModuleVarFromGlobal(VarInfo* info,
}
// 6.1 ValidateModule - foreign imports
-bool AsmJsParser::ValidateModuleVarImport(VarInfo* info,
+void AsmJsParser::ValidateModuleVarImport(VarInfo* info,
bool mutable_variable) {
if (Check('+')) {
- EXPECT_TOKENf(foreign_name_);
- EXPECT_TOKENf('.');
- AddGlobalImport(scanner_.GetIdentifierString(), AsmType::Double(), kWasmF64,
- mutable_variable, info);
+ EXPECT_TOKEN(foreign_name_);
+ EXPECT_TOKEN('.');
+ Vector<const char> name = CopyCurrentIdentifierString();
+ AddGlobalImport(name, AsmType::Double(), kWasmF64, mutable_variable, info);
scanner_.Next();
- return true;
- } else if (Check(foreign_name_)) {
- EXPECT_TOKENf('.');
- std::string import_name = scanner_.GetIdentifierString();
+ } else {
+ EXPECT_TOKEN(foreign_name_);
+ EXPECT_TOKEN('.');
+ Vector<const char> name = CopyCurrentIdentifierString();
scanner_.Next();
if (Check('|')) {
if (!CheckForZero()) {
- FAILf("Expected |0 type annotation for foreign integer import");
+ FAIL("Expected |0 type annotation for foreign integer import");
}
- AddGlobalImport(import_name, AsmType::Int(), kWasmI32, mutable_variable,
- info);
- return true;
+ AddGlobalImport(name, AsmType::Int(), kWasmI32, mutable_variable, info);
+ } else {
+ info->kind = VarKind::kImportedFunction;
+ info->import = new (zone()->New(sizeof(FunctionImportInfo)))
+ FunctionImportInfo({name, WasmModuleBuilder::SignatureMap(zone())});
+ info->mutable_variable = false;
}
- info->kind = VarKind::kImportedFunction;
- function_import_info_.resize(function_import_info_.size() + 1);
- info->import = &function_import_info_.back();
- // TODO(bradnelson): Refactor memory management here.
- // AsmModuleBuilder should really own import names.
- info->import->function_name = zone()->NewArray<char>(import_name.size());
- memcpy(info->import->function_name, import_name.data(), import_name.size());
- info->import->function_name_size = import_name.size();
- return true;
}
- return false;
}
// 6.1 ValidateModule - one variable
@@ -589,7 +548,8 @@ void AsmJsParser::ValidateModuleVarNewStdlib(VarInfo* info) {
switch (Consume()) {
#define V(name, _junk1, _junk2, _junk3) \
case TOK(name): \
- info->DeclareStdlibFunc(VarKind::kSpecial, AsmType::name()); \
+ DeclareStdlibFunc(info, VarKind::kSpecial, AsmType::name()); \
+ stdlib_uses_.insert(StandardMember::k##name); \
break;
STDLIB_ARRAY_TYPE_LIST(V)
#undef V
@@ -608,18 +568,18 @@ void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
if (Check(TOK(Math))) {
EXPECT_TOKEN('.');
switch (Consume()) {
-#define V(name) \
+#define V(name, const_value) \
case TOK(name): \
DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
- WasmInitExpr(M_##name)); \
- stdlib_uses_.insert(AsmTyper::kMath##name); \
+ WasmInitExpr(const_value)); \
+ stdlib_uses_.insert(StandardMember::kMath##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); \
+ DeclareStdlibFunc(info, VarKind::kMath##Name, stdlib_##sig##_); \
+ stdlib_uses_.insert(StandardMember::kMath##Name); \
break;
STDLIB_MATH_FUNCTION_LIST(V)
#undef V
@@ -629,11 +589,11 @@ void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
} else if (Check(TOK(Infinity))) {
DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
WasmInitExpr(std::numeric_limits<double>::infinity()));
- stdlib_uses_.insert(AsmTyper::kInfinity);
+ stdlib_uses_.insert(StandardMember::kInfinity);
} else if (Check(TOK(NaN))) {
DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
- stdlib_uses_.insert(AsmTyper::kNaN);
+ stdlib_uses_.insert(StandardMember::kNaN);
} else {
FAIL("Invalid member of stdlib");
}
@@ -643,10 +603,10 @@ void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
void AsmJsParser::ValidateExport() {
// clang-format off
EXPECT_TOKEN(TOK(return));
- // clang format on
+ // clang-format on
if (Check('{')) {
for (;;) {
- std::string name = scanner_.GetIdentifierString();
+ Vector<const char> name = CopyCurrentIdentifierString();
if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
FAIL("Illegal export name");
}
@@ -659,8 +619,7 @@ void AsmJsParser::ValidateExport() {
if (info->kind != VarKind::kFunction) {
FAIL("Expected function");
}
- info->function_builder->ExportAs(
- {name.c_str(), static_cast<int>(name.size())});
+ module_builder_->AddExport(name, info->function_builder);
if (Check(',')) {
if (!Peek('}')) {
continue;
@@ -677,8 +636,8 @@ void AsmJsParser::ValidateExport() {
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));
+ module_builder_->AddExport(CStrVector(AsmJs::kSingleFunctionName),
+ info->function_builder);
}
}
@@ -711,7 +670,6 @@ void AsmJsParser::ValidateFunctionTable() {
// 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).
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");
}
@@ -744,13 +702,14 @@ void AsmJsParser::ValidateFunction() {
FAIL("Expected function name");
}
- std::string function_name_raw = scanner_.GetIdentifierString();
+ Vector<const char> function_name_str = CopyCurrentIdentifierString();
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->index = function_info->function_builder->func_index();
+ function_info->mutable_variable = false;
} else if (function_info->kind != VarKind::kFunction) {
FAIL("Function name collides with variable");
} else if (function_info->function_defined) {
@@ -758,12 +717,7 @@ void AsmJsParser::ValidateFunction() {
}
function_info->function_defined = true;
- // TODO(bradnelson): Cleanup memory management here.
- // WasmModuleBuilder should own these.
- char* function_name_chr = zone()->NewArray<char>(function_name_raw.size());
- memcpy(function_name_chr, function_name_raw.data(), function_name_raw.size());
- function_info->function_builder->SetName(
- {function_name_chr, static_cast<int>(function_name_raw.size())});
+ function_info->function_builder->SetName(function_name_str);
current_function_builder_ = function_info->function_builder;
return_type_ = nullptr;
@@ -781,21 +735,27 @@ void AsmJsParser::ValidateFunction() {
function_temp_locals_used_ = 0;
function_temp_locals_depth_ = 0;
+ bool last_statement_is_return = false;
while (!failed_ && !Peek('}')) {
+ // clang-format off
+ last_statement_is_return = Peek(TOK(return));
+ // clang-format on
RECURSE(ValidateStatement());
}
EXPECT_TOKEN('}');
- if (return_type_ == nullptr) {
- return_type_ = AsmType::Void();
+ if (!last_statement_is_return) {
+ if (return_type_ == nullptr) {
+ return_type_ = AsmType::Void();
+ } else if (!return_type_->IsA(AsmType::Void())) {
+ FAIL("Expected return at end of non-void function");
+ }
}
+ DCHECK_NOT_NULL(return_type_);
// 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);
@@ -870,7 +830,8 @@ void AsmJsParser::ValidateFunctionParams(std::vector<AsmType*>* params) {
info->index = static_cast<uint32_t>(params->size());
params->push_back(AsmType::Double());
} else {
- if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
+ if (!scanner_.IsGlobal() ||
+ !GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
FAIL("Expected fround");
}
EXPECT_TOKEN('(');
@@ -904,15 +865,14 @@ void AsmJsParser::ValidateFunctionLocals(
// Store types.
EXPECT_TOKEN('=');
double dvalue = 0.0;
- uint64_t uvalue = 0;
+ uint32_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_->EmitF64Const(-dvalue);
current_function_builder_->EmitSetLocal(info->index);
} else if (CheckForUnsigned(&uvalue)) {
if (uvalue > 0x7fffffff) {
@@ -946,7 +906,7 @@ void AsmJsParser::ValidateFunctionLocals(
} else {
FAIL("Bad local variable definition");
}
- current_function_builder_->EmitWithVarInt(kExprGetGlobal,
+ current_function_builder_->EmitWithI32V(kExprGetGlobal,
VarIndex(sinfo));
current_function_builder_->EmitSetLocal(info->index);
} else if (sinfo->type->IsA(stdlib_fround_)) {
@@ -964,8 +924,7 @@ void AsmJsParser::ValidateFunctionLocals(
if (negate) {
dvalue = -dvalue;
}
- byte code[] = {WASM_F32(dvalue)};
- current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitF32Const(dvalue);
current_function_builder_->EmitSetLocal(info->index);
} else if (CheckForUnsigned(&uvalue)) {
if (uvalue > 0x7fffffff) {
@@ -979,9 +938,8 @@ void AsmJsParser::ValidateFunctionLocals(
if (negate) {
value = -value;
}
- double fvalue = static_cast<double>(value);
- byte code[] = {WASM_F32(fvalue)};
- current_function_builder_->EmitCode(code, sizeof(code));
+ float fvalue = static_cast<float>(value);
+ current_function_builder_->EmitF32Const(fvalue);
current_function_builder_->EmitSetLocal(info->index);
} else {
FAIL("Expected variable initial value");
@@ -995,8 +953,7 @@ void AsmJsParser::ValidateFunctionLocals(
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_->EmitF64Const(dvalue);
current_function_builder_->EmitSetLocal(info->index);
} else if (CheckForUnsigned(&uvalue)) {
info->kind = VarKind::kLocal;
@@ -1020,7 +977,7 @@ void AsmJsParser::ValidateFunctionLocals(
}
}
-// ValidateStatement
+// 6.5 ValidateStatement
void AsmJsParser::ValidateStatement() {
call_coercion_ = nullptr;
if (Peek('{')) {
@@ -1106,7 +1063,7 @@ void AsmJsParser::IfStatement() {
// 6.5.5 ReturnStatement
void AsmJsParser::ReturnStatement() {
// clang-format off
- EXPECT_TOKEN(TOK(return ));
+ EXPECT_TOKEN(TOK(return));
// clang-format on
if (!Peek(';') && !Peek('}')) {
// TODO(bradnelson): See if this can be factored out.
@@ -1121,8 +1078,10 @@ void AsmJsParser::ReturnStatement() {
} else {
FAIL("Invalid return type");
}
- } else {
+ } else if (return_type_ == nullptr) {
return_type_ = AsmType::Void();
+ } else if (!return_type_->IsA(AsmType::Void())) {
+ FAIL("Invalid void return type");
}
current_function_builder_->Emit(kExprReturn);
SkipSemicolon();
@@ -1202,7 +1161,11 @@ void AsmJsParser::ForStatement() {
EXPECT_TOKEN(TOK(for));
EXPECT_TOKEN('(');
if (!Peek(';')) {
- Expression(nullptr);
+ AsmType* ret;
+ RECURSE(ret = Expression(nullptr));
+ if (!ret->IsA(AsmType::Void())) {
+ current_function_builder_->Emit(kExprDrop);
+ }
}
EXPECT_TOKEN(';');
// a: block {
@@ -1217,20 +1180,21 @@ void AsmJsParser::ForStatement() {
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);
+ // Race past INCREMENT
+ size_t increment_position = scanner_.Position();
+ ScanToClosingParenthesis();
EXPECT_TOKEN(')');
// BODY
RECURSE(ValidateStatement());
// INCREMENT
- current_function_builder_->EmitCode(
- increment_code.data(), static_cast<uint32_t>(increment_code.size()));
+ size_t end_position = scanner_.Position();
+ scanner_.Seek(increment_position);
+ if (!Peek(')')) {
+ RECURSE(Expression(nullptr));
+ // NOTE: No explicit drop because below break is an implicit drop.
+ }
current_function_builder_->EmitWithU8(kExprBr, 0);
+ scanner_.Seek(end_position);
// }
End();
// }
@@ -1250,7 +1214,7 @@ void AsmJsParser::BreakStatement() {
FAIL("Illegal break");
}
current_function_builder_->Emit(kExprBr);
- current_function_builder_->EmitVarInt(depth);
+ current_function_builder_->EmitI32V(depth);
SkipSemicolon();
}
@@ -1266,8 +1230,7 @@ void AsmJsParser::ContinueStatement() {
if (depth < 0) {
FAIL("Illegal continue");
}
- current_function_builder_->Emit(kExprBr);
- current_function_builder_->EmitVarInt(depth);
+ current_function_builder_->EmitWithI32V(kExprBr, depth);
SkipSemicolon();
}
@@ -1300,7 +1263,8 @@ void AsmJsParser::SwitchStatement() {
pending_label_ = 0;
// TODO(bradnelson): Make less weird.
std::vector<int32_t> cases;
- GatherCases(&cases); // Skips { implicitly.
+ GatherCases(&cases);
+ EXPECT_TOKEN('{');
size_t count = cases.size() + 1;
for (size_t i = 0; i < count; ++i) {
BareBegin(BlockKind::kOther);
@@ -1311,9 +1275,9 @@ void AsmJsParser::SwitchStatement() {
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_->EmitWithI32V(kExprBrIf, table_pos++);
}
- current_function_builder_->EmitWithVarInt(kExprBr, table_pos++);
+ current_function_builder_->EmitWithI32V(kExprBr, table_pos++);
while (!failed_ && Peek(TOK(case))) {
current_function_builder_->Emit(kExprEnd);
BareEnd();
@@ -1335,7 +1299,7 @@ void AsmJsParser::ValidateCase() {
if (Check('-')) {
negate = true;
}
- uint64_t uvalue;
+ uint32_t uvalue;
if (!CheckForUnsigned(&uvalue)) {
FAIL("Expected numeric literal");
}
@@ -1396,10 +1360,9 @@ AsmType* AsmJsParser::Expression(AsmType* expected) {
AsmType* AsmJsParser::NumericLiteral() {
call_coercion_ = nullptr;
double dvalue = 0.0;
- uint64_t uvalue = 0;
+ uint32_t uvalue = 0;
if (CheckForDouble(&dvalue)) {
- byte code[] = {WASM_F64(dvalue)};
- current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitF64Const(dvalue);
return AsmType::Double();
} else if (CheckForUnsigned(&uvalue)) {
if (uvalue <= 0x7fffffff) {
@@ -1431,7 +1394,7 @@ AsmType* AsmJsParser::Identifier() {
if (info->kind != VarKind::kGlobal) {
FAILn("Undefined global variable");
}
- current_function_builder_->EmitWithVarInt(kExprGetGlobal, VarIndex(info));
+ current_function_builder_->EmitWithI32V(kExprGetGlobal, VarIndex(info));
return info->type;
}
UNREACHABLE();
@@ -1463,7 +1426,8 @@ AsmType* AsmJsParser::CallExpression() {
// 6.8.5 MemberExpression
AsmType* AsmJsParser::MemberExpression() {
call_coercion_ = nullptr;
- ValidateHeapAccess();
+ RECURSEn(ValidateHeapAccess());
+ DCHECK_NOT_NULL(heap_access_type_);
if (Peek('=')) {
inside_heap_assignment_ = true;
return heap_access_type_->StoreType();
@@ -1490,6 +1454,7 @@ AsmType* AsmJsParser::AssignmentExpression() {
FAILn("Invalid assignment target");
}
inside_heap_assignment_ = false;
+ DCHECK_NOT_NULL(heap_access_type_);
AsmType* heap_type = heap_access_type_;
EXPECT_TOKENn('=');
AsmType* value;
@@ -1523,6 +1488,9 @@ AsmType* AsmJsParser::AssignmentExpression() {
if (info->kind == VarKind::kUnused) {
FAILn("Undeclared assignment target");
}
+ if (!info->mutable_variable) {
+ FAILn("Expected mutable variable in assignment");
+ }
DCHECK(is_local ? info->kind == VarKind::kLocal
: info->kind == VarKind::kGlobal);
AsmType* value;
@@ -1533,10 +1501,8 @@ AsmType* AsmJsParser::AssignmentExpression() {
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));
+ current_function_builder_->EmitWithU32V(kExprSetGlobal, VarIndex(info));
+ current_function_builder_->EmitWithU32V(kExprGetGlobal, VarIndex(info));
} else {
UNREACHABLE();
}
@@ -1554,7 +1520,7 @@ AsmType* AsmJsParser::AssignmentExpression() {
AsmType* AsmJsParser::UnaryExpression() {
AsmType* ret;
if (Check('-')) {
- uint64_t uvalue;
+ uint32_t uvalue;
if (CheckForUnsigned(&uvalue)) {
// TODO(bradnelson): was supposed to be 0x7fffffff, check errata.
if (uvalue <= 0x80000000) {
@@ -1634,9 +1600,9 @@ AsmType* AsmJsParser::UnaryExpression() {
return ret;
}
-// 6.8.8 MultaplicativeExpression
+// 6.8.8 MultiplicativeExpression
AsmType* AsmJsParser::MultiplicativeExpression() {
- uint64_t uvalue;
+ uint32_t uvalue;
if (CheckForUnsignedBelow(0x100000, &uvalue)) {
if (Check('*')) {
AsmType* a;
@@ -1644,14 +1610,16 @@ AsmType* AsmJsParser::MultiplicativeExpression() {
if (!a->IsA(AsmType::Int())) {
FAILn("Expected int");
}
- current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ int32_t value = static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
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));
+ int32_t value = -static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
if (Check('*')) {
AsmType* a;
RECURSEn(a = UnaryExpression());
@@ -1669,7 +1637,7 @@ AsmType* AsmJsParser::MultiplicativeExpression() {
RECURSEn(a = UnaryExpression());
for (;;) {
if (Check('*')) {
- uint64_t uvalue;
+ uint32_t uvalue;
if (Check('-')) {
if (CheckForUnsigned(&uvalue)) {
if (uvalue >= 0x100000) {
@@ -1678,7 +1646,8 @@ AsmType* AsmJsParser::MultiplicativeExpression() {
if (!a->IsA(AsmType::Int())) {
FAILn("Integer multiply of expects int");
}
- current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ int32_t value = -static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
current_function_builder_->Emit(kExprI32Mul);
return AsmType::Intish();
}
@@ -1690,7 +1659,8 @@ AsmType* AsmJsParser::MultiplicativeExpression() {
if (!a->IsA(AsmType::Int())) {
FAILn("Integer multiply of expects int");
}
- current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ int32_t value = static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
current_function_builder_->Emit(kExprI32Mul);
return AsmType::Intish();
}
@@ -1945,26 +1915,36 @@ AsmType* AsmJsParser::BitwiseXORExpression() {
// 6.8.15 BitwiseORExpression
AsmType* AsmJsParser::BitwiseORExpression() {
AsmType* a = nullptr;
+ call_coercion_deferred_position_ = scanner_.Position();
RECURSEn(a = BitwiseXORExpression());
while (Check('|')) {
- // TODO(bradnelson): Make it prettier.
AsmType* b = nullptr;
+ // Remember whether the first operand to this OR-expression has requested
+ // deferred validation of the |0 annotation.
+ // NOTE: This has to happen here to work recursively.
+ bool requires_zero = call_coercion_deferred_->IsExactly(AsmType::Signed());
+ call_coercion_deferred_ = nullptr;
+ // TODO(bradnelson): Make it prettier.
bool zero = false;
- int old_pos;
+ size_t old_pos;
size_t old_code;
- if (CheckForZero()) {
- old_pos = scanner_.GetPosition();
+ if (a->IsA(AsmType::Intish()) && CheckForZero()) {
+ old_pos = scanner_.Position();
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);
+ if (zero && old_pos == scanner_.Position()) {
+ current_function_builder_->DeleteCodeAfter(old_code);
a = AsmType::Signed();
continue;
}
+ // Anything not matching |0 breaks the lookahead in {ValidateCall}.
+ if (requires_zero) {
+ FAILn("Expected |0 type annotation for call");
+ }
if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
current_function_builder_->Emit(kExprI32Ior);
a = AsmType::Signed();
@@ -1972,6 +1952,7 @@ AsmType* AsmJsParser::BitwiseORExpression() {
FAILn("Expected intish for operator |.");
}
}
+ DCHECK_NULL(call_coercion_deferred_);
return a;
}
@@ -2026,6 +2007,7 @@ AsmType* AsmJsParser::ValidateCall() {
call_coercion_ = nullptr;
int call_pos = static_cast<int>(scanner_.Position());
int to_number_pos = static_cast<int>(call_coercion_position_);
+ bool allow_peek = (call_coercion_deferred_position_ == scanner_.Position());
AsmJsScanner::token_t function_name = Consume();
// Distinguish between ordinary function calls and function table calls. In
@@ -2037,30 +2019,31 @@ AsmType* AsmJsParser::ValidateCall() {
if (Check('[')) {
RECURSEn(EqualityExpression());
EXPECT_TOKENn('&');
- uint64_t mask = 0;
+ uint32_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))) {
+ if (!base::bits::IsPowerOfTwo32(mask + 1)) {
FAILn("Expected power of 2 mask");
}
- current_function_builder_->EmitI32Const(static_cast<uint32_t>(mask));
+ current_function_builder_->EmitI32Const(mask);
current_function_builder_->Emit(kExprI32And);
EXPECT_TOKENn(']');
VarInfo* function_info = GetVarInfo(function_name);
if (function_info->kind == VarKind::kUnused) {
+ uint32_t index = module_builder_->AllocateIndirectFunctions(mask + 1);
+ if (index == std::numeric_limits<uint32_t>::max()) {
+ FAILn("Exceeded maximum function table size");
+ }
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));
+ function_info->mask = mask;
+ function_info->index = index;
+ function_info->mutable_variable = false;
} else {
if (function_info->kind != VarKind::kTable) {
FAILn("Expected call table");
}
- if (function_info->mask != static_cast<int32_t>(mask)) {
+ if (function_info->mask != mask) {
FAILn("Mask size mismatch");
}
}
@@ -2077,6 +2060,7 @@ AsmType* AsmJsParser::ValidateCall() {
function_info->kind = VarKind::kFunction;
function_info->function_builder = module_builder_->AddFunction();
function_info->index = function_info->function_builder->func_index();
+ function_info->mutable_variable = false;
} else {
if (function_info->kind != VarKind::kFunction &&
function_info->kind < VarKind::kImportedFunction) {
@@ -2100,7 +2084,6 @@ AsmType* AsmJsParser::ValidateCall() {
} 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(')')) {
@@ -2109,12 +2092,30 @@ AsmType* AsmJsParser::ValidateCall() {
}
EXPECT_TOKENn(')');
+ // Reload {VarInfo} after parsing arguments as table might have grown.
+ VarInfo* function_info = GetVarInfo(function_name);
+
// We potentially use lookahead in order to determine the return type in case
- // it is not yet clear from the call context.
- // TODO(mstarzinger,6183): Several issues with look-ahead are known. Fix!
- // TODO(bradnelson): clarify how this binds, and why only float?
- if (Peek('|') &&
+ // it is not yet clear from the call context. Special care has to be taken to
+ // ensure the non-contextual lookahead is valid. The following restrictions
+ // substantiate the validity of the lookahead implemented below:
+ // - All calls (except stdlib calls) require some sort of type annotation.
+ // - The coercion to "signed" is part of the {BitwiseORExpression}, any
+ // intermittent expressions like parenthesis in `(callsite(..))|0` are
+ // syntactically not considered coercions.
+ // - The coercion to "double" as part of the {UnaryExpression} has higher
+ // precedence and wins in `+callsite(..)|0` cases. Only "float" return
+ // types are overridden in `fround(callsite(..)|0)` expressions.
+ // - Expected coercions to "signed" are flagged via {call_coercion_deferred}
+ // and later on validated as part of {BitwiseORExpression} to ensure they
+ // indeed apply to the current call expression.
+ // - The deferred validation is only allowed if {BitwiseORExpression} did
+ // promise to fulfill the request via {call_coercion_deferred_position}.
+ if (allow_peek && Peek('|') &&
+ function_info->kind <= VarKind::kImportedFunction &&
(return_type == nullptr || return_type->IsA(AsmType::Float()))) {
+ DCHECK_NULL(call_coercion_deferred_);
+ call_coercion_deferred_ = AsmType::Signed();
to_number_pos = static_cast<int>(scanner_.Position());
return_type = AsmType::Signed();
} else if (return_type == nullptr) {
@@ -2128,16 +2129,11 @@ AsmType* AsmJsParser::ValidateCall() {
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);
// Emit actual function invocation depending on the kind. At this point we
// also determined the complete function type and can perform checking against
// the expected type or update the expected type in case of first occurrence.
- // Reload {VarInfo} as table might have grown.
- VarInfo* function_info = GetVarInfo(function_name);
if (function_info->kind == VarKind::kImportedFunction) {
for (auto t : param_specific_types) {
if (!t->IsA(AsmType::Extern())) {
@@ -2149,20 +2145,17 @@ AsmType* AsmJsParser::ValidateCall() {
}
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->function_name,
- static_cast<uint32_t>(function_info->import->function_name_size),
- sig);
- function_info->import->cache_index.push_back(index);
+ auto it = function_info->import->cache.find(sig);
+ if (it != function_info->import->cache.end()) {
+ index = it->second;
} else {
- index = function_info->import->cache_index[cache_index];
+ index =
+ module_builder_->AddImport(function_info->import->function_name, sig);
+ function_info->import->cache[sig] = index;
}
current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
- current_function_builder_->Emit(kExprCallFunction);
- current_function_builder_->EmitVarUint(index);
+ current_function_builder_->EmitWithU32V(kExprCallFunction, index);
} else if (function_info->kind > VarKind::kImportedFunction) {
AsmCallableType* callable = function_info->type->AsCallableType();
if (!callable) {
@@ -2171,16 +2164,13 @@ AsmType* AsmJsParser::ValidateCall() {
// 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(),
+ } else if (callable->CanBeInvokedWith(AsmType::Float(),
param_specific_types)) {
return_type = AsmType::Float();
- } else if (return_type->IsA(AsmType::Void()) &&
- callable->CanBeInvokedWith(AsmType::Double(),
+ } else if (callable->CanBeInvokedWith(AsmType::Double(),
param_specific_types)) {
return_type = AsmType::Double();
- } else if (return_type->IsA(AsmType::Void()) &&
- callable->CanBeInvokedWith(AsmType::Signed(),
+ } else if (callable->CanBeInvokedWith(AsmType::Signed(),
param_specific_types)) {
return_type = AsmType::Signed();
} else {
@@ -2296,8 +2286,8 @@ AsmType* AsmJsParser::ValidateCall() {
current_function_builder_->EmitGetLocal(tmp.get()->get());
current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
current_function_builder_->Emit(kExprCallIndirect);
- current_function_builder_->EmitVarUint(signature_index);
- current_function_builder_->EmitVarUint(0); // table index
+ current_function_builder_->EmitU32V(signature_index);
+ current_function_builder_->EmitU32V(0); // table index
} else {
current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
current_function_builder_->Emit(kExprCallFunction);
@@ -2336,10 +2326,14 @@ void AsmJsParser::ValidateHeapAccess() {
VarInfo* info = GetVarInfo(Consume());
int32_t size = info->type->ElementSizeInBytes();
EXPECT_TOKEN('[');
- uint64_t offset;
+ uint32_t offset;
if (CheckForUnsigned(&offset)) {
// TODO(bradnelson): Check more things.
- if (offset > 0x7fffffff || offset * size > 0x7fffffff) {
+ // TODO(mstarzinger): Clarify and explain where this limit is coming from,
+ // as it is not mandated by the spec directly.
+ if (offset > 0x7fffffff ||
+ static_cast<uint64_t>(offset) * static_cast<uint64_t>(size) >
+ 0x7fffffff) {
FAIL("Heap access out of range");
}
if (Check(']')) {
@@ -2359,7 +2353,7 @@ void AsmJsParser::ValidateHeapAccess() {
} else {
RECURSE(index_type = AdditiveExpression());
EXPECT_TOKEN(TOK(SAR));
- uint64_t shift;
+ uint32_t shift;
if (!CheckForUnsigned(&shift)) {
FAIL("Expected shift of word size");
}
@@ -2409,8 +2403,25 @@ void AsmJsParser::ValidateFloatCoercion() {
EXPECT_TOKEN(')');
}
+void AsmJsParser::ScanToClosingParenthesis() {
+ int depth = 0;
+ for (;;) {
+ if (Peek('(')) {
+ ++depth;
+ } else if (Peek(')')) {
+ --depth;
+ if (depth < 0) {
+ break;
+ }
+ } else if (Peek(AsmJsScanner::kEndOfInput)) {
+ break;
+ }
+ scanner_.Next();
+ }
+}
+
void AsmJsParser::GatherCases(std::vector<int32_t>* cases) {
- int start = scanner_.GetPosition();
+ size_t start = scanner_.Position();
int depth = 0;
for (;;) {
if (Peek('{')) {
@@ -2423,7 +2434,7 @@ void AsmJsParser::GatherCases(std::vector<int32_t>* cases) {
} else if (depth == 1 && Peek(TOK(case))) {
scanner_.Next();
int32_t value;
- uint64_t uvalue;
+ uint32_t uvalue;
if (Check('-')) {
if (!CheckForUnsigned(&uvalue)) {
break;