[wasm] Basic wasm tier-up

Wasm tier-up first compiles the whole module using Liftoff, and then
using Turbofan. The idea is to achieve fast start-up times by first
running Liftoff-compiled code. In the meantime we finish compilation
with Turbofan, and replace the Liftoff-compiled code as soon
as Turbofan finished compilation, thus achieving high performance.
Tier-up is enabled through the flag FLAG_wasm_tier_up.

Bug: v8:6600
Change-Id: I70552969c53d909a591666a1e7ce1ee1419b2f34
Reviewed-on: https://chromium-review.googlesource.com/1010422
Commit-Queue: Kim-Anh Tran <kimanh@google.com>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52759}
This commit is contained in:
Kim-Anh Tran 2018-04-24 15:10:51 +02:00 committed by Commit Bot
parent 3a56441a8c
commit e47072c97a
15 changed files with 533 additions and 155 deletions

View File

@ -344,6 +344,11 @@ class LiftoffCompiler {
}
void StartFunctionBody(Decoder* decoder, Control* block) {
for (uint32_t i = 0; i < __ num_locals(); ++i) {
if (!CheckSupportedType(decoder, kTypes_ilfd, __ local_type(i), "param"))
return;
}
// Input 0 is the call target, the instance is at 1.
constexpr int kInstanceParameterIndex = 1;
// Store the instance parameter to a special stack slot.
@ -383,10 +388,6 @@ class LiftoffCompiler {
// finish compilation without errors even if we hit unimplemented
// LiftoffAssembler methods.
if (DidAssemblerBailout(decoder)) return;
for (uint32_t i = 0; i < __ num_locals(); ++i) {
if (!CheckSupportedType(decoder, kTypes_ilfd, __ local_type(i), "param"))
return;
}
__ SpillInstance(instance_reg);
// Input 0 is the code target, 1 is the instance. First parameter at 2.

View File

@ -14,6 +14,19 @@ namespace v8 {
namespace internal {
namespace wasm {
namespace {
const char* GetCompilationModeAsString(
WasmCompilationUnit::CompilationMode mode) {
switch (mode) {
case WasmCompilationUnit::CompilationMode::kLiftoff:
return "liftoff";
case WasmCompilationUnit::CompilationMode::kTurbofan:
return "turbofan";
}
UNREACHABLE();
}
} // namespace
// static
WasmCompilationUnit::CompilationMode
WasmCompilationUnit::GetDefaultCompilationMode() {
@ -56,7 +69,8 @@ void WasmCompilationUnit::ExecuteCompilation() {
TimedHistogramScope wasm_compile_function_time_scope(timed_histogram);
if (FLAG_trace_wasm_compiler) {
PrintF("Compiling wasm function %d\n\n", func_index_);
PrintF("Compiling wasm function %d with %s\n\n", func_index_,
GetCompilationModeAsString(mode_));
}
switch (mode_) {

View File

@ -80,6 +80,7 @@ class WasmCompilationUnit final {
size_t memory_cost() const { return memory_cost_; }
wasm::NativeModule* native_module() const { return native_module_; }
CompilationMode mode() const { return mode_; }
private:
friend class LiftoffCompilationUnit;

View File

@ -58,10 +58,12 @@ namespace wasm {
enum class CompilationEvent : uint8_t {
kFinishedBaselineCompilation,
kFailedCompilation
kFinishedTopTierCompilation,
kFailedCompilation,
kDestroyed
};
enum class NotifyCompilationCallback : uint8_t { kNotify, kNoNotify };
enum class CompileMode : uint8_t { kRegular, kTiering };
// The CompilationState keeps track of the compilation state of the
// owning NativeModule, i.e. which functions are left to be compiled.
@ -69,7 +71,7 @@ enum class NotifyCompilationCallback : uint8_t { kNotify, kNoNotify };
// compilation of functions.
class CompilationState {
public:
explicit CompilationState(internal::Isolate* isolate);
CompilationState(internal::Isolate* isolate, ModuleEnv& env);
~CompilationState();
// Needs to be set before {AddCompilationUnits} is run, which triggers
@ -80,14 +82,17 @@ class CompilationState {
// Inserts new functions to compile and kicks off compilation.
void AddCompilationUnits(
std::vector<std::unique_ptr<WasmCompilationUnit>>& units);
std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units);
std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit();
std::unique_ptr<WasmCompilationUnit> GetNextExecutedUnit();
bool HasCompilationUnitToFinish();
void OnError(Handle<Object> error, NotifyCompilationCallback notify);
void OnFinishedUnit(NotifyCompilationCallback notify);
void ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit);
void OnError(Handle<Object> error);
void OnFinishedUnit();
void ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit,
WasmCompilationUnit::CompilationMode mode);
void CancelAndWait();
void OnBackgroundTaskStopped();
@ -108,11 +113,48 @@ class CompilationState {
return failed_;
}
bool baseline_compilation_finished() const {
return baseline_compilation_finished_;
}
CompileMode compile_mode() const { return compile_mode_; }
ModuleEnv* module_env() { return &module_env_; }
const ModuleWireBytes& wire_bytes() const { return wire_bytes_; }
void SetWireBytes(const ModuleWireBytes& wire_bytes) {
DCHECK_NULL(bytes_copy_);
DCHECK_EQ(0, wire_bytes_.length());
bytes_copy_ = std::unique_ptr<byte[]>(new byte[wire_bytes.length()]);
memcpy(bytes_copy_.get(), wire_bytes.start(), wire_bytes.length());
wire_bytes_ = ModuleWireBytes(bytes_copy_.get(),
bytes_copy_.get() + wire_bytes.length());
}
private:
void NotifyOnEvent(CompilationEvent event, Handle<Object> error);
std::vector<std::unique_ptr<WasmCompilationUnit>>& finish_units() {
return baseline_compilation_finished_ ? tiering_finish_units_
: baseline_finish_units_;
}
size_t GetNumCompilationUnitsScheduled() const {
return baseline_compilation_units_.size() +
tiering_compilation_units_.size();
}
Isolate* const isolate_;
ModuleEnv module_env_;
const size_t max_memory_;
const CompileMode compile_mode_;
bool baseline_compilation_finished_ = false;
// TODO(wasm): eventually we want to get rid of this
// additional copy (see AsyncCompileJob).
std::unique_ptr<byte[]> bytes_copy_;
ModuleWireBytes wire_bytes_;
// This mutex protects all information of this CompilationState which is being
// accessed concurrently.
@ -121,12 +163,16 @@ class CompilationState {
//////////////////////////////////////////////////////////////////////////////
// Protected by {mutex_}:
std::vector<std::unique_ptr<WasmCompilationUnit>> compilation_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_;
bool finisher_is_running_ = false;
bool failed_ = false;
size_t num_background_tasks_ = 0;
std::vector<std::unique_ptr<WasmCompilationUnit>> finish_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_finish_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_finish_units_;
size_t allocated_memory_ = 0;
// End of fields protected by {mutex_}.
@ -144,6 +190,7 @@ class CompilationState {
const size_t max_background_tasks_ = 0;
size_t outstanding_units_ = 0;
size_t num_tiering_units_ = 0;
};
namespace {
@ -889,7 +936,7 @@ double MonotonicallyIncreasingTimeInMs() {
base::Time::kMillisecondsPerSecond;
}
ModuleEnv CreateDefaultModuleEnv(Isolate* isolate, WasmModule* module) {
ModuleEnv CreateDefaultModuleEnv(WasmModule* module) {
// TODO(kschimpf): Add module-specific policy handling here (see v8:7143)?
UseTrapHandler use_trap_handler =
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler;
@ -899,9 +946,9 @@ ModuleEnv CreateDefaultModuleEnv(Isolate* isolate, WasmModule* module) {
Handle<WasmCompiledModule> NewCompiledModule(Isolate* isolate,
WasmModule* module,
Handle<FixedArray> export_wrappers,
ModuleEnv* env) {
Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New(
isolate, module, export_wrappers, env->use_trap_handler);
ModuleEnv& env) {
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, module, export_wrappers, env);
return compiled_module;
}
@ -917,39 +964,62 @@ size_t GetMaxUsableMemorySize(Isolate* isolate) {
class CompilationUnitBuilder {
public:
explicit CompilationUnitBuilder(NativeModule* native_module,
ModuleEnv* module_env,
Handle<Code> centry_stub)
: native_module_(native_module),
compilation_state_(native_module->compilation_state()),
module_env_(module_env),
centry_stub_(centry_stub) {}
void AddUnit(const WasmFunction* function, uint32_t buffer_offset,
Vector<const uint8_t> bytes, WasmName name) {
units_.emplace_back(new WasmCompilationUnit(
compilation_state_->isolate(), module_env_, native_module_,
wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(),
bytes.end()},
name, function->func_index, centry_stub_,
WasmCompilationUnit::GetDefaultCompilationMode(),
compilation_state_->isolate()->async_counters().get()));
switch (compilation_state_->compile_mode()) {
case CompileMode::kTiering:
tiering_units_.emplace_back(
CreateUnit(function, buffer_offset, bytes, name,
WasmCompilationUnit::CompilationMode::kTurbofan));
baseline_units_.emplace_back(
CreateUnit(function, buffer_offset, bytes, name,
WasmCompilationUnit::CompilationMode::kLiftoff));
return;
case CompileMode::kRegular:
baseline_units_.emplace_back(
CreateUnit(function, buffer_offset, bytes, name,
WasmCompilationUnit::GetDefaultCompilationMode()));
return;
}
UNREACHABLE();
}
bool Commit() {
if (units_.empty()) return false;
compilation_state_->AddCompilationUnits(units_);
units_.clear();
if (baseline_units_.empty() && tiering_units_.empty()) return false;
compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_);
Clear();
return true;
}
void Clear() { units_.clear(); }
void Clear() {
baseline_units_.clear();
tiering_units_.clear();
}
private:
std::unique_ptr<WasmCompilationUnit> CreateUnit(
const WasmFunction* function, uint32_t buffer_offset,
Vector<const uint8_t> bytes, WasmName name,
WasmCompilationUnit::CompilationMode mode) {
return base::make_unique<WasmCompilationUnit>(
compilation_state_->isolate(), compilation_state_->module_env(),
native_module_,
wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(),
bytes.end()},
name, function->func_index, centry_stub_, mode,
compilation_state_->isolate()->async_counters().get());
}
NativeModule* native_module_;
CompilationState* compilation_state_;
ModuleEnv* module_env_;
Handle<Code> centry_stub_;
std::vector<std::unique_ptr<WasmCompilationUnit>> units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_units_;
};
// Run by each compilation task and by the main thread (i.e. in both
@ -966,39 +1036,45 @@ bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state) {
compilation_state->GetNextCompilationUnit();
if (unit == nullptr) return false;
// TODO(kimanh): We need to find out in which mode the unit
// should be compiled in before compiling it, as it might fallback
// to Turbofan if it cannot be compiled using Liftoff. This can be removed
// later as soon as Liftoff can compile any function. Then, we can directly
// access {unit->mode()} within {ScheduleUnitForFinishing()}.
WasmCompilationUnit::CompilationMode mode = unit->mode();
unit->ExecuteCompilation();
compilation_state->ScheduleUnitForFinishing(std::move(unit));
compilation_state->ScheduleUnitForFinishing(std::move(unit), mode);
return true;
}
size_t GetNumFunctionsToCompile(const std::vector<WasmFunction>& functions,
ModuleEnv* module_env) {
size_t GetNumFunctionsToCompile(const WasmModule* wasm_module) {
// TODO(kimanh): Remove, FLAG_skip_compiling_wasm_funcs: previously used for
// debugging, and now not necessarily working anymore.
uint32_t start = module_env->module->num_imported_functions +
FLAG_skip_compiling_wasm_funcs;
uint32_t num_funcs = static_cast<uint32_t>(functions.size());
uint32_t start =
wasm_module->num_imported_functions + FLAG_skip_compiling_wasm_funcs;
uint32_t num_funcs = static_cast<uint32_t>(wasm_module->functions.size());
uint32_t funcs_to_compile = start > num_funcs ? 0 : num_funcs - start;
return funcs_to_compile;
}
void InitializeCompilationUnits(const std::vector<WasmFunction>& functions,
const ModuleWireBytes& wire_bytes,
ModuleEnv* module_env, Handle<Code> centry_stub,
const WasmModule* wasm_module,
Handle<Code> centry_stub,
NativeModule* native_module) {
uint32_t start = module_env->module->num_imported_functions +
FLAG_skip_compiling_wasm_funcs;
uint32_t start =
wasm_module->num_imported_functions + FLAG_skip_compiling_wasm_funcs;
uint32_t num_funcs = static_cast<uint32_t>(functions.size());
CompilationUnitBuilder builder(native_module, module_env, centry_stub);
CompilationUnitBuilder builder(native_module, centry_stub);
for (uint32_t i = start; i < num_funcs; ++i) {
const WasmFunction* func = &functions[i];
uint32_t buffer_offset = func->code.offset();
Vector<const uint8_t> bytes(wire_bytes.start() + func->code.offset(),
func->code.end_offset() - func->code.offset());
WasmName name = wire_bytes.GetName(func, module_env->module);
WasmName name = wire_bytes.GetName(func, wasm_module);
DCHECK_NOT_NULL(native_module);
builder.AddUnit(func, buffer_offset, bytes, name);
}
@ -1014,8 +1090,13 @@ void FinishCompilationUnits(CompilationState* compilation_state,
if (unit == nullptr) break;
wasm::WasmCode* result = unit->FinishCompilation(thrower);
if (thrower->error()) {
compilation_state->Abort();
break;
}
// Update the compilation state.
compilation_state->OnFinishedUnit(NotifyCompilationCallback::kNoNotify);
compilation_state->OnFinishedUnit();
DCHECK_IMPLIES(result == nullptr, thrower->error());
if (result == nullptr) break;
}
@ -1024,6 +1105,52 @@ void FinishCompilationUnits(CompilationState* compilation_state,
}
}
void PatchNativeModule(NativeModule* cloning_module,
const NativeModule* source_module) {
// Clone optimized code into {cloning_module}.
if (source_module != cloning_module) {
cloning_module->CloneHigherTierCodeFrom(source_module);
}
// Link.
CodeSpecialization code_specialization;
code_specialization.RelocateDirectCalls(cloning_module);
code_specialization.ApplyToWholeModule(cloning_module);
}
void UpdateAllCompiledModulesWithTopTierCode(
Handle<WasmCompiledModule> compiled_module) {
// We want to disallow heap allocation here, as this might interfere with
// iterating over the chain of compiled modules for updating all native
// modules.
DisallowHeapAllocation no_gc;
WasmModule* module = compiled_module->shared()->module();
DCHECK_GT(module->functions.size() - module->num_imported_functions, 0);
USE(module);
CodeSpaceMemoryModificationScope modification_scope(
compiled_module->GetIsolate()->heap());
NativeModule* updated_module = compiled_module->GetNativeModule();
Handle<WasmCompiledModule> current = compiled_module;
PatchNativeModule(current->GetNativeModule(), updated_module);
// Go through the chain of compiled modules to update each (next in chain).
while (current->has_next_instance()) {
current = handle(current->next_instance());
PatchNativeModule(current->GetNativeModule(), updated_module);
}
// Go through the chain of compiled modules to update each (previous in
// chain).
current = compiled_module;
while (current->has_prev_instance()) {
current = handle(current->prev_instance());
PatchNativeModule(current->GetNativeModule(), updated_module);
}
}
void CompileInParallel(Isolate* isolate, NativeModule* native_module,
const ModuleWireBytes& wire_bytes, ModuleEnv* module_env,
Handle<Code> centry_stub, ErrorThrower* thrower) {
@ -1040,12 +1167,15 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module,
// 2.a) The background threads and the main thread pick one compilation
// unit at a time and execute the parallel phase of the compilation
// unit. After finishing the execution of the parallel phase, the
// result is enqueued in {finish_units_}.
// 2.b) If {finish_units_} contains a compilation unit, the main thread
// dequeues it and finishes the compilation.
// result is enqueued in {baseline_finish_units_}.
// 2.b) If {baseline_finish_units_} contains a compilation unit, the main
// thread dequeues it and finishes the compilation.
// 3) After the parallel phase of all compilation units has started, the
// main thread waits for all {BackgroundCompileTasks} instances to finish.
// 4) The main thread finishes the compilation.
// main thread continues to finish all compilation units as long as
// baseline-compilation units are left to be processed.
// 4) If tier-up is enabled, the main thread restarts background tasks
// that take care of compiling and finishing the top-tier compilation
// units.
// Turn on the {CanonicalHandleScope} so that the background threads can
// use the node cache.
@ -1056,45 +1186,102 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module,
// the compilation units. This foreground thread will be
// responsible for finishing compilation.
compilation_state->SetFinisherIsRunning(true);
size_t functions_count =
GetNumFunctionsToCompile(module->functions, module_env);
size_t functions_count = GetNumFunctionsToCompile(module);
compilation_state->SetNumberOfFunctionsToCompile(functions_count);
compilation_state->SetWireBytes(wire_bytes);
DeferredHandles* deferred_handles = nullptr;
Handle<Code> centry_deferred = centry_stub;
Handle<WasmCompiledModule> compiled_module_deferred;
if (compilation_state->compile_mode() == CompileMode::kTiering) {
// Open a deferred handle scope for the centry_stub, in order to allow
// for background tiering compilation.
DeferredHandleScope deferred(isolate);
centry_deferred = Handle<Code>(*centry_stub, isolate);
compiled_module_deferred =
handle(native_module->compiled_module(), compilation_state->isolate());
deferred_handles = deferred.Detach();
}
compilation_state->AddCallback(
[compiled_module_deferred, deferred_handles](
// Callback is called from a foreground thread.
CompilationEvent event, Handle<Object> error) mutable {
switch (event) {
case CompilationEvent::kFinishedBaselineCompilation:
// Nothing to do, since we are finishing baseline compilation
// in this foreground thread.
return;
case CompilationEvent::kFinishedTopTierCompilation:
UpdateAllCompiledModulesWithTopTierCode(compiled_module_deferred);
// TODO(wasm): Currently compilation has to finish before the
// {deferred_handles} can be removed. We need to make sure that
// we can clean it up at a time when the native module
// should die (but currently cannot, since it's kept alive
// through the {deferred_handles} themselves).
delete deferred_handles;
deferred_handles = nullptr;
return;
case CompilationEvent::kFailedCompilation:
// If baseline compilation failed, we will reflect this without
// a callback, in this thread through {thrower}.
// Tier-up compilation should not fail if baseline compilation
// did not fail.
DCHECK(!compiled_module_deferred->GetNativeModule()
->compilation_state()
->baseline_compilation_finished());
delete deferred_handles;
deferred_handles = nullptr;
return;
case CompilationEvent::kDestroyed:
if (deferred_handles) delete deferred_handles;
return;
}
UNREACHABLE();
});
// 1) The main thread allocates a compilation unit for each wasm function
// and stores them in the vector {compilation_units} within the
// {compilation_state}. By adding units to the {compilation_state}, new
// {BackgroundCompileTask} instances are spawned which run on
// background threads.
InitializeCompilationUnits(module->functions, wire_bytes, module_env,
centry_stub, native_module);
InitializeCompilationUnits(module->functions, compilation_state->wire_bytes(),
module, centry_deferred, native_module);
// 2.a) The background threads and the main thread pick one compilation
// unit at a time and execute the parallel phase of the compilation
// unit. After finishing the execution of the parallel phase, the
// result is enqueued in {finish_units_}.
// result is enqueued in {baseline_finish_units_}.
// The foreground task bypasses waiting on memory threshold, because
// its results will immediately be converted to code (below).
while (FetchAndExecuteCompilationUnit(compilation_state)) {
// 2.b) If {finish_units_} contains a compilation unit, the main thread
// dequeues it and finishes the compilation unit. Compilation units
// are finished concurrently to the background threads to save
while (FetchAndExecuteCompilationUnit(compilation_state) &&
!compilation_state->baseline_compilation_finished()) {
// 2.b) If {baseline_finish_units_} contains a compilation unit, the main
// thread dequeues it and finishes the compilation unit. Compilation
// units are finished concurrently to the background threads to save
// memory.
FinishCompilationUnits(compilation_state, thrower);
if (compilation_state->failed()) break;
}
// 3) After the parallel phase of all compilation units has started, the
// main thread waits for all {BackgroundCompileTasks} instances to finish -
// which happens once they all realize there's no next work item to
// process. If compilation already failed, all background tasks have
// already been canceled in {FinishCompilationUnits}, and there are
// no units to finish.
if (!compilation_state->failed()) {
compilation_state->CancelAndWait();
// 4) Finish all compilation units which have been executed while we waited.
while (!compilation_state->failed()) {
// 3) After the parallel phase of all compilation units has started, the
// main thread continues to finish compilation units as long as
// baseline compilation units are left to be processed. If compilation
// already failed, all background tasks have already been canceled
// in {FinishCompilationUnits}, and there are no units to finish.
FinishCompilationUnits(compilation_state, thrower);
if (compilation_state->baseline_compilation_finished()) break;
}
// 4) If tiering-compilation is enabled, we need to set the finisher
// to false, such that the background threads will spawn a foreground
// thread to finish the top-tier compilation units.
if (!compilation_state->failed() &&
compilation_state->compile_mode() == CompileMode::kTiering) {
compilation_state->SetFinisherIsRunning(false);
compilation_state->RestartBackgroundTasks();
}
}
@ -1209,14 +1396,14 @@ MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal(
for (int i = 0, e = export_wrappers->length(); i < e; ++i) {
export_wrappers->set(i, *init_builtin);
}
ModuleEnv env = CreateDefaultModuleEnv(isolate, wasm_module);
ModuleEnv env = CreateDefaultModuleEnv(wasm_module);
// Create the compiled module object and populate with compiled functions
// and information needed at instantiation time. This object needs to be
// serializable. Instantiation may occur off a deserialized version of this
// object.
Handle<WasmCompiledModule> compiled_module =
NewCompiledModule(isolate, shared->module(), export_wrappers, &env);
NewCompiledModule(isolate, shared->module(), export_wrappers, env);
NativeModule* native_module = compiled_module->GetNativeModule();
compiled_module->set_shared(*shared);
if (lazy_compile) {
@ -1314,21 +1501,33 @@ class FinishCompileTask : public CancelableTask {
ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile");
wasm::WasmCode* result = unit->FinishCompilation(&thrower);
NativeModule* native_module = unit->native_module();
if (thrower.error()) {
DCHECK_NULL(result);
USE(result);
SaveContext saved_context(isolate);
isolate->set_context(
unit->native_module()->compiled_module()->native_context());
native_module->compiled_module()->native_context());
Handle<Object> error = thrower.Reify();
compilation_state_->OnError(error, NotifyCompilationCallback::kNotify);
compilation_state_->OnError(error);
compilation_state_->SetFinisherIsRunning(false);
break;
}
if (compilation_state_->baseline_compilation_finished()) {
// If Liftoff compilation finishes it will directly start executing.
// As soon as we have Turbofan-compiled code available, it will
// directly be used by Liftoff-compiled code. Therefore we need
// to patch the compiled Turbofan function directly after finishing it.
DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode());
CodeSpecialization code_specialization;
code_specialization.RelocateDirectCalls(native_module);
code_specialization.ApplyToWasmCode(result);
}
// Update the compilation state, and possibly notify
// threads waiting for events.
compilation_state_->OnFinishedUnit(NotifyCompilationCallback::kNotify);
compilation_state_->OnFinishedUnit();
if (deadline < MonotonicallyIncreasingTimeInMs()) {
// We reached the deadline. We reschedule this task and return
@ -2498,7 +2697,7 @@ class AsyncStreamingProcessor final : public StreamingProcessor {
void OnAbort() override;
private:
// Finishes the AsyncCOmpileJob with an error.
// Finishes the AsyncCompileJob with an error.
void FinishAsyncCompileJobWithError(ResultBase result);
void CommitCompilationUnits();
@ -2532,9 +2731,6 @@ void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) {
}
void AsyncCompileJob::AsyncCompileSucceeded(Handle<Object> result) {
// {job} keeps the {this} pointer alive.
std::shared_ptr<AsyncCompileJob> job =
isolate_->wasm_engine()->compilation_manager()->RemoveJob(this);
MaybeHandle<Object> promise_result =
JSPromise::Resolve(module_promise_, result);
CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
@ -2695,10 +2891,6 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
Isolate* isolate = job_->isolate_;
DCHECK_NULL(job_->module_env_);
job_->module_env_.reset(
new ModuleEnv(CreateDefaultModuleEnv(isolate, module_)));
Handle<Code> centry_stub = CEntryStub(isolate, 1).GetCode();
{
// Now reopen the handles in a deferred scope in order to use
@ -2717,8 +2909,9 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
Handle<FixedArray> export_wrappers =
job_->isolate_->factory()->NewFixedArray(export_wrapper_size, TENURED);
job_->compiled_module_ = NewCompiledModule(
job_->isolate_, module_, export_wrappers, job_->module_env_.get());
ModuleEnv env = CreateDefaultModuleEnv(module_);
job_->compiled_module_ =
NewCompiledModule(job_->isolate_, module_, export_wrappers, env);
{
DeferredHandleScope deferred(job_->isolate_);
@ -2729,6 +2922,9 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
module_->functions.size() - module_->num_imported_functions;
if (num_functions == 0) {
// Tiering has nothing to do if module is empty.
job_->tiering_completed_ = true;
// Degenerate case of an empty module.
job_->DoSync<FinishCompile>();
return;
@ -2744,18 +2940,42 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
AsyncCompileJob* job = job_;
compilation_state->AddCallback(
[job](CompilationEvent event, Handle<Object> error) {
// Callback is called from a foreground thread.
switch (event) {
case CompilationEvent::kFinishedBaselineCompilation:
if (job->DecrementAndCheckFinisherCount()) {
job->DoSync<FinishCompile>();
}
return;
case CompilationEvent::kFailedCompilation:
case CompilationEvent::kFinishedTopTierCompilation:
// It is only safe to schedule the UpdateToTopTierCompiledCode
// step if no foreground task is currently pending, and no
// finisher is outstanding (streaming compilation).
if (job->num_pending_foreground_tasks_ == 0 &&
job->outstanding_finishers_.Value() == 0) {
job->DoSync<UpdateToTopTierCompiledCode>();
}
// If a foreground task was pending or a finsher was pending,
// we will rely on FinishModule to switch the step to
// UpdateToTopTierCompiledCode.
job->tiering_completed_ = true;
return;
case CompilationEvent::kFailedCompilation: {
// Tier-up compilation should not fail if baseline compilation
// did not fail.
DCHECK(!job->compiled_module_->GetNativeModule()
->compilation_state()
->baseline_compilation_finished());
DeferredHandleScope deferred(job->isolate());
error = handle(*error, job->isolate());
job->deferred_handles_.push_back(deferred.Detach());
job->DoSync<CompileFailed>(error);
return;
}
case CompilationEvent::kDestroyed:
// Nothing to do.
return;
}
UNREACHABLE();
});
@ -2766,12 +2986,11 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
// InitializeCompilationUnits always returns 0 for streaming compilation,
// then DoAsync would do the same as NextStep already.
size_t functions_count =
GetNumFunctionsToCompile(module_->functions, job_->module_env_.get());
size_t functions_count = GetNumFunctionsToCompile(env.module);
compilation_state->SetNumberOfFunctionsToCompile(functions_count);
// Add compilation units and kick off compilation.
InitializeCompilationUnits(module_->functions, job_->wire_bytes_,
job_->module_env_.get(), job_->centry_stub_,
env.module, job_->centry_stub_,
job_->compiled_module_->GetNativeModule());
}
}
@ -2870,8 +3089,40 @@ class AsyncCompileJob::FinishModule : public CompileStep {
TRACE_COMPILE("(7) Finish module...\n");
Handle<WasmModuleObject> result =
WasmModuleObject::New(job_->isolate_, job_->compiled_module_);
// {job_} is deleted in AsyncCompileSucceeded, therefore the {return}.
return job_->AsyncCompileSucceeded(result);
job_->AsyncCompileSucceeded(result);
WasmModule* module = job_->compiled_module_->shared()->module();
size_t num_functions =
module->functions.size() - module->num_imported_functions;
if (job_->compiled_module_->GetNativeModule()
->compilation_state()
->compile_mode() == CompileMode::kRegular ||
num_functions == 0) {
// If we do not tier up, the async compile job is done here and
// can be deleted.
job_->isolate_->wasm_engine()->compilation_manager()->RemoveJob(job_);
return;
}
// If background tiering compilation finished before we resolved the
// promise, switch to patching now. Otherwise, patching will be scheduled
// by a callback.
DCHECK_EQ(CompileMode::kTiering, job_->compiled_module_->GetNativeModule()
->compilation_state()
->compile_mode());
if (job_->tiering_completed_) {
job_->DoSync<UpdateToTopTierCompiledCode>();
}
}
};
//==========================================================================
// Step 8 (sync): Update with top tier code.
//==========================================================================
class AsyncCompileJob::UpdateToTopTierCompiledCode : public CompileStep {
void RunInForeground() override {
TRACE_COMPILE("(8) Update native module to use optimized code...\n");
UpdateAllCompiledModulesWithTopTierCode(job_->compiled_module_);
job_->isolate_->wasm_engine()->compilation_manager()->RemoveJob(job_);
}
};
@ -2987,8 +3238,8 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader(size_t functions_count,
// Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
// AsyncStreamingProcessor have to finish.
job_->outstanding_finishers_.SetValue(2);
compilation_unit_builder_.reset(new CompilationUnitBuilder(
native_module, job_->module_env_.get(), job_->centry_stub_));
compilation_unit_builder_.reset(
new CompilationUnitBuilder(native_module, job_->centry_stub_));
return true;
}
@ -3063,14 +3314,22 @@ void CompilationStateDeleter::operator()(
}
std::unique_ptr<CompilationState, CompilationStateDeleter> NewCompilationState(
Isolate* isolate) {
Isolate* isolate, ModuleEnv& env) {
return std::unique_ptr<CompilationState, CompilationStateDeleter>(
new CompilationState(isolate));
new CompilationState(isolate, env));
}
CompilationState::CompilationState(internal::Isolate* isolate)
ModuleEnv* GetModuleEnv(CompilationState* compilation_state) {
return compilation_state->module_env();
}
CompilationState::CompilationState(internal::Isolate* isolate, ModuleEnv& env)
: isolate_(isolate),
module_env_(env),
max_memory_(GetMaxUsableMemorySize(isolate) / 2),
compile_mode_(FLAG_wasm_tier_up ? CompileMode::kTiering
: CompileMode::kRegular),
wire_bytes_(ModuleWireBytes(nullptr, nullptr)),
max_background_tasks_(std::max(
1, std::min(FLAG_wasm_num_compilation_tasks,
V8::GetCurrentPlatform()->NumberOfWorkerThreads()))) {
@ -3082,16 +3341,24 @@ CompilationState::CompilationState(internal::Isolate* isolate)
// Register task manager for clean shutdown in case of an isolate shutdown.
isolate_->wasm_engine()->Register(&background_task_manager_);
isolate_->wasm_engine()->Register(&foreground_task_manager_);
}
CompilationState::~CompilationState() {
CancelAndWait();
foreground_task_manager_.CancelAndWait();
isolate_->wasm_engine()->Unregister(&foreground_task_manager_);
NotifyOnEvent(CompilationEvent::kDestroyed, Handle<Object>::null());
}
void CompilationState::SetNumberOfFunctionsToCompile(size_t num_functions) {
DCHECK(!failed());
outstanding_units_ = num_functions;
if (compile_mode_ == CompileMode::kTiering) {
outstanding_units_ += num_functions;
num_tiering_units_ = num_functions;
}
}
void CompilationState::AddCallback(
@ -3100,23 +3367,43 @@ void CompilationState::AddCallback(
}
void CompilationState::AddCompilationUnits(
std::vector<std::unique_ptr<WasmCompilationUnit>>& units) {
std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units) {
{
base::LockGuard<base::Mutex> guard(&mutex_);
compilation_units_.insert(compilation_units_.end(),
std::make_move_iterator(units.begin()),
std::make_move_iterator(units.end()));
if (compile_mode_ == CompileMode::kTiering) {
DCHECK_EQ(baseline_units.size(), tiering_units.size());
DCHECK_EQ(tiering_units.back()->mode(),
WasmCompilationUnit::CompilationMode::kTurbofan);
tiering_compilation_units_.insert(
tiering_compilation_units_.end(),
std::make_move_iterator(tiering_units.begin()),
std::make_move_iterator(tiering_units.end()));
} else {
DCHECK(tiering_compilation_units_.empty());
}
baseline_compilation_units_.insert(
baseline_compilation_units_.end(),
std::make_move_iterator(baseline_units.begin()),
std::make_move_iterator(baseline_units.end()));
}
RestartBackgroundTasks(units.size());
RestartBackgroundTasks(GetNumCompilationUnitsScheduled());
}
std::unique_ptr<WasmCompilationUnit>
CompilationState::GetNextCompilationUnit() {
base::LockGuard<base::Mutex> guard(&mutex_);
if (!compilation_units_.empty()) {
std::unique_ptr<WasmCompilationUnit> unit =
std::move(compilation_units_.back());
compilation_units_.pop_back();
std::vector<std::unique_ptr<WasmCompilationUnit>>& units =
baseline_compilation_units_.empty() ? tiering_compilation_units_
: baseline_compilation_units_;
if (!units.empty()) {
std::unique_ptr<WasmCompilationUnit> unit = std::move(units.back());
units.pop_back();
return unit;
}
@ -3125,44 +3412,67 @@ CompilationState::GetNextCompilationUnit() {
std::unique_ptr<WasmCompilationUnit> CompilationState::GetNextExecutedUnit() {
base::LockGuard<base::Mutex> guard(&mutex_);
if (finish_units_.empty()) return {};
std::unique_ptr<WasmCompilationUnit> ret = std::move(finish_units_.back());
finish_units_.pop_back();
std::vector<std::unique_ptr<WasmCompilationUnit>>& units = finish_units();
if (units.empty()) return {};
std::unique_ptr<WasmCompilationUnit> ret = std::move(units.back());
units.pop_back();
allocated_memory_ -= ret->memory_cost();
return ret;
}
bool CompilationState::HasCompilationUnitToFinish() {
base::LockGuard<base::Mutex> guard(&mutex_);
return !finish_units_.empty();
return !finish_units().empty();
}
void CompilationState::OnError(Handle<Object> error,
NotifyCompilationCallback notify) {
void CompilationState::OnError(Handle<Object> error) {
Abort();
if (notify == NotifyCompilationCallback::kNotify) {
NotifyOnEvent(CompilationEvent::kFailedCompilation, error);
}
NotifyOnEvent(CompilationEvent::kFailedCompilation, error);
}
void CompilationState::OnFinishedUnit(NotifyCompilationCallback notify) {
void CompilationState::OnFinishedUnit() {
DCHECK_GT(outstanding_units_, 0);
--outstanding_units_;
if (outstanding_units_ == 0) {
CancelAndWait();
if (notify == NotifyCompilationCallback::kNotify) {
NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation,
Handle<Object>::null());
}
baseline_compilation_finished_ = true;
DCHECK(compile_mode_ == CompileMode::kRegular ||
compile_mode_ == CompileMode::kTiering);
NotifyOnEvent(compile_mode_ == CompileMode::kRegular
? CompilationEvent::kFinishedBaselineCompilation
: CompilationEvent::kFinishedTopTierCompilation,
Handle<Object>::null());
} else if (outstanding_units_ == num_tiering_units_) {
DCHECK_EQ(compile_mode_, CompileMode::kTiering);
baseline_compilation_finished_ = true;
// TODO(wasm): For streaming compilation, we want to start top tier
// compilation before all functions have been compiled with Liftoff, e.g.
// in the case when all received functions have been compiled with Liftoff
// and we are waiting for new functions to compile.
// If we are in {kRegular} mode, {num_tiering_units_} is 0, therefore
// this case is already caught by the previous check.
NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation,
Handle<Object>::null());
RestartBackgroundTasks(GetNumCompilationUnitsScheduled());
}
}
void CompilationState::ScheduleUnitForFinishing(
std::unique_ptr<WasmCompilationUnit> unit) {
std::unique_ptr<WasmCompilationUnit> unit,
WasmCompilationUnit::CompilationMode mode) {
size_t cost = unit->memory_cost();
base::LockGuard<base::Mutex> guard(&mutex_);
finish_units_.push_back(std::move(unit));
if (compile_mode_ == CompileMode::kTiering &&
mode == WasmCompilationUnit::CompilationMode::kTurbofan) {
tiering_finish_units_.push_back(std::move(unit));
} else {
baseline_finish_units_.push_back(std::move(unit));
}
allocated_memory_ += cost;
if (!finisher_is_running_ && !failed_) {
@ -3192,7 +3502,7 @@ void CompilationState::RestartBackgroundTasks(size_t max) {
DCHECK_LE(num_background_tasks_, max_background_tasks_);
if (num_background_tasks_ == max_background_tasks_) return;
num_restart = std::min(
num_restart, std::min(compilation_units_.size(),
num_restart, std::min(GetNumCompilationUnitsScheduled(),
max_background_tasks_ - num_background_tasks_));
num_background_tasks_ += num_restart;
}

View File

@ -22,7 +22,6 @@ namespace wasm {
class CompilationState;
class ModuleCompiler;
struct ModuleEnv;
class WasmCode;
struct CompilationStateDeleter {
@ -32,7 +31,9 @@ struct CompilationStateDeleter {
// Wrapper to create a CompilationState exists in order to avoid having
// the the CompilationState in the header file.
std::unique_ptr<CompilationState, CompilationStateDeleter> NewCompilationState(
Isolate* isolate);
Isolate* isolate, ModuleEnv& env);
ModuleEnv* GetModuleEnv(CompilationState* compilation_state);
MaybeHandle<WasmModuleObject> CompileToModuleObject(
Isolate* isolate, ErrorThrower* thrower, std::unique_ptr<WasmModule> module,
@ -99,6 +100,7 @@ class AsyncCompileJob {
class CompileWrappers;
class FinishModule;
class AbortCompilation;
class UpdateToTopTierCompiledCode;
const std::shared_ptr<Counters>& async_counters() const {
return async_counters_;
@ -138,7 +140,6 @@ class AsyncCompileJob {
ModuleWireBytes wire_bytes_;
Handle<Context> context_;
Handle<JSPromise> module_promise_;
std::unique_ptr<ModuleEnv> module_env_;
std::unique_ptr<WasmModule> module_;
std::vector<DeferredHandles*> deferred_handles_;
@ -172,6 +173,8 @@ class AsyncCompileJob {
// compilation. The AsyncCompileJob does not actively use the
// StreamingDecoder.
std::shared_ptr<StreamingDecoder> stream_;
bool tiering_completed_ = false;
};
} // namespace wasm
} // namespace internal

View File

@ -330,10 +330,10 @@ WasmCode::~WasmCode() {
// {source_native_module} into a {cloning_native_module}.
class NativeModule::CloneCodeHelper {
public:
explicit CloneCodeHelper(NativeModule* source_native_module,
explicit CloneCodeHelper(const NativeModule* source_native_module,
NativeModule* cloning_native_module);
void SelectForCloning(int32_t code_index);
void SelectForCloning(uint32_t code_index);
void CloneAndPatchCode();
@ -342,14 +342,34 @@ class NativeModule::CloneCodeHelper {
WasmCode::FlushICache flush_icache);
private:
NativeModule* source_native_module_;
const NativeModule* source_native_module_;
NativeModule* cloning_native_module_;
std::vector<uint32_t> selection_;
std::unordered_map<Address, Address, AddressHasher> reverse_lookup_;
};
void NativeModule::CloneHigherTierCodeFrom(
const NativeModule* source_native_module) {
NativeModule::CloneCodeHelper helper(source_native_module, this);
for (uint32_t i = num_imported_functions_, e = FunctionCount(); i < e; ++i) {
WasmCode* wasm_code = GetCode(i);
if (!wasm_code->is_liftoff()) continue;
helper.SelectForCloning(i);
}
helper.CloneAndPatchCode();
// Sanity check: after cloning the code, every function in the
// code table should not be Liftoff-compiled code anymore.
for (uint32_t i = num_imported_functions(), e = FunctionCount(); i < e; ++i) {
DCHECK(!GetCode(i)->is_liftoff());
}
}
NativeModule::CloneCodeHelper::CloneCodeHelper(
NativeModule* source_native_module, NativeModule* cloning_native_module)
const NativeModule* source_native_module,
NativeModule* cloning_native_module)
: source_native_module_(source_native_module),
cloning_native_module_(cloning_native_module) {
for (auto& pair : source_native_module_->trampolines_) {
@ -361,7 +381,7 @@ NativeModule::CloneCodeHelper::CloneCodeHelper(
}
}
void NativeModule::CloneCodeHelper::SelectForCloning(int32_t code_index) {
void NativeModule::CloneCodeHelper::SelectForCloning(uint32_t code_index) {
selection_.emplace_back(code_index);
}
@ -438,12 +458,12 @@ base::AtomicNumber<size_t> NativeModule::next_id_;
NativeModule::NativeModule(uint32_t num_functions, uint32_t num_imports,
bool can_request_more, VirtualMemory* mem,
WasmCodeManager* code_manager)
WasmCodeManager* code_manager, ModuleEnv& env)
: instance_id(next_id_.Increment(1)),
code_table_(num_functions),
num_imported_functions_(num_imports),
compilation_state_(NewCompilationState(
reinterpret_cast<Isolate*>(code_manager->isolate_))),
reinterpret_cast<Isolate*>(code_manager->isolate_), env)),
free_memory_(mem->address(), mem->end()),
wasm_code_manager_(code_manager),
can_request_more_memory_(can_request_more) {
@ -1021,24 +1041,25 @@ size_t WasmCodeManager::GetAllocationChunk(const WasmModule& module) {
}
std::unique_ptr<NativeModule> WasmCodeManager::NewNativeModule(
const WasmModule& module) {
const WasmModule& module, ModuleEnv& env) {
size_t code_size = GetAllocationChunk(module);
return NewNativeModule(
code_size, static_cast<uint32_t>(module.functions.size()),
module.num_imported_functions, kModuleCanAllocateMoreMemory);
module.num_imported_functions, kModuleCanAllocateMoreMemory, env);
}
std::unique_ptr<NativeModule> WasmCodeManager::NewNativeModule(
size_t size_estimate, uint32_t num_functions,
uint32_t num_imported_functions, bool can_request_more) {
uint32_t num_imported_functions, bool can_request_more, ModuleEnv& env) {
VirtualMemory mem;
TryAllocate(size_estimate, &mem);
if (mem.IsReserved()) {
Address start = mem.address();
size_t size = mem.size();
Address end = mem.end();
std::unique_ptr<NativeModule> ret(new NativeModule(
num_functions, num_imported_functions, can_request_more, &mem, this));
std::unique_ptr<NativeModule> ret(
new NativeModule(num_functions, num_imported_functions,
can_request_more, &mem, this, env));
TRACE_HEAP("New Module: ID:%zu. Mem: %p,+%zu\n", ret->instance_id,
reinterpret_cast<void*>(start), size);
AssignRanges(start, end, ret.get());
@ -1098,9 +1119,10 @@ bool NativeModule::SetExecutable(bool executable) {
}
std::unique_ptr<NativeModule> NativeModule::Clone() {
ModuleEnv* module_env = GetModuleEnv(compilation_state());
std::unique_ptr<NativeModule> ret = wasm_code_manager_->NewNativeModule(
owned_memory_.front().size(), FunctionCount(), num_imported_functions(),
can_request_more_memory_);
can_request_more_memory_, *module_env);
TRACE_HEAP("%zu cloned from %zu\n", ret->instance_id, instance_id);
if (!ret) return ret;

View File

@ -247,6 +247,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
WasmCode* GetCode(uint32_t index) const;
void SetCode(uint32_t index, WasmCode* wasm_code);
// Clones higher tier code from a {source_native_module} to
// this native module.
void CloneHigherTierCodeFrom(const NativeModule* source_native_module);
// Register/release the protected instructions in all code objects with the
// global trap handler for this process.
void UnpackAndRegisterProtectedInstructions();
@ -297,7 +301,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
static base::AtomicNumber<size_t> next_id_;
NativeModule(uint32_t num_functions, uint32_t num_imports,
bool can_request_more, VirtualMemory* vmem,
WasmCodeManager* code_manager);
WasmCodeManager* code_manager, ModuleEnv& env);
WasmCode* AddAnonymousCode(Handle<Code>, WasmCode::Kind kind);
Address AllocateForCode(size_t size);
@ -367,11 +371,13 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
// which will be page size aligned. The size of the initial memory
// is determined with a heuristic based on the total size of wasm
// code. The native module may later request more memory.
std::unique_ptr<NativeModule> NewNativeModule(const WasmModule&);
std::unique_ptr<NativeModule> NewNativeModule(const WasmModule& module,
ModuleEnv& env);
std::unique_ptr<NativeModule> NewNativeModule(size_t memory_estimate,
uint32_t num_functions,
uint32_t num_imported_functions,
bool can_request_more);
bool can_request_more,
ModuleEnv& env);
WasmCode* LookupCode(Address pc) const;
WasmCode* GetCodeFromStartAddress(Address pc) const;

View File

@ -205,6 +205,7 @@ bool CodeSpecialization::ApplyToWasmCode(wasm::WasmCode* code,
} break;
case RelocInfo::WASM_CODE_TABLE_ENTRY: {
DCHECK(FLAG_wasm_tier_up);
DCHECK(code->is_liftoff());
WasmCode* const* code_table_entry =
native_module->code_table().data() + code->index();
it.rinfo()->set_wasm_code_table_entry(

View File

@ -1357,13 +1357,13 @@ MaybeHandle<FixedArray> WasmSharedModuleData::CheckBreakPoints(
Handle<WasmCompiledModule> WasmCompiledModule::New(
Isolate* isolate, WasmModule* module, Handle<FixedArray> export_wrappers,
bool use_trap_handler) {
wasm::ModuleEnv& env) {
Handle<WasmCompiledModule> compiled_module = Handle<WasmCompiledModule>::cast(
isolate->factory()->NewStruct(WASM_COMPILED_MODULE_TYPE, TENURED));
Handle<WeakCell> weak_native_context =
isolate->factory()->NewWeakCell(isolate->native_context());
compiled_module->set_weak_native_context(*weak_native_context);
compiled_module->set_use_trap_handler(use_trap_handler);
compiled_module->set_use_trap_handler(env.use_trap_handler);
if (!export_wrappers.is_null()) {
compiled_module->set_export_wrappers(*export_wrappers);
}
@ -1371,7 +1371,7 @@ Handle<WasmCompiledModule> WasmCompiledModule::New(
wasm::NativeModule* native_module = nullptr;
{
std::unique_ptr<wasm::NativeModule> native_module_ptr =
isolate->wasm_engine()->code_manager()->NewNativeModule(*module);
isolate->wasm_engine()->code_manager()->NewNativeModule(*module, env);
native_module = native_module_ptr.release();
Handle<Foreign> native_module_wrapper =
Managed<wasm::NativeModule>::From(isolate, native_module);

View File

@ -26,6 +26,7 @@ namespace internal {
namespace wasm {
class InterpretedFrame;
class NativeModule;
struct ModuleEnv;
class WasmCode;
struct WasmModule;
class SignatureMap;
@ -545,10 +546,10 @@ class WasmCompiledModule : public Struct {
WCM_SMALL_CONST_NUMBER(bool, use_trap_handler)
public:
static Handle<WasmCompiledModule> New(
Isolate* isolate, wasm::WasmModule* module,
Handle<FixedArray> export_wrappers,
bool use_trap_hander);
static Handle<WasmCompiledModule> New(Isolate* isolate,
wasm::WasmModule* module,
Handle<FixedArray> export_wrappers,
wasm::ModuleEnv& env);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
Handle<WasmCompiledModule> module);

View File

@ -13,6 +13,7 @@
#include "src/snapshot/serializer-common.h"
#include "src/utils.h"
#include "src/version.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-manager.h"
@ -702,9 +703,14 @@ MaybeHandle<WasmCompiledModule> DeserializeNativeModule(
Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(
static_cast<int>(export_wrappers_size), TENURED);
// TODO(eholk): We need to properly preserve the flag whether the trap
// handler was used or not when serializing.
UseTrapHandler use_trap_handler =
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler;
wasm::ModuleEnv env(shared->module(), use_trap_handler,
wasm::RuntimeExceptionSupport::kRuntimeExceptionSupport);
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, shared->module(), export_wrappers,
trap_handler::IsTrapHandlerEnabled());
WasmCompiledModule::New(isolate, shared->module(), export_wrappers, env);
compiled_module->set_shared(*shared);
script->set_wasm_compiled_module(*compiled_module);
NativeModuleDeserializer deserializer(isolate,

View File

@ -17,6 +17,7 @@
#include "src/macro-assembler-inl.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes.h"
@ -123,12 +124,15 @@ Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) {
std::unique_ptr<wasm::NativeModule> AllocateNativeModule(Isolate* isolate,
size_t code_size) {
wasm::ModuleEnv env(
nullptr, wasm::UseTrapHandler::kNoTrapHandler,
wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport);
// We have to add the code object to a NativeModule, because the
// WasmCallDescriptor assumes that code is on the native heap and not
// within a code object.
std::unique_ptr<wasm::NativeModule> module =
isolate->wasm_engine()->code_manager()->NewNativeModule(code_size, 1, 0,
false);
false, env);
return module;
}

View File

@ -225,9 +225,9 @@ Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
WasmSharedModuleData::New(isolate_, module_wrapper, empty_string, script,
Handle<ByteArray>::null());
Handle<FixedArray> export_wrappers = isolate_->factory()->NewFixedArray(0);
ModuleEnv env = CreateModuleEnv();
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate_, test_module_ptr_, export_wrappers,
trap_handler::IsTrapHandlerEnabled());
WasmCompiledModule::New(isolate_, test_module_ptr_, export_wrappers, env);
compiled_module->set_shared(*shared_module_data);
// This method is called when we initialize TestEnvironment. We don't
// have a memory yet, so we won't create it here. We'll update the

View File

@ -151,12 +151,16 @@ CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count,
std::unique_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
size_t code_size) {
wasm::ModuleEnv env(
nullptr, wasm::UseTrapHandler::kNoTrapHandler,
wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport);
// We have to add the code object to a NativeModule, because the
// WasmCallDescriptor assumes that code is on the native heap and not
// within a code object.
std::unique_ptr<wasm::NativeModule> module =
isolate->wasm_engine()->code_manager()->NewNativeModule(code_size, 1, 0,
false);
false, env);
return module;
}

View File

@ -5,6 +5,7 @@
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/wasm-code-manager.h"
namespace v8 {
@ -161,11 +162,15 @@ class WasmCodeManagerTest : public TestWithContext,
// We pretend all our modules have 10 functions and no imports, just so
// we can size up the code_table.
NativeModulePtr AllocFixedModule(WasmCodeManager* manager, size_t size) {
return manager->NewNativeModule(size, 10, 0, false);
wasm::ModuleEnv env(nullptr, UseTrapHandler::kNoTrapHandler,
RuntimeExceptionSupport::kNoRuntimeExceptionSupport);
return manager->NewNativeModule(size, 10, 0, false, env);
}
NativeModulePtr AllocGrowableModule(WasmCodeManager* manager, size_t size) {
return manager->NewNativeModule(size, 10, 0, true);
wasm::ModuleEnv env(nullptr, UseTrapHandler::kNoTrapHandler,
RuntimeExceptionSupport::kNoRuntimeExceptionSupport);
return manager->NewNativeModule(size, 10, 0, true, env);
}
NativeModulePtr AllocModule(WasmCodeManager* manager, size_t size,