blob: 3a8b1972d67ed54680c6c423ec67be70311d84ff [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_WASM_MODULE_COMPILER_H_
#define V8_WASM_MODULE_COMPILER_H_
#include <functional>
#include "src/base/atomic-utils.h"
#include "src/cancelable-task.h"
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
namespace wasm {
class ModuleCompiler;
class WasmCode;
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
Isolate* isolate, ErrorThrower* thrower,
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
MaybeHandle<JSArrayBuffer> memory);
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory);
V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
const ModuleWireBytes& bytes,
bool is_shared);
V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
Handle<JSPromise> promise,
Handle<WasmModuleObject> module_object,
MaybeHandle<JSReceiver> imports);
V8_EXPORT_PRIVATE void CompileJsToWasmWrappers(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
Counters* counters);
V8_EXPORT_PRIVATE Handle<Script> CreateWasmScript(
Isolate* isolate, const ModuleWireBytes& wire_bytes);
// Triggered by the WasmCompileLazy builtin.
// Walks the stack (top three frames) to determine the wasm instance involved
// and which function to compile.
// Then triggers WasmCompiledModule::CompileLazy, taking care of correctly
// patching the call site or indirect function tables.
// Returns either the Code object that has been lazily compiled, or Illegal if
// an error occurred. In the latter case, a pending exception has been set,
// which will be triggered when returning from the runtime function, i.e. the
// Illegal builtin will never be called.
Address CompileLazy(Isolate* isolate);
Handle<Code> CompileLazyOnGCHeap(Isolate* isolate);
// This class orchestrates the lazy compilation of wasm functions. It is
// triggered by the WasmCompileLazy builtin.
// It contains the logic for compiling and specializing wasm functions, and
// patching the calling wasm code.
// Once we support concurrent lazy compilation, this class will contain the
// logic to actually orchestrate parallel execution of wasm compilation jobs.
// TODO(clemensh): Implement concurrent lazy compilation.
class LazyCompilationOrchestrator {
const WasmCode* CompileFunction(Isolate*, Handle<WasmInstanceObject>,
int func_index);
public:
Handle<Code> CompileLazyOnGCHeap(Isolate*, Handle<WasmInstanceObject>,
Handle<Code> caller, int call_offset,
int exported_func_index, bool patch_caller);
const wasm::WasmCode* CompileFromJsToWasm(Isolate*,
Handle<WasmInstanceObject>,
Handle<Code> caller,
uint32_t exported_func_index);
const wasm::WasmCode* CompileDirectCall(Isolate*, Handle<WasmInstanceObject>,
Maybe<uint32_t>,
const WasmCode* caller,
int call_offset);
const wasm::WasmCode* CompileIndirectCall(Isolate*,
Handle<WasmInstanceObject>,
uint32_t func_index);
#ifdef DEBUG
// Call this method in tests to disallow any further lazy compilation; then
// call into the wasm instance again to verify that no lazy compilation is
// triggered.
void FreezeLazyCompilationForTesting() { frozen_ = true; }
bool IsFrozenForTesting() const { return frozen_; }
private:
bool frozen_;
#else
void FreezeLazyCompilationForTesting() {}
bool IsFrozenForTesting() { return false; }
#endif
};
// Encapsulates all the state and steps of an asynchronous compilation.
// An asynchronous compile job consists of a number of tasks that are executed
// as foreground and background tasks. Any phase that touches the V8 heap or
// allocates on the V8 heap (e.g. creating the module object) must be a
// foreground task. All other tasks (e.g. decoding and validating, the majority
// of the work of compilation) can be background tasks.
// TODO(wasm): factor out common parts of this with the synchronous pipeline.
class AsyncCompileJob {
public:
explicit AsyncCompileJob(Isolate* isolate, std::unique_ptr<byte[]> bytes_copy,
size_t length, Handle<Context> context,
Handle<JSPromise> promise);
void Start();
std::shared_ptr<StreamingDecoder> CreateStreamingDecoder();
void Abort();
~AsyncCompileJob();
private:
class CompileTask;
class CompileStep;
// States of the AsyncCompileJob.
class DecodeModule;
class DecodeFail;
class PrepareAndStartCompile;
class ExecuteAndFinishCompilationUnits;
class WaitForBackgroundTasks;
class FinishCompilationUnits;
class FinishCompile;
class CompileWrappers;
class FinishModule;
class AbortCompilation;
const std::shared_ptr<Counters>& async_counters() const {
return async_counters_;
}
Counters* counters() const { return async_counters().get(); }
void AsyncCompileFailed(ErrorThrower& thrower);
void AsyncCompileSucceeded(Handle<Object> result);
void StartForegroundTask();
void StartBackgroundTask();
void RestartBackgroundTasks();
// Switches to the compilation step {Step} and starts a foreground task to
// execute it.
template <typename Step, typename... Args>
void DoSync(Args&&... args);
// Switches to the compilation step {Step} and starts a background task to
// execute it.
template <typename Step, typename... Args>
void DoAsync(Args&&... args);
// Switches to the compilation step {Step} but does not start a task to
// execute it.
template <typename Step, typename... Args>
void NextStep(Args&&... args);
Isolate* isolate() { return isolate_; }
friend class AsyncStreamingProcessor;
Isolate* isolate_;
const std::shared_ptr<Counters> async_counters_;
std::unique_ptr<byte[]> bytes_copy_;
ModuleWireBytes wire_bytes_;
Handle<Context> context_;
Handle<JSPromise> module_promise_;
std::unique_ptr<ModuleCompiler> compiler_;
std::unique_ptr<compiler::ModuleEnv> module_env_;
std::unique_ptr<WasmModule> module_;
std::vector<DeferredHandles*> deferred_handles_;
Handle<WasmModuleObject> module_object_;
Handle<WasmCompiledModule> compiled_module_;
Handle<FixedArray> code_table_;
size_t outstanding_units_ = 0;
std::unique_ptr<CompileStep> step_;
CancelableTaskManager background_task_manager_;
std::shared_ptr<v8::TaskRunner> foreground_task_runner_;
std::shared_ptr<v8::TaskRunner> background_task_runner_;
// The number of background tasks which stopped executing within a step.
base::AtomicNumber<size_t> stopped_tasks_{0};
// For async compilation the AsyncCompileJob is the only finisher. For
// streaming compilation also the AsyncStreamingProcessor has to finish before
// compilation can be finished.
base::AtomicNumber<int32_t> outstanding_finishers_{1};
// Decrements the number of outstanding finishers. The last caller of this
// function should finish the asynchronous compilation, see the comment on
// {outstanding_finishers_}.
V8_WARN_UNUSED_RESULT bool DecrementAndCheckFinisherCount() {
return outstanding_finishers_.Decrement(1) == 0;
}
// Counts the number of pending foreground tasks.
int32_t num_pending_foreground_tasks_ = 0;
// The AsyncCompileJob owns the StreamingDecoder because the StreamingDecoder
// contains data which is needed by the AsyncCompileJob for streaming
// compilation. The AsyncCompileJob does not actively use the
// StreamingDecoder.
std::shared_ptr<StreamingDecoder> stream_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_MODULE_COMPILER_H_