Commit 14e05c10 authored by bradnelson's avatar bradnelson Committed by Commit bot

[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}
parent d385ed06
......@@ -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;
......
This diff is collapsed.
......@@ -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);
};
......
This diff is collapsed.
......@@ -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));
})();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment