Commit 79656609 authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[asmjs] Implement AsmJsCompilationJob.

Moves AsmJs compilation into an AsmJs compilation job. This enables it
to be treated like other unoptimized compilations and avoids some
special-casing in compiler.cc.

BUG=v8:5203

Change-Id: I71ad27e3f72815b4c4074634fff0d168a9c89102
Reviewed-on: https://chromium-review.googlesource.com/581487Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46863}
parent 3cbceb21
......@@ -10,6 +10,7 @@
#include "src/ast/ast.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/compilation-info.h"
#include "src/compiler.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/handles.h"
......@@ -166,91 +167,131 @@ void ReportInstantiationFailure(Handle<Script> script, int position,
} // namespace
MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
wasm::ZoneBuffer* module = nullptr;
wasm::ZoneBuffer* asm_offsets = nullptr;
Handle<FixedArray> uses_array;
Handle<WasmModuleObject> compiled;
// The compilation of asm.js modules is split into two distinct steps:
// [1] The asm.js module source is parsed, validated, and translated to a
// valid WebAssembly module. The result are two vectors representing the
// encoded module as well as encoded source position information.
// [2] The module is handed to WebAssembly which decodes it into an internal
// representation and eventually compiles it to machine code.
double translate_time; // Time (milliseconds) taken to execute step [1].
double compile_time; // Time (milliseconds) taken to execute step [2].
// The compilation of asm.js modules is split into two distinct steps:
// [1] PrepareJobImpl: The asm.js module source is parsed, validated, and
// translated to a valid WebAssembly module. The result are two vectors
// representing the encoded module as well as encoded source position
// information and a StdlibSet.
// [2] FinalizeJobImp: The module is handed to WebAssembly which decodes it
// into an internal representation and eventually compiles it to machine
// code.
class AsmJsCompilationJob final : public CompilationJob {
public:
explicit AsmJsCompilationJob(CompilationInfo* info)
: CompilationJob(info->isolate(), info, "AsmJs"),
module_(nullptr),
asm_offsets_(nullptr),
translate_time_(0),
compile_time_(0) {}
protected:
Status PrepareJobImpl() final;
Status ExecuteJobImpl() final;
Status FinalizeJobImpl() final;
private:
wasm::ZoneBuffer* module_;
wasm::ZoneBuffer* asm_offsets_;
// TODO(mstarzinger) Replace with a bitvector.
wasm::AsmJsParser::StdlibSet stdlib_uses_;
double translate_time_; // Time (milliseconds) taken to execute step [1].
double compile_time_; // Time (milliseconds) taken to execute step [2].
DISALLOW_COPY_AND_ASSIGN(AsmJsCompilationJob);
};
CompilationJob::Status AsmJsCompilationJob::PrepareJobImpl() {
// Step 1: Translate asm.js module to WebAssembly module.
{
HistogramTimerScope translate_time_scope(
info->isolate()->counters()->asm_wasm_translation_time());
size_t compile_zone_start = info->zone()->allocation_size();
base::ElapsedTimer translate_timer;
translate_timer.Start();
Zone* compile_zone = info->zone();
Zone translate_zone(info->isolate()->allocator(), ZONE_NAME);
std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
handle(String::cast(info->script()->source())),
info->literal()->start_position(), info->literal()->end_position()));
uintptr_t stack_limit = info->isolate()->stack_guard()->real_climit();
wasm::AsmJsParser parser(&translate_zone, stack_limit, std::move(stream));
if (!parser.Run()) {
DCHECK(!info->isolate()->has_pending_exception());
ReportCompilationFailure(info->script(), parser.failure_location(),
parser.failure_message());
return MaybeHandle<FixedArray>();
}
module = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteTo(*module);
asm_offsets = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets);
uses_array = info->isolate()->factory()->NewFixedArray(
static_cast<int>(parser.stdlib_uses()->size()));
int count = 0;
for (auto i : *parser.stdlib_uses()) {
uses_array->set(count++, Smi::FromInt(i));
}
size_t compile_zone_size =
info->zone()->allocation_size() - compile_zone_start;
size_t translate_zone_size = translate_zone.allocation_size();
info->isolate()
->counters()
->asm_wasm_translation_peak_memory_bytes()
->AddSample(static_cast<int>(translate_zone_size));
translate_time = translate_timer.Elapsed().InMillisecondsF();
if (FLAG_trace_asm_parser) {
PrintF(
"[asm.js translation successful: time=%0.3fms, "
"translate_zone=%" PRIuS "KB, compile_zone+=%" PRIuS "KB]\n",
translate_time, translate_zone_size / KB, compile_zone_size / KB);
}
HistogramTimerScope translate_time_scope(
info()->isolate()->counters()->asm_wasm_translation_time());
size_t compile_zone_start = info()->zone()->allocation_size();
base::ElapsedTimer translate_timer;
translate_timer.Start();
Zone* compile_zone = info()->zone();
Zone translate_zone(info()->isolate()->allocator(), ZONE_NAME);
// TODO(mstarzinger): In order to move translation to the non-main thread
// ExecuteJob phase, the scanner stream needs to be off-heap.
std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
handle(String::cast(info()->script()->source())),
info()->literal()->start_position(), info()->literal()->end_position()));
wasm::AsmJsParser parser(&translate_zone, stack_limit(), std::move(stream));
if (!parser.Run()) {
DCHECK(!info()->isolate()->has_pending_exception());
ReportCompilationFailure(info()->script(), parser.failure_location(),
parser.failure_message());
return FAILED;
}
module_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteTo(*module_);
asm_offsets_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_);
stdlib_uses_.swap(*parser.stdlib_uses());
size_t compile_zone_size =
info()->zone()->allocation_size() - compile_zone_start;
size_t translate_zone_size = translate_zone.allocation_size();
info()
->isolate()
->counters()
->asm_wasm_translation_peak_memory_bytes()
->AddSample(static_cast<int>(translate_zone_size));
translate_time_ = translate_timer.Elapsed().InMillisecondsF();
if (FLAG_trace_asm_parser) {
PrintF(
"[asm.js translation successful: time=%0.3fms, "
"translate_zone=%" PRIuS "KB, compile_zone+=%" PRIuS "KB]\n",
translate_time_, translate_zone_size / KB, compile_zone_size / KB);
}
return SUCCEEDED;
}
CompilationJob::Status AsmJsCompilationJob::ExecuteJobImpl() {
// TODO(mstarzinger): Move translation to the Execute phase (see comment about
// scanner stream above.
return SUCCEEDED;
}
CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
// Step 2: Compile and decode the WebAssembly module.
{
base::ElapsedTimer compile_timer;
compile_timer.Start();
wasm::ErrorThrower thrower(info->isolate(), "AsmJs::Compile");
MaybeHandle<WasmModuleObject> maybe_compiled = SyncCompileTranslatedAsmJs(
info->isolate(), &thrower,
wasm::ModuleWireBytes(module->begin(), module->end()), info->script(),
Vector<const byte>(asm_offsets->begin(), asm_offsets->size()));
DCHECK(!maybe_compiled.is_null());
DCHECK(!thrower.error());
compile_time = compile_timer.Elapsed().InMillisecondsF();
compiled = maybe_compiled.ToHandleChecked();
base::ElapsedTimer compile_timer;
compile_timer.Start();
Handle<FixedArray> uses_array = info()->isolate()->factory()->NewFixedArray(
static_cast<int>(stdlib_uses_.size()));
int count = 0;
for (auto i : stdlib_uses_) {
uses_array->set(count++, Smi::FromInt(i));
}
wasm::ErrorThrower thrower(info()->isolate(), "AsmJs::Compile");
Handle<WasmModuleObject> compiled =
SyncCompileTranslatedAsmJs(
info()->isolate(), &thrower,
wasm::ModuleWireBytes(module_->begin(), module_->end()),
info()->script(),
Vector<const byte>(asm_offsets_->begin(), asm_offsets_->size()))
.ToHandleChecked();
DCHECK(!thrower.error());
compile_time_ = compile_timer.Elapsed().InMillisecondsF();
// The result is a compiled module and serialized standard library uses.
Handle<FixedArray> result =
info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
info()->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled);
result->set(kWasmDataUsesArray, *uses_array);
ReportCompilationSuccess(info->script(), info->literal()->position(),
translate_time, compile_time, module->size());
return result;
info()->SetAsmWasmData(result);
info()->SetCode(info()->isolate()->builtins()->InstantiateAsmJs());
ReportCompilationSuccess(info()->script(), info()->literal()->position(),
translate_time_, compile_time_, module_->size());
return SUCCEEDED;
}
CompilationJob* AsmJs::NewCompilationJob(CompilationInfo* info) {
return new AsmJsCompilationJob(info);
}
MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
......
......@@ -13,13 +13,14 @@ namespace v8 {
namespace internal {
class CompilationInfo;
class CompilationJob;
class JSArrayBuffer;
class SharedFunctionInfo;
// Interface to compile and instantiate for asm.js modules.
class AsmJs {
public:
static MaybeHandle<FixedArray> CompileAsmViaWasm(CompilationInfo* info);
static CompilationJob* NewCompilationJob(CompilationInfo* info);
static MaybeHandle<Object> InstantiateAsmWasm(Isolate* isolate,
Handle<SharedFunctionInfo>,
Handle<FixedArray> wasm_data,
......
......@@ -55,7 +55,7 @@ class AsmJsParser {
const char* failure_message() const { return failure_message_; }
int failure_location() const { return failure_location_; }
WasmModuleBuilder* module_builder() { return module_builder_; }
const StdlibSet* stdlib_uses() const { return &stdlib_uses_; }
StdlibSet* stdlib_uses() { return &stdlib_uses_; }
private:
// clang-format off
......
......@@ -96,6 +96,9 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
bool has_bytecode_array() const { return !bytecode_array_.is_null(); }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
bool has_asm_wasm_data() const { return !asm_wasm_data_.is_null(); }
Handle<FixedArray> asm_wasm_data() const { return asm_wasm_data_; }
bool is_calling() const {
return GetFlag(kDeferredCalling) || GetFlag(kNonDeferredCalling);
}
......@@ -202,6 +205,10 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
bytecode_array_ = bytecode_array;
}
void SetAsmWasmData(Handle<FixedArray> asm_wasm_data) {
asm_wasm_data_ = asm_wasm_data;
}
bool ShouldTrapOnDeopt() const {
return (FLAG_trap_on_deopt && IsOptimizing()) ||
(FLAG_trap_on_stub_deopt && IsStub());
......@@ -369,6 +376,9 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
// refactored to avoid us needing to carry the BytcodeArray around.
Handle<BytecodeArray> bytecode_array_;
// Holds the asm_wasm array generated by the asmjs compiler.
Handle<FixedArray> asm_wasm_data_;
// The zone from which the compilation pipeline working on this
// CompilationInfo allocates.
Zone* zone_;
......
......@@ -358,7 +358,10 @@ void InstallUnoptimizedCode(CompilationInfo* info) {
shared->ReplaceCode(*info->code());
if (info->has_bytecode_array()) {
DCHECK(!shared->HasBytecodeArray()); // Only compiled once.
DCHECK(!info->has_asm_wasm_data());
shared->set_bytecode_array(*info->bytecode_array());
} else if (info->has_asm_wasm_data()) {
shared->set_asm_wasm_data(*info->asm_wasm_data());
}
// Install coverage info on the shared function info.
......@@ -443,29 +446,20 @@ bool Renumber(ParseInfo* info,
info->collect_type_profile());
}
bool RunUnoptimizedCompilationJob(CompilationJob* job) {
if (job->PrepareJob() != CompilationJob::SUCCEEDED) return false;
if (job->ExecuteJob() != CompilationJob::SUCCEEDED) return false;
return FinalizeUnoptimizedCompilationJob(job) == CompilationJob::SUCCEEDED;
}
bool GenerateUnoptimizedCode(CompilationInfo* info) {
if (UseAsmWasm(info->scope(), info->shared_info(), info->is_debug())) {
MaybeHandle<FixedArray> wasm_data;
wasm_data = AsmJs::CompileAsmViaWasm(info);
if (!wasm_data.is_null()) {
SetSharedFunctionFlagsFromLiteral(info->literal(), info->shared_info());
DeclarationScope::AllocateScopeInfos(info->parse_info(), info->isolate(),
AnalyzeMode::kRegular);
info->shared_info()->set_asm_wasm_data(*wasm_data.ToHandleChecked());
info->SetCode(info->isolate()->builtins()->InstantiateAsmJs());
InstallUnoptimizedCode(info);
return true;
}
std::unique_ptr<CompilationJob> job(AsmJs::NewCompilationJob(info));
if (RunUnoptimizedCompilationJob(job.get())) return true;
// asm.js validation failed, fall through to standard unoptimized compile.
}
std::unique_ptr<CompilationJob> job(GetUnoptimizedCompilationJob(info));
if (job->PrepareJob() != CompilationJob::SUCCEEDED) return false;
if (job->ExecuteJob() != CompilationJob::SUCCEEDED) return false;
if (FinalizeUnoptimizedCompilationJob(job.get()) !=
CompilationJob::SUCCEEDED) {
return false;
}
return true;
return RunUnoptimizedCompilationJob(job.get());
}
bool CompileUnoptimizedInnerFunctions(
......
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