[wasm] asm.js - Parse and convert asm.js to wasm a function at a time.
Make the AsmWasmBuilder drive the process of typing and potentially parsing function bodies. This will allow us to keep only a single asm.js function's AST in memory as we convert to WebAssembly. This is needed to keep our memory footprint low. Add some additional output to a few tests that's helpful to see which stage they fail at. BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203 LOG=N R=marja@chromium.org,adamk@chromium.org,aseemgarg@chromium.org,titzer@chromium.org Review-Url: https://codereview.chromium.org/2398023002 Cr-Commit-Position: refs/heads/master@{#41372}
This commit is contained in:
parent
d385ed069b
commit
14e05c1046
@ -153,27 +153,27 @@ bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
|
||||
|
||||
MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
|
||||
ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
|
||||
wasm::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
|
||||
info->literal());
|
||||
if (!typer.Validate()) {
|
||||
wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
|
||||
info->ast_value_factory(), *info->script(),
|
||||
info->literal());
|
||||
Handle<FixedArray> foreign_globals;
|
||||
auto asm_wasm_result = builder.Run(&foreign_globals);
|
||||
if (!asm_wasm_result.success) {
|
||||
DCHECK(!info->isolate()->has_pending_exception());
|
||||
PrintF("Validation of asm.js module failed: %s\n", typer.error_message());
|
||||
PrintF("Validation of asm.js module failed: %s\n",
|
||||
builder.typer()->error_message());
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
|
||||
info->literal(), &typer);
|
||||
i::Handle<i::FixedArray> foreign_globals;
|
||||
auto asm_wasm_result = builder.Run(&foreign_globals);
|
||||
wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
|
||||
wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
|
||||
|
||||
i::MaybeHandle<i::JSObject> compiled = wasm::CreateModuleObjectFromBytes(
|
||||
MaybeHandle<JSObject> compiled = wasm::CreateModuleObjectFromBytes(
|
||||
info->isolate(), module->begin(), module->end(), &thrower,
|
||||
internal::wasm::kAsmJsOrigin, info->script(), asm_offsets->begin(),
|
||||
asm_offsets->end());
|
||||
DCHECK(!compiled.is_null());
|
||||
|
||||
wasm::AsmTyper::StdlibSet uses = typer.StdlibUses();
|
||||
wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
|
||||
Handle<FixedArray> uses_array =
|
||||
info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
|
||||
int count = 0;
|
||||
|
@ -19,14 +19,19 @@
|
||||
#include "src/globals.h"
|
||||
#include "src/utils.h"
|
||||
|
||||
#define FAIL_LINE(line, msg) \
|
||||
do { \
|
||||
base::OS::SNPrintF(error_message_, sizeof(error_message_), \
|
||||
"asm: line %d: %s", (line) + 1, msg); \
|
||||
return AsmType::None(); \
|
||||
} while (false)
|
||||
|
||||
#define FAIL(node, msg) \
|
||||
do { \
|
||||
int line = node->position() == kNoSourcePosition \
|
||||
? -1 \
|
||||
: script_->GetLineNumber(node->position()); \
|
||||
base::OS::SNPrintF(error_message_, sizeof(error_message_), \
|
||||
"asm: line %d: %s", line + 1, msg); \
|
||||
return AsmType::None(); \
|
||||
FAIL_LINE(line, msg); \
|
||||
} while (false)
|
||||
|
||||
#define RECURSE(call) \
|
||||
@ -90,6 +95,53 @@ Statement* AsmTyper::FlattenedStatements::Next() {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementation of AsmTyper::SourceLayoutTracker
|
||||
|
||||
bool AsmTyper::SourceLayoutTracker::IsValid() const {
|
||||
const Section* kAllSections[] = {&use_asm_, &globals_, &functions_, &tables_,
|
||||
&exports_};
|
||||
for (size_t ii = 0; ii < arraysize(kAllSections); ++ii) {
|
||||
const auto& curr_section = *kAllSections[ii];
|
||||
for (size_t jj = ii + 1; jj < arraysize(kAllSections); ++jj) {
|
||||
if (curr_section.IsPrecededBy(*kAllSections[jj])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsmTyper::SourceLayoutTracker::Section::AddNewElement(
|
||||
const AstNode& node) {
|
||||
const int node_pos = node.position();
|
||||
if (start_ == kNoSourcePosition) {
|
||||
start_ = node_pos;
|
||||
} else {
|
||||
start_ = std::min(start_, node_pos);
|
||||
}
|
||||
if (end_ == kNoSourcePosition) {
|
||||
end_ = node_pos;
|
||||
} else {
|
||||
end_ = std::max(end_, node_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool AsmTyper::SourceLayoutTracker::Section::IsPrecededBy(
|
||||
const Section& other) const {
|
||||
if (start_ == kNoSourcePosition) {
|
||||
DCHECK_EQ(end_, kNoSourcePosition);
|
||||
return false;
|
||||
}
|
||||
if (other.start_ == kNoSourcePosition) {
|
||||
DCHECK_EQ(other.end_, kNoSourcePosition);
|
||||
return false;
|
||||
}
|
||||
DCHECK_LE(start_, end_);
|
||||
DCHECK_LE(other.start_, other.end_);
|
||||
return other.start_ <= end_;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementation of AsmTyper::VariableInfo
|
||||
|
||||
@ -112,10 +164,10 @@ AsmTyper::VariableInfo* AsmTyper::VariableInfo::Clone(Zone* zone) const {
|
||||
return new_var_info;
|
||||
}
|
||||
|
||||
void AsmTyper::VariableInfo::FirstForwardUseIs(VariableProxy* var) {
|
||||
DCHECK(first_forward_use_ == nullptr);
|
||||
void AsmTyper::VariableInfo::SetFirstForwardUse(int source_location) {
|
||||
DCHECK(source_location_ == -1);
|
||||
missing_definition_ = true;
|
||||
first_forward_use_ = var;
|
||||
source_location_ = source_location;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -137,9 +189,11 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
|
||||
local_scope_(ZoneHashMap::kDefaultHashMapCapacity,
|
||||
ZoneAllocationPolicy(zone)),
|
||||
stack_limit_(isolate->stack_guard()->real_climit()),
|
||||
node_types_(zone_),
|
||||
module_node_types_(zone_),
|
||||
function_node_types_(zone_),
|
||||
fround_type_(AsmType::FroundType(zone_)),
|
||||
ffi_type_(AsmType::FFIType(zone_)) {
|
||||
ffi_type_(AsmType::FFIType(zone_)),
|
||||
function_pointer_tables_(zone_) {
|
||||
InitializeStdlib();
|
||||
}
|
||||
|
||||
@ -345,7 +399,9 @@ AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const {
|
||||
}
|
||||
|
||||
void AsmTyper::AddForwardReference(VariableProxy* proxy, VariableInfo* info) {
|
||||
info->FirstForwardUseIs(proxy);
|
||||
info->SetFirstForwardUse(proxy->position() == kNoSourcePosition
|
||||
? -1
|
||||
: script_->GetLineNumber(proxy->position()));
|
||||
forward_definitions_.push_back(info);
|
||||
}
|
||||
|
||||
@ -390,13 +446,22 @@ bool AsmTyper::AddLocal(Variable* variable, VariableInfo* info) {
|
||||
|
||||
void AsmTyper::SetTypeOf(AstNode* node, AsmType* type) {
|
||||
DCHECK_NE(type, AsmType::None());
|
||||
DCHECK(node_types_.find(node) == node_types_.end());
|
||||
node_types_.insert(std::make_pair(node, type));
|
||||
if (in_function_) {
|
||||
DCHECK(function_node_types_.find(node) == function_node_types_.end());
|
||||
function_node_types_.insert(std::make_pair(node, type));
|
||||
} else {
|
||||
DCHECK(module_node_types_.find(node) == module_node_types_.end());
|
||||
module_node_types_.insert(std::make_pair(node, type));
|
||||
}
|
||||
}
|
||||
|
||||
AsmType* AsmTyper::TypeOf(AstNode* node) const {
|
||||
auto node_type_iter = node_types_.find(node);
|
||||
if (node_type_iter != node_types_.end()) {
|
||||
auto node_type_iter = function_node_types_.find(node);
|
||||
if (node_type_iter != function_node_types_.end()) {
|
||||
return node_type_iter->second;
|
||||
}
|
||||
node_type_iter = module_node_types_.find(node);
|
||||
if (node_type_iter != module_node_types_.end()) {
|
||||
return node_type_iter->second;
|
||||
}
|
||||
|
||||
@ -434,12 +499,34 @@ AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
|
||||
}
|
||||
|
||||
bool AsmTyper::Validate() {
|
||||
if (!AsmType::None()->IsExactly(ValidateModule(root_))) {
|
||||
return ValidateBeforeFunctionsPhase() &&
|
||||
!AsmType::None()->IsExactly(ValidateModuleFunctions(root_)) &&
|
||||
ValidateAfterFunctionsPhase();
|
||||
}
|
||||
|
||||
bool AsmTyper::ValidateBeforeFunctionsPhase() {
|
||||
if (!AsmType::None()->IsExactly(ValidateModuleBeforeFunctionsPhase(root_))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsmTyper::ValidateInnerFunction(FunctionDeclaration* fun_decl) {
|
||||
if (!AsmType::None()->IsExactly(ValidateModuleFunction(fun_decl))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsmTyper::ValidateAfterFunctionsPhase() {
|
||||
if (!AsmType::None()->IsExactly(ValidateModuleAfterFunctionsPhase(root_))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AsmTyper::ClearFunctionNodeTypes() { function_node_types_.clear(); }
|
||||
|
||||
namespace {
|
||||
bool IsUseAsmDirective(Statement* first_statement) {
|
||||
ExpressionStatement* use_asm = first_statement->AsExpressionStatement();
|
||||
@ -477,89 +564,7 @@ Assignment* ExtractInitializerExpression(Statement* statement) {
|
||||
} // namespace
|
||||
|
||||
// 6.1 ValidateModule
|
||||
namespace {
|
||||
// SourceLayoutTracker keeps track of the start and end positions of each
|
||||
// section in the asm.js source. The sections should not overlap, otherwise the
|
||||
// asm.js source is invalid.
|
||||
class SourceLayoutTracker {
|
||||
public:
|
||||
SourceLayoutTracker() = default;
|
||||
|
||||
bool IsValid() const {
|
||||
const Section* kAllSections[] = {&use_asm_, &globals_, &functions_,
|
||||
&tables_, &exports_};
|
||||
for (size_t ii = 0; ii < arraysize(kAllSections); ++ii) {
|
||||
const auto& curr_section = *kAllSections[ii];
|
||||
for (size_t jj = ii + 1; jj < arraysize(kAllSections); ++jj) {
|
||||
if (curr_section.OverlapsWith(*kAllSections[jj])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddUseAsm(const AstNode& node) { use_asm_.AddNewElement(node); }
|
||||
|
||||
void AddGlobal(const AstNode& node) { globals_.AddNewElement(node); }
|
||||
|
||||
void AddFunction(const AstNode& node) { functions_.AddNewElement(node); }
|
||||
|
||||
void AddTable(const AstNode& node) { tables_.AddNewElement(node); }
|
||||
|
||||
void AddExport(const AstNode& node) { exports_.AddNewElement(node); }
|
||||
|
||||
private:
|
||||
class Section {
|
||||
public:
|
||||
Section() = default;
|
||||
Section(const Section&) = default;
|
||||
Section& operator=(const Section&) = default;
|
||||
|
||||
void AddNewElement(const AstNode& node) {
|
||||
const int node_pos = node.position();
|
||||
if (start_ == kNoSourcePosition) {
|
||||
start_ = node_pos;
|
||||
} else {
|
||||
start_ = std::max(start_, node_pos);
|
||||
}
|
||||
if (end_ == kNoSourcePosition) {
|
||||
end_ = node_pos;
|
||||
} else {
|
||||
end_ = std::max(end_, node_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool OverlapsWith(const Section& other) const {
|
||||
if (start_ == kNoSourcePosition) {
|
||||
DCHECK_EQ(end_, kNoSourcePosition);
|
||||
return false;
|
||||
}
|
||||
if (other.start_ == kNoSourcePosition) {
|
||||
DCHECK_EQ(other.end_, kNoSourcePosition);
|
||||
return false;
|
||||
}
|
||||
return other.start_ < end_ || other.end_ < start_;
|
||||
}
|
||||
|
||||
private:
|
||||
int start_ = kNoSourcePosition;
|
||||
int end_ = kNoSourcePosition;
|
||||
};
|
||||
|
||||
Section use_asm_;
|
||||
Section globals_;
|
||||
Section functions_;
|
||||
Section tables_;
|
||||
Section exports_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SourceLayoutTracker);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
|
||||
SourceLayoutTracker source_layout;
|
||||
|
||||
AsmType* AsmTyper::ValidateModuleBeforeFunctionsPhase(FunctionLiteral* fun) {
|
||||
DeclarationScope* scope = fun->scope();
|
||||
if (!scope->is_function_scope()) FAIL(fun, "Not at function scope.");
|
||||
if (!ValidAsmIdentifier(fun->name()))
|
||||
@ -594,7 +599,6 @@ AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
|
||||
}
|
||||
}
|
||||
|
||||
ZoneVector<Assignment*> function_pointer_tables(zone_);
|
||||
FlattenedStatements iter(zone_, fun->body());
|
||||
auto* use_asm_directive = iter.Next();
|
||||
if (use_asm_directive == nullptr) {
|
||||
@ -616,8 +620,8 @@ AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
|
||||
if (!IsUseAsmDirective(use_asm_directive)) {
|
||||
FAIL(fun, "Missing \"use asm\".");
|
||||
}
|
||||
source_layout.AddUseAsm(*use_asm_directive);
|
||||
ReturnStatement* module_return = nullptr;
|
||||
source_layout_.AddUseAsm(*use_asm_directive);
|
||||
module_return_ = nullptr;
|
||||
|
||||
// *VIOLATION* The spec states that globals should be followed by function
|
||||
// declarations, which should be followed by function pointer tables, followed
|
||||
@ -627,40 +631,57 @@ AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
|
||||
if (auto* assign = ExtractInitializerExpression(current)) {
|
||||
if (assign->value()->IsArrayLiteral()) {
|
||||
// Save function tables for later validation.
|
||||
function_pointer_tables.push_back(assign);
|
||||
function_pointer_tables_.push_back(assign);
|
||||
} else {
|
||||
RECURSE(ValidateGlobalDeclaration(assign));
|
||||
source_layout.AddGlobal(*assign);
|
||||
source_layout_.AddGlobal(*assign);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto* current_as_return = current->AsReturnStatement()) {
|
||||
if (module_return != nullptr) {
|
||||
if (module_return_ != nullptr) {
|
||||
FAIL(fun, "Multiple export statements.");
|
||||
}
|
||||
module_return = current_as_return;
|
||||
source_layout.AddExport(*module_return);
|
||||
module_return_ = current_as_return;
|
||||
source_layout_.AddExport(*module_return_);
|
||||
continue;
|
||||
}
|
||||
|
||||
FAIL(current, "Invalid top-level statement in asm.js module.");
|
||||
}
|
||||
|
||||
return AsmType::Int(); // Any type that is not AsmType::None();
|
||||
}
|
||||
|
||||
AsmType* AsmTyper::ValidateModuleFunction(FunctionDeclaration* fun_decl) {
|
||||
RECURSE(ValidateFunction(fun_decl));
|
||||
source_layout_.AddFunction(*fun_decl);
|
||||
|
||||
return AsmType::Int(); // Any type that is not AsmType::None();
|
||||
}
|
||||
|
||||
AsmType* AsmTyper::ValidateModuleFunctions(FunctionLiteral* fun) {
|
||||
DeclarationScope* scope = fun->scope();
|
||||
Declaration::List* decls = scope->declarations();
|
||||
for (Declaration* decl : *decls) {
|
||||
if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) {
|
||||
RECURSE(ValidateFunction(fun_decl));
|
||||
source_layout.AddFunction(*fun_decl);
|
||||
RECURSE(ValidateModuleFunction(fun_decl));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* function_table : function_pointer_tables) {
|
||||
return AsmType::Int(); // Any type that is not AsmType::None();
|
||||
}
|
||||
|
||||
AsmType* AsmTyper::ValidateModuleAfterFunctionsPhase(FunctionLiteral* fun) {
|
||||
for (auto* function_table : function_pointer_tables_) {
|
||||
RECURSE(ValidateFunctionTable(function_table));
|
||||
source_layout.AddTable(*function_table);
|
||||
source_layout_.AddTable(*function_table);
|
||||
}
|
||||
|
||||
DeclarationScope* scope = fun->scope();
|
||||
Declaration::List* decls = scope->declarations();
|
||||
for (Declaration* decl : *decls) {
|
||||
if (decl->IsFunctionDeclaration()) {
|
||||
continue;
|
||||
@ -682,20 +703,20 @@ AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
|
||||
}
|
||||
|
||||
// 6.2 ValidateExport
|
||||
if (module_return == nullptr) {
|
||||
if (module_return_ == nullptr) {
|
||||
FAIL(fun, "Missing asm.js module export.");
|
||||
}
|
||||
|
||||
for (auto* forward_def : forward_definitions_) {
|
||||
if (forward_def->missing_definition()) {
|
||||
FAIL(forward_def->first_forward_use(),
|
||||
"Missing definition for forward declared identifier.");
|
||||
FAIL_LINE(forward_def->source_location(),
|
||||
"Missing definition for forward declared identifier.");
|
||||
}
|
||||
}
|
||||
|
||||
RECURSE(ValidateExport(module_return));
|
||||
RECURSE(ValidateExport(module_return_));
|
||||
|
||||
if (!source_layout.IsValid()) {
|
||||
if (!source_layout_.IsValid()) {
|
||||
FAIL(fun, "Invalid asm.js source code layout.");
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace wasm {
|
||||
|
||||
class AsmType;
|
||||
class AsmTyperHarnessBuilder;
|
||||
class SourceLayoutTracker;
|
||||
|
||||
class AsmTyper final {
|
||||
public:
|
||||
@ -69,6 +70,11 @@ class AsmTyper final {
|
||||
AsmTyper(Isolate* isolate, Zone* zone, Script* script, FunctionLiteral* root);
|
||||
|
||||
bool Validate();
|
||||
// Do asm.js validation in phases (to interleave with conversion to wasm).
|
||||
bool ValidateBeforeFunctionsPhase();
|
||||
bool ValidateInnerFunction(FunctionDeclaration* decl);
|
||||
bool ValidateAfterFunctionsPhase();
|
||||
void ClearFunctionNodeTypes();
|
||||
|
||||
const char* error_message() const { return error_message_; }
|
||||
|
||||
@ -130,7 +136,7 @@ class AsmTyper final {
|
||||
bool IsHeap() const { return standard_member_ == kHeap; }
|
||||
|
||||
void MarkDefined() { missing_definition_ = false; }
|
||||
void FirstForwardUseIs(VariableProxy* var);
|
||||
void SetFirstForwardUse(int source_location);
|
||||
|
||||
StandardMember standard_member() const { return standard_member_; }
|
||||
void set_standard_member(StandardMember standard_member) {
|
||||
@ -145,7 +151,7 @@ class AsmTyper final {
|
||||
|
||||
bool missing_definition() const { return missing_definition_; }
|
||||
|
||||
VariableProxy* first_forward_use() const { return first_forward_use_; }
|
||||
int source_location() const { return source_location_; }
|
||||
|
||||
static VariableInfo* ForSpecialSymbol(Zone* zone,
|
||||
StandardMember standard_member);
|
||||
@ -157,9 +163,11 @@ class AsmTyper final {
|
||||
// missing_definition_ is set to true for forward definition - i.e., use
|
||||
// before definition.
|
||||
bool missing_definition_ = false;
|
||||
// first_forward_use_ holds the AST node that first referenced this
|
||||
// source_location_ holds the line number that first referenced this
|
||||
// VariableInfo. Used for error messages.
|
||||
VariableProxy* first_forward_use_ = nullptr;
|
||||
// TODO(bradnelson): When merged with console change, this should
|
||||
// become a source location.
|
||||
int source_location_ = -1;
|
||||
};
|
||||
|
||||
// RAII-style manager for the in_function_ member variable.
|
||||
@ -199,6 +207,40 @@ class AsmTyper final {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(FlattenedStatements);
|
||||
};
|
||||
|
||||
class SourceLayoutTracker {
|
||||
public:
|
||||
SourceLayoutTracker() = default;
|
||||
bool IsValid() const;
|
||||
void AddUseAsm(const AstNode& node) { use_asm_.AddNewElement(node); }
|
||||
void AddGlobal(const AstNode& node) { globals_.AddNewElement(node); }
|
||||
void AddFunction(const AstNode& node) { functions_.AddNewElement(node); }
|
||||
void AddTable(const AstNode& node) { tables_.AddNewElement(node); }
|
||||
void AddExport(const AstNode& node) { exports_.AddNewElement(node); }
|
||||
|
||||
private:
|
||||
class Section {
|
||||
public:
|
||||
Section() = default;
|
||||
Section(const Section&) = default;
|
||||
Section& operator=(const Section&) = default;
|
||||
|
||||
void AddNewElement(const AstNode& node);
|
||||
bool IsPrecededBy(const Section& other) const;
|
||||
|
||||
private:
|
||||
int start_ = kNoSourcePosition;
|
||||
int end_ = kNoSourcePosition;
|
||||
};
|
||||
|
||||
Section use_asm_;
|
||||
Section globals_;
|
||||
Section functions_;
|
||||
Section tables_;
|
||||
Section exports_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SourceLayoutTracker);
|
||||
};
|
||||
|
||||
using ObjectTypeMap = ZoneMap<std::string, VariableInfo*>;
|
||||
void InitializeStdlib();
|
||||
void SetTypeOf(AstNode* node, AsmType* type);
|
||||
@ -220,7 +262,10 @@ class AsmTyper final {
|
||||
// validation failure.
|
||||
|
||||
// 6.1 ValidateModule
|
||||
AsmType* ValidateModule(FunctionLiteral* fun);
|
||||
AsmType* ValidateModuleBeforeFunctionsPhase(FunctionLiteral* fun);
|
||||
AsmType* ValidateModuleFunction(FunctionDeclaration* fun_decl);
|
||||
AsmType* ValidateModuleFunctions(FunctionLiteral* fun);
|
||||
AsmType* ValidateModuleAfterFunctionsPhase(FunctionLiteral* fun);
|
||||
AsmType* ValidateGlobalDeclaration(Assignment* assign);
|
||||
// 6.2 ValidateExport
|
||||
AsmType* ExportType(VariableProxy* fun_export);
|
||||
@ -345,13 +390,18 @@ class AsmTyper final {
|
||||
|
||||
std::uintptr_t stack_limit_;
|
||||
bool stack_overflow_ = false;
|
||||
ZoneMap<AstNode*, AsmType*> node_types_;
|
||||
ZoneMap<AstNode*, AsmType*> module_node_types_;
|
||||
ZoneMap<AstNode*, AsmType*> function_node_types_;
|
||||
static const int kErrorMessageLimit = 128;
|
||||
AsmType* fround_type_;
|
||||
AsmType* ffi_type_;
|
||||
char error_message_[kErrorMessageLimit];
|
||||
StdlibSet stdlib_uses_;
|
||||
|
||||
SourceLayoutTracker source_layout_;
|
||||
ReturnStatement* module_return_;
|
||||
ZoneVector<Assignment*> function_pointer_tables_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(AsmTyper);
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,10 @@
|
||||
|
||||
#include "src/ast/ast.h"
|
||||
#include "src/ast/scopes.h"
|
||||
#include "src/codegen.h"
|
||||
#include "src/compiler.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/parsing/parse-info.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -42,8 +46,9 @@ struct ForeignVariable {
|
||||
|
||||
class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
public:
|
||||
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal,
|
||||
AsmTyper* typer)
|
||||
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone,
|
||||
AstValueFactory* ast_value_factory, Script* script,
|
||||
FunctionLiteral* literal, AsmTyper* typer)
|
||||
: local_variables_(ZoneHashMap::kDefaultHashMapCapacity,
|
||||
ZoneAllocationPolicy(zone)),
|
||||
functions_(ZoneHashMap::kDefaultHashMapCapacity,
|
||||
@ -56,12 +61,14 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
literal_(literal),
|
||||
isolate_(isolate),
|
||||
zone_(zone),
|
||||
ast_value_factory_(ast_value_factory),
|
||||
script_(script),
|
||||
typer_(typer),
|
||||
typer_failed_(false),
|
||||
breakable_blocks_(zone),
|
||||
foreign_variables_(zone),
|
||||
init_function_(nullptr),
|
||||
foreign_init_function_(nullptr),
|
||||
next_table_index_(0),
|
||||
function_tables_(ZoneHashMap::kDefaultHashMapCapacity,
|
||||
ZoneAllocationPolicy(zone)),
|
||||
imported_function_table_(this) {
|
||||
@ -92,8 +99,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
}
|
||||
|
||||
i::Handle<i::FixedArray> GetForeignArgs() {
|
||||
i::Handle<FixedArray> ret = isolate_->factory()->NewFixedArray(
|
||||
Handle<FixedArray> GetForeignArgs() {
|
||||
Handle<FixedArray> ret = isolate_->factory()->NewFixedArray(
|
||||
static_cast<int>(foreign_variables_.size()));
|
||||
for (size_t i = 0; i < foreign_variables_.size(); ++i) {
|
||||
ForeignVariable* fv = &foreign_variables_[i];
|
||||
@ -102,10 +109,21 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Build() {
|
||||
bool Build() {
|
||||
InitializeInitFunction();
|
||||
RECURSE(VisitFunctionLiteral(literal_));
|
||||
if (!typer_->ValidateBeforeFunctionsPhase()) {
|
||||
return false;
|
||||
}
|
||||
DCHECK(!HasStackOverflow());
|
||||
VisitFunctionLiteral(literal_);
|
||||
if (HasStackOverflow()) {
|
||||
return false;
|
||||
}
|
||||
if (typer_failed_) {
|
||||
return false;
|
||||
}
|
||||
BuildForeignInitFunction();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitVariableDeclaration(VariableDeclaration* decl) {}
|
||||
@ -113,12 +131,62 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
||||
DCHECK_EQ(kModuleScope, scope_);
|
||||
DCHECK_NULL(current_function_builder_);
|
||||
FunctionLiteral* old_func = decl->fun();
|
||||
Zone zone(isolate_->allocator(), ZONE_NAME);
|
||||
DeclarationScope* new_func_scope = nullptr;
|
||||
if (decl->fun()->body() == nullptr) {
|
||||
// TODO(bradnelson): Refactor parser so we don't need a
|
||||
// SharedFunctionInfo to parse a single function,
|
||||
// or squirrel away the SharedFunctionInfo to use later.
|
||||
Handle<SharedFunctionInfo> shared =
|
||||
isolate_->factory()->NewSharedFunctionInfoForLiteral(
|
||||
decl->fun(), handle(script_, isolate_));
|
||||
shared->set_is_toplevel(false);
|
||||
ParseInfo info(&zone, handle(script_, isolate_));
|
||||
info.set_shared_info(shared);
|
||||
info.set_toplevel(false);
|
||||
info.set_language_mode(decl->fun()->scope()->language_mode());
|
||||
info.set_allow_lazy_parsing(false);
|
||||
info.set_function_literal_id(shared->function_literal_id());
|
||||
info.set_ast_value_factory(ast_value_factory_);
|
||||
info.set_ast_value_factory_owned(false);
|
||||
// Create fresh function scope to use to parse the function in.
|
||||
new_func_scope = new (info.zone()) DeclarationScope(
|
||||
info.zone(), decl->fun()->scope()->outer_scope(), FUNCTION_SCOPE);
|
||||
info.set_asm_function_scope(new_func_scope);
|
||||
if (!Compiler::ParseAndAnalyze(&info)) {
|
||||
typer_failed_ = true;
|
||||
return;
|
||||
}
|
||||
FunctionLiteral* func = info.literal();
|
||||
DCHECK_NOT_NULL(func);
|
||||
decl->set_fun(func);
|
||||
}
|
||||
if (!typer_->ValidateInnerFunction(decl)) {
|
||||
typer_failed_ = true;
|
||||
decl->set_fun(old_func);
|
||||
if (new_func_scope != nullptr) {
|
||||
DCHECK_EQ(new_func_scope, decl->scope()->inner_scope());
|
||||
if (!decl->scope()->RemoveInnerScope(new_func_scope)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
current_function_builder_ = LookupOrInsertFunction(decl->proxy()->var());
|
||||
scope_ = kFuncScope;
|
||||
RECURSE(Visit(decl->fun()));
|
||||
decl->set_fun(old_func);
|
||||
if (new_func_scope != nullptr) {
|
||||
DCHECK_EQ(new_func_scope, decl->scope()->inner_scope());
|
||||
if (!decl->scope()->RemoveInnerScope(new_func_scope)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
scope_ = kModuleScope;
|
||||
current_function_builder_ = nullptr;
|
||||
local_variables_.Clear();
|
||||
typer_->ClearFunctionNodeTypes();
|
||||
}
|
||||
|
||||
void VisitStatements(ZoneList<Statement*>* stmts) {
|
||||
@ -129,6 +197,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
continue;
|
||||
}
|
||||
RECURSE(Visit(stmt));
|
||||
if (typer_failed_) break;
|
||||
if (stmt->IsJump()) break;
|
||||
}
|
||||
}
|
||||
@ -245,6 +314,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
void VisitReturnStatement(ReturnStatement* stmt) {
|
||||
if (scope_ == kModuleScope) {
|
||||
if (!typer_->ValidateAfterFunctionsPhase()) {
|
||||
typer_failed_ = true;
|
||||
return;
|
||||
}
|
||||
scope_ = kExportScope;
|
||||
RECURSE(Visit(stmt->expression()));
|
||||
scope_ = kModuleScope;
|
||||
@ -448,8 +521,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
RECURSE(VisitStatements(expr->body()));
|
||||
RECURSE(VisitDeclarations(scope->declarations()));
|
||||
if (typer_failed_) return;
|
||||
RECURSE(VisitStatements(expr->body()));
|
||||
}
|
||||
|
||||
void VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
|
||||
@ -660,10 +734,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
current_function_builder_ = nullptr;
|
||||
}
|
||||
|
||||
void AddFunctionTable(VariableProxy* table, ArrayLiteral* funcs) {
|
||||
auto* func_tbl_type = typer_->TypeOf(funcs)->AsFunctionTableType();
|
||||
DCHECK_NOT_NULL(func_tbl_type);
|
||||
auto* func_type = func_tbl_type->signature()->AsFunctionType();
|
||||
struct FunctionTableIndices : public ZoneObject {
|
||||
uint32_t start_index;
|
||||
uint32_t signature_index;
|
||||
};
|
||||
|
||||
FunctionTableIndices* LookupOrAddFunctionTable(VariableProxy* table,
|
||||
Property* p) {
|
||||
FunctionTableIndices* indices = LookupFunctionTable(table->var());
|
||||
if (indices != nullptr) {
|
||||
// Already setup.
|
||||
return indices;
|
||||
}
|
||||
indices = new (zone()) FunctionTableIndices();
|
||||
auto* func_type = typer_->TypeOf(p)->AsFunctionType();
|
||||
auto* func_table_type = typer_->TypeOf(p->obj()->AsVariableProxy()->var())
|
||||
->AsFunctionTableType();
|
||||
const auto& arguments = func_type->Arguments();
|
||||
LocalType return_type = TypeFrom(func_type->ReturnType());
|
||||
FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1,
|
||||
@ -675,38 +761,37 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
sig.AddParam(TypeFrom(arg));
|
||||
}
|
||||
uint32_t signature_index = builder_->AddSignature(sig.Build());
|
||||
InsertFunctionTable(table->var(), next_table_index_, signature_index);
|
||||
next_table_index_ += funcs->values()->length();
|
||||
for (int i = 0; i < funcs->values()->length(); ++i) {
|
||||
VariableProxy* func = funcs->values()->at(i)->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(func);
|
||||
builder_->AddIndirectFunction(
|
||||
LookupOrInsertFunction(func->var())->func_index());
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionTableIndices : public ZoneObject {
|
||||
uint32_t start_index;
|
||||
uint32_t signature_index;
|
||||
};
|
||||
|
||||
void InsertFunctionTable(Variable* v, uint32_t start_index,
|
||||
uint32_t signature_index) {
|
||||
FunctionTableIndices* container = new (zone()) FunctionTableIndices();
|
||||
container->start_index = start_index;
|
||||
container->signature_index = signature_index;
|
||||
indices->start_index = builder_->AllocateIndirectFunctions(
|
||||
static_cast<uint32_t>(func_table_type->length()));
|
||||
indices->signature_index = signature_index;
|
||||
ZoneHashMap::Entry* entry = function_tables_.LookupOrInsert(
|
||||
v, ComputePointerHash(v), ZoneAllocationPolicy(zone()));
|
||||
entry->value = container;
|
||||
table->var(), ComputePointerHash(table->var()),
|
||||
ZoneAllocationPolicy(zone()));
|
||||
entry->value = indices;
|
||||
return indices;
|
||||
}
|
||||
|
||||
FunctionTableIndices* LookupFunctionTable(Variable* v) {
|
||||
ZoneHashMap::Entry* entry =
|
||||
function_tables_.Lookup(v, ComputePointerHash(v));
|
||||
DCHECK_NOT_NULL(entry);
|
||||
if (entry == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<FunctionTableIndices*>(entry->value);
|
||||
}
|
||||
|
||||
void PopulateFunctionTable(VariableProxy* table, ArrayLiteral* funcs) {
|
||||
FunctionTableIndices* indices = LookupFunctionTable(table->var());
|
||||
DCHECK_NOT_NULL(indices);
|
||||
for (int i = 0; i < funcs->values()->length(); ++i) {
|
||||
VariableProxy* func = funcs->values()->at(i)->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(func);
|
||||
builder_->SetIndirectFunction(
|
||||
indices->start_index + i,
|
||||
LookupOrInsertFunction(func->var())->func_index());
|
||||
}
|
||||
}
|
||||
|
||||
class ImportedFunctionTable {
|
||||
private:
|
||||
class ImportedFunctionIndices : public ZoneObject {
|
||||
@ -727,20 +812,33 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
ZoneAllocationPolicy(builder->zone())),
|
||||
builder_(builder) {}
|
||||
|
||||
void AddImport(Variable* v, const char* name, int name_length) {
|
||||
ImportedFunctionIndices* indices = new (builder_->zone())
|
||||
ImportedFunctionIndices(name, name_length, builder_->zone());
|
||||
ImportedFunctionIndices* LookupOrInsertImport(Variable* v) {
|
||||
auto* entry = table_.LookupOrInsert(
|
||||
v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone()));
|
||||
entry->value = indices;
|
||||
ImportedFunctionIndices* indices;
|
||||
if (entry->value == nullptr) {
|
||||
indices = new (builder_->zone())
|
||||
ImportedFunctionIndices(nullptr, 0, builder_->zone());
|
||||
entry->value = indices;
|
||||
} else {
|
||||
indices = reinterpret_cast<ImportedFunctionIndices*>(entry->value);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
void SetImportName(Variable* v, const char* name, int name_length) {
|
||||
auto* indices = LookupOrInsertImport(v);
|
||||
indices->name_ = name;
|
||||
indices->name_length_ = name_length;
|
||||
for (auto i : indices->signature_to_index_) {
|
||||
builder_->builder_->SetImportName(i.second, indices->name_,
|
||||
indices->name_length_);
|
||||
}
|
||||
}
|
||||
|
||||
// Get a function's index (or allocate if new).
|
||||
uint32_t LookupOrInsertImport(Variable* v, FunctionSig* sig) {
|
||||
ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v));
|
||||
DCHECK_NOT_NULL(entry);
|
||||
ImportedFunctionIndices* indices =
|
||||
reinterpret_cast<ImportedFunctionIndices*>(entry->value);
|
||||
uint32_t LookupOrInsertImportUse(Variable* v, FunctionSig* sig) {
|
||||
auto* indices = LookupOrInsertImport(v);
|
||||
WasmModuleBuilder::SignatureMap::iterator pos =
|
||||
indices->signature_to_index_.find(sig);
|
||||
if (pos != indices->signature_to_index_.end()) {
|
||||
@ -901,7 +999,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (typer_->TypeOf(target)->AsFFIType() != nullptr) {
|
||||
const AstRawString* name =
|
||||
prop->key()->AsLiteral()->AsRawPropertyName();
|
||||
imported_function_table_.AddImport(
|
||||
imported_function_table_.SetImportName(
|
||||
target->var(), reinterpret_cast<const char*>(name->raw_data()),
|
||||
name->length());
|
||||
}
|
||||
@ -910,14 +1008,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
return;
|
||||
}
|
||||
ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
|
||||
if (funcs != nullptr &&
|
||||
typer_->TypeOf(funcs)
|
||||
->AsFunctionTableType()
|
||||
->signature()
|
||||
->AsFunctionType()) {
|
||||
if (funcs != nullptr) {
|
||||
VariableProxy* target = expr->target()->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(target);
|
||||
AddFunctionTable(target, funcs);
|
||||
PopulateFunctionTable(target, funcs);
|
||||
// Only add to the function table. No init needed.
|
||||
return;
|
||||
}
|
||||
@ -952,7 +1046,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
DCHECK_NOT_NULL(key_literal);
|
||||
if (!key_literal->value().is_null()) {
|
||||
Handle<Name> name =
|
||||
i::Object::ToName(isolate_, key_literal->value()).ToHandleChecked();
|
||||
Object::ToName(isolate_, key_literal->value()).ToHandleChecked();
|
||||
LocalType type = is_float ? kAstF64 : kAstI32;
|
||||
foreign_variables_.push_back({name, var, type});
|
||||
}
|
||||
@ -1325,7 +1419,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
for (int i = 0; i < args->length(); ++i) {
|
||||
sig.AddParam(TypeOf(args->at(i)));
|
||||
}
|
||||
uint32_t index = imported_function_table_.LookupOrInsertImport(
|
||||
uint32_t index = imported_function_table_.LookupOrInsertImportUse(
|
||||
vp->var(), sig.Build());
|
||||
VisitCallArgs(expr);
|
||||
current_function_builder_->AddAsmWasmOffset(expr->position());
|
||||
@ -1348,7 +1442,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
DCHECK_NOT_NULL(p);
|
||||
VariableProxy* var = p->obj()->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(var);
|
||||
FunctionTableIndices* indices = LookupFunctionTable(var->var());
|
||||
FunctionTableIndices* indices = LookupOrAddFunctionTable(var, p);
|
||||
Visit(p->key()); // TODO(titzer): should use RECURSE()
|
||||
|
||||
// We have to use a temporary for the correct order of evaluation.
|
||||
@ -1694,6 +1788,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitDeclarations(Declaration::List* decls) {
|
||||
for (Declaration* decl : *decls) {
|
||||
RECURSE(Visit(decl));
|
||||
if (typer_failed_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1821,7 +1918,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
FunctionLiteral* literal_;
|
||||
Isolate* isolate_;
|
||||
Zone* zone_;
|
||||
AstValueFactory* ast_value_factory_;
|
||||
Script* script_;
|
||||
AsmTyper* typer_;
|
||||
bool typer_failed_;
|
||||
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
|
||||
ZoneVector<ForeignVariable> foreign_variables_;
|
||||
WasmFunctionBuilder* init_function_;
|
||||
@ -1837,21 +1937,27 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
};
|
||||
|
||||
AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
|
||||
FunctionLiteral* literal, AsmTyper* typer)
|
||||
: isolate_(isolate), zone_(zone), literal_(literal), typer_(typer) {}
|
||||
AstValueFactory* ast_value_factory,
|
||||
Script* script, FunctionLiteral* literal)
|
||||
: isolate_(isolate),
|
||||
zone_(zone),
|
||||
ast_value_factory_(ast_value_factory),
|
||||
script_(script),
|
||||
literal_(literal),
|
||||
typer_(isolate, zone, script, literal) {}
|
||||
|
||||
// TODO(aseemgarg): probably should take zone (to write wasm to) as input so
|
||||
// that zone in constructor may be thrown away once wasm module is written.
|
||||
AsmWasmBuilder::Result AsmWasmBuilder::Run(
|
||||
i::Handle<i::FixedArray>* foreign_args) {
|
||||
AsmWasmBuilderImpl impl(isolate_, zone_, literal_, typer_);
|
||||
impl.Build();
|
||||
AsmWasmBuilder::Result AsmWasmBuilder::Run(Handle<FixedArray>* foreign_args) {
|
||||
AsmWasmBuilderImpl impl(isolate_, zone_, ast_value_factory_, script_,
|
||||
literal_, &typer_);
|
||||
bool success = impl.Build();
|
||||
*foreign_args = impl.GetForeignArgs();
|
||||
ZoneBuffer* module_buffer = new (zone_) ZoneBuffer(zone_);
|
||||
impl.builder_->WriteTo(*module_buffer);
|
||||
ZoneBuffer* asm_offsets_buffer = new (zone_) ZoneBuffer(zone_);
|
||||
impl.builder_->WriteAsmJsOffsetTable(*asm_offsets_buffer);
|
||||
return {module_buffer, asm_offsets_buffer};
|
||||
return {module_buffer, asm_offsets_buffer, success};
|
||||
}
|
||||
|
||||
const char* AsmWasmBuilder::foreign_init_name = "__foreign_init__";
|
||||
|
@ -15,6 +15,7 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class FunctionLiteral;
|
||||
class Script;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
@ -23,20 +24,26 @@ class AsmWasmBuilder {
|
||||
struct Result {
|
||||
ZoneBuffer* module_bytes;
|
||||
ZoneBuffer* asm_offset_table;
|
||||
bool success;
|
||||
};
|
||||
|
||||
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
|
||||
AsmTyper* typer);
|
||||
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone,
|
||||
AstValueFactory* ast_value_factory, Script* script,
|
||||
FunctionLiteral* root);
|
||||
Result Run(Handle<FixedArray>* foreign_args);
|
||||
|
||||
static const char* foreign_init_name;
|
||||
static const char* single_function_name;
|
||||
|
||||
const AsmTyper* typer() { return &typer_; }
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
Zone* zone_;
|
||||
AstValueFactory* ast_value_factory_;
|
||||
Script* script_;
|
||||
FunctionLiteral* literal_;
|
||||
AsmTyper* typer_;
|
||||
AsmTyper typer_;
|
||||
};
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
|
@ -542,13 +542,15 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
|
||||
scope->HoistSloppyBlockFunctions(&factory);
|
||||
}
|
||||
|
||||
// We are compiling one of three cases:
|
||||
// We are compiling one of four cases:
|
||||
// 1) top-level code,
|
||||
// 2) a function/eval/module on the top-level
|
||||
// 3) a function/eval in a scope that was already resolved.
|
||||
// 4) an asm.js function
|
||||
DCHECK(scope->scope_type() == SCRIPT_SCOPE ||
|
||||
scope->outer_scope()->scope_type() == SCRIPT_SCOPE ||
|
||||
scope->outer_scope()->already_resolved_);
|
||||
scope->outer_scope()->already_resolved_ ||
|
||||
(info->asm_function_scope() && scope->is_function_scope()));
|
||||
|
||||
// The outer scope is never lazy.
|
||||
scope->set_should_eager_compile();
|
||||
|
@ -420,6 +420,22 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
|
||||
void set_is_debug_evaluate_scope() { is_debug_evaluate_scope_ = true; }
|
||||
bool is_debug_evaluate_scope() const { return is_debug_evaluate_scope_; }
|
||||
|
||||
bool RemoveInnerScope(Scope* inner_scope) {
|
||||
DCHECK_NOT_NULL(inner_scope);
|
||||
if (inner_scope == inner_scope_) {
|
||||
inner_scope_ = inner_scope_->sibling_;
|
||||
return true;
|
||||
}
|
||||
for (Scope* scope = inner_scope_; scope != nullptr;
|
||||
scope = scope->sibling_) {
|
||||
if (scope->sibling_ == inner_scope) {
|
||||
scope->sibling_ = scope->sibling_->sibling_;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit Scope(Zone* zone);
|
||||
|
||||
@ -557,21 +573,6 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
|
||||
inner_scope->outer_scope_ = this;
|
||||
}
|
||||
|
||||
void RemoveInnerScope(Scope* inner_scope) {
|
||||
DCHECK_NOT_NULL(inner_scope);
|
||||
if (inner_scope == inner_scope_) {
|
||||
inner_scope_ = inner_scope_->sibling_;
|
||||
return;
|
||||
}
|
||||
for (Scope* scope = inner_scope_; scope != nullptr;
|
||||
scope = scope->sibling_) {
|
||||
if (scope->sibling_ == inner_scope) {
|
||||
scope->sibling_ = scope->sibling_->sibling_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetDefaults();
|
||||
|
||||
friend class DeclarationScope;
|
||||
|
@ -948,18 +948,6 @@ MaybeHandle<Code> GetLazyCode(Handle<JSFunction> function) {
|
||||
}
|
||||
|
||||
|
||||
Handle<SharedFunctionInfo> NewSharedFunctionInfoForLiteral(
|
||||
Isolate* isolate, FunctionLiteral* literal, Handle<Script> script) {
|
||||
Handle<Code> code = isolate->builtins()->CompileLazy();
|
||||
Handle<ScopeInfo> scope_info = handle(ScopeInfo::Empty(isolate));
|
||||
Handle<SharedFunctionInfo> result = isolate->factory()->NewSharedFunctionInfo(
|
||||
literal->name(), literal->materialized_literal_count(), literal->kind(),
|
||||
code, scope_info);
|
||||
SharedFunctionInfo::InitFromFunctionLiteral(result, literal);
|
||||
SharedFunctionInfo::SetScript(result, script);
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) {
|
||||
Isolate* isolate = info->isolate();
|
||||
TimerEventScope<TimerEventCompileCode> timer(isolate);
|
||||
@ -998,7 +986,7 @@ Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) {
|
||||
|
||||
// Allocate a shared function info object.
|
||||
DCHECK_EQ(kNoSourcePosition, lit->function_token_position());
|
||||
result = NewSharedFunctionInfoForLiteral(isolate, lit, script);
|
||||
result = isolate->factory()->NewSharedFunctionInfoForLiteral(lit, script);
|
||||
result->set_is_toplevel(true);
|
||||
parse_info->set_shared_info(result);
|
||||
parse_info->set_function_literal_id(result->function_literal_id());
|
||||
@ -1577,7 +1565,8 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
|
||||
// Allocate a shared function info object.
|
||||
Handle<SharedFunctionInfo> result;
|
||||
if (!maybe_existing.ToHandle(&result)) {
|
||||
result = NewSharedFunctionInfoForLiteral(isolate, literal, script);
|
||||
result =
|
||||
isolate->factory()->NewSharedFunctionInfoForLiteral(literal, script);
|
||||
result->set_is_toplevel(false);
|
||||
|
||||
// If the outer function has been compiled before, we cannot be sure that
|
||||
|
@ -2261,6 +2261,17 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
|
||||
return shared;
|
||||
}
|
||||
|
||||
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForLiteral(
|
||||
FunctionLiteral* literal, Handle<Script> script) {
|
||||
Handle<Code> code = isolate()->builtins()->CompileLazy();
|
||||
Handle<ScopeInfo> scope_info(ScopeInfo::Empty(isolate()));
|
||||
Handle<SharedFunctionInfo> result = NewSharedFunctionInfo(
|
||||
literal->name(), literal->materialized_literal_count(), literal->kind(),
|
||||
code, scope_info);
|
||||
SharedFunctionInfo::InitFromFunctionLiteral(result, literal);
|
||||
SharedFunctionInfo::SetScript(result, script);
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<JSMessageObject> Factory::NewJSMessageObject(
|
||||
MessageTemplate::Template message, Handle<Object> argument,
|
||||
|
@ -706,6 +706,9 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
MaybeHandle<Code> code,
|
||||
bool is_constructor);
|
||||
|
||||
Handle<SharedFunctionInfo> NewSharedFunctionInfoForLiteral(
|
||||
FunctionLiteral* literal, Handle<Script> script);
|
||||
|
||||
static bool IsFunctionModeWithPrototype(FunctionMode function_mode) {
|
||||
return (function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE ||
|
||||
function_mode == FUNCTION_WITH_READONLY_PROTOTYPE);
|
||||
|
@ -19,6 +19,7 @@ ParseInfo::ParseInfo(Zone* zone)
|
||||
extension_(nullptr),
|
||||
compile_options_(ScriptCompiler::kNoCompileOptions),
|
||||
script_scope_(nullptr),
|
||||
asm_function_scope_(nullptr),
|
||||
unicode_cache_(nullptr),
|
||||
stack_limit_(0),
|
||||
hash_seed_(0),
|
||||
|
@ -105,6 +105,11 @@ class V8_EXPORT_PRIVATE ParseInfo {
|
||||
script_scope_ = script_scope;
|
||||
}
|
||||
|
||||
DeclarationScope* asm_function_scope() const { return asm_function_scope_; }
|
||||
void set_asm_function_scope(DeclarationScope* scope) {
|
||||
asm_function_scope_ = scope;
|
||||
}
|
||||
|
||||
AstValueFactory* ast_value_factory() const { return ast_value_factory_; }
|
||||
void set_ast_value_factory(AstValueFactory* ast_value_factory) {
|
||||
ast_value_factory_ = ast_value_factory;
|
||||
@ -223,6 +228,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
|
||||
v8::Extension* extension_;
|
||||
ScriptCompiler::CompileOptions compile_options_;
|
||||
DeclarationScope* script_scope_;
|
||||
DeclarationScope* asm_function_scope_;
|
||||
UnicodeCache* unicode_cache_;
|
||||
uintptr_t stack_limit_;
|
||||
uint32_t hash_seed_;
|
||||
|
@ -859,6 +859,12 @@ FunctionLiteral* Parser::ParseFunction(Isolate* isolate, ParseInfo* info) {
|
||||
}
|
||||
Handle<SharedFunctionInfo> shared_info = info->shared_info();
|
||||
DeserializeScopeChain(info, info->maybe_outer_scope_info());
|
||||
if (info->asm_function_scope()) {
|
||||
original_scope_ = info->asm_function_scope();
|
||||
factory()->set_zone(info->zone());
|
||||
} else {
|
||||
DCHECK_EQ(factory()->zone(), info->zone());
|
||||
}
|
||||
|
||||
// Initialize parser state.
|
||||
source = String::Flatten(source);
|
||||
@ -2631,8 +2637,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
|
||||
// FunctionExpression; even without enclosing parentheses it might be
|
||||
// immediately invoked.
|
||||
// - The function literal shouldn't be hinted to eagerly compile.
|
||||
// - For asm.js functions the body needs to be available when module
|
||||
// validation is active, because we examine the entire module at once.
|
||||
|
||||
// Inner functions will be parsed using a temporary Zone. After parsing, we
|
||||
// will migrate unresolved variable into a Scope in the main Zone.
|
||||
@ -2642,8 +2646,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
|
||||
? can_preparse
|
||||
: (is_lazy_top_level_function ||
|
||||
(allow_lazy_ && function_type == FunctionLiteral::kDeclaration &&
|
||||
eager_compile_hint == FunctionLiteral::kShouldLazyCompile))) &&
|
||||
!(FLAG_validate_asm && scope()->IsAsmModule());
|
||||
eager_compile_hint == FunctionLiteral::kShouldLazyCompile)));
|
||||
bool is_lazy_inner_function =
|
||||
use_temp_zone && FLAG_lazy_inner_functions && !is_lazy_top_level_function;
|
||||
|
||||
|
@ -271,8 +271,15 @@ uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
|
||||
}
|
||||
}
|
||||
|
||||
void WasmModuleBuilder::AddIndirectFunction(uint32_t index) {
|
||||
indirect_functions_.push_back(index);
|
||||
uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
|
||||
uint32_t ret = static_cast<uint32_t>(indirect_functions_.size());
|
||||
indirect_functions_.resize(indirect_functions_.size() + count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
|
||||
uint32_t direct) {
|
||||
indirect_functions_[indirect] = direct;
|
||||
}
|
||||
|
||||
uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
|
||||
|
@ -230,7 +230,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
|
||||
const WasmInitExpr& init = WasmInitExpr());
|
||||
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
|
||||
uint32_t AddSignature(FunctionSig* sig);
|
||||
void AddIndirectFunction(uint32_t index);
|
||||
uint32_t AllocateIndirectFunctions(uint32_t count);
|
||||
void SetIndirectFunction(uint32_t indirect, uint32_t direct);
|
||||
void MarkStartFunction(WasmFunctionBuilder* builder);
|
||||
|
||||
// Writing methods.
|
||||
|
@ -126,7 +126,7 @@ class AsmTyperHarnessBuilder {
|
||||
WithGlobal(var_name, type);
|
||||
auto* var_info = typer_->Lookup(DeclareVariable(var_name));
|
||||
CHECK(var_info);
|
||||
var_info->FirstForwardUseIs(nullptr);
|
||||
var_info->SetFirstForwardUse(-1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1082,6 +1082,7 @@ function TestForeignFunctionMultipleUse() {
|
||||
assertEquals(89, module.caller(83, 83.25));
|
||||
}
|
||||
|
||||
print("TestForeignFunctionMultipleUse...");
|
||||
TestForeignFunctionMultipleUse();
|
||||
|
||||
|
||||
@ -1173,6 +1174,7 @@ function TestForeignVariables() {
|
||||
TestCase(undefined, 0, NaN, 0, NaN);
|
||||
}
|
||||
|
||||
print("TestForeignVariables...");
|
||||
TestForeignVariables();
|
||||
|
||||
|
||||
@ -1386,6 +1388,7 @@ assertWasm(7, TestIntegerMultiplyBothWays);
|
||||
}
|
||||
return {func: func};
|
||||
}
|
||||
print("TestBadAssignDoubleFromIntish...");
|
||||
Module(stdlib);
|
||||
assertTrue(%IsNotAsmWasmCode(Module));
|
||||
})();
|
||||
@ -1401,6 +1404,7 @@ assertWasm(7, TestIntegerMultiplyBothWays);
|
||||
}
|
||||
return {func: func};
|
||||
}
|
||||
print("TestBadAssignIntFromDouble...");
|
||||
Module(stdlib);
|
||||
assertTrue(%IsNotAsmWasmCode(Module));
|
||||
})();
|
||||
@ -1415,6 +1419,7 @@ assertWasm(7, TestIntegerMultiplyBothWays);
|
||||
}
|
||||
return {func: func};
|
||||
}
|
||||
print("TestBadMultiplyIntish...");
|
||||
Module(stdlib);
|
||||
assertTrue(%IsNotAsmWasmCode(Module));
|
||||
})();
|
||||
@ -1429,6 +1434,7 @@ assertWasm(7, TestIntegerMultiplyBothWays);
|
||||
}
|
||||
return {func: func};
|
||||
}
|
||||
print("TestBadCastFromInt...");
|
||||
Module(stdlib);
|
||||
assertTrue(%IsNotAsmWasmCode(Module));
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user