Commit 9b81ab7d authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Publish background compiled code in batches

In order to reduce lock contention in the NativeModule, to publish
compiled code in batches.
This is implemented via a new {NativeModule::AddCompiledCode} variant
that takes a {Vector<WasmCompilationResult>}, allocates code space for
all of the results, copies all code over and relocates it, and then
publishes all of it.

R=titzer@chromium.org

Bug: v8:8916
Change-Id: I437bd222dc2471b89b114cdb42049991af36f1f4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1532062
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60373}
parent 9f6ddb48
...@@ -695,18 +695,46 @@ class BackgroundCompileTask : public CancelableTask { ...@@ -695,18 +695,46 @@ class BackgroundCompileTask : public CancelableTask {
} }
} }
std::vector<WasmCompilationResult> results_to_publish;
auto publish_results =
[&results_to_publish](BackgroundCompileScope* compile_scope) {
if (results_to_publish.empty()) return;
// TODO(clemensh): Refactor {OnFinishedUnit} and remove this.
std::vector<ExecutionTier> requested_tiers;
requested_tiers.reserve(results_to_publish.size());
for (auto& result : results_to_publish) {
requested_tiers.push_back(result.requested_tier);
}
std::vector<WasmCode*> generated_code =
compile_scope->native_module()->AddCompiledCode(
VectorOf(results_to_publish));
results_to_publish.clear();
// Account for the finished compilation units.
// TODO(clemensh): This takes a lock on each invokation. Only do this
// once and pass accumulated counts.
DCHECK_EQ(generated_code.size(), requested_tiers.size());
for (size_t i = 0; i < generated_code.size(); ++i) {
compile_scope->compilation_state()->OnFinishedUnit(
requested_tiers[i], generated_code[i]);
}
};
bool compilation_failed = false; bool compilation_failed = false;
while (true) { while (true) {
// (asynchronous): Execute the compilation. // (asynchronous): Execute the compilation.
WasmCompilationResult result = unit->ExecuteCompilation( WasmCompilationResult result = unit->ExecuteCompilation(
&env.value(), wire_bytes, async_counters_.get(), &detected_features); &env.value(), wire_bytes, async_counters_.get(), &detected_features);
results_to_publish.emplace_back(std::move(result));
// (synchronized): Publish the compilation result and get the next unit. // (synchronized): Publish the compilation result and get the next unit.
{ {
BackgroundCompileScope compile_scope(token_); BackgroundCompileScope compile_scope(token_);
if (compile_scope.cancelled()) return; if (compile_scope.cancelled()) return;
if (!result.succeeded()) { if (!results_to_publish.back().succeeded()) {
// Compile error. // Compile error.
compile_scope.compilation_state()->SetError(); compile_scope.compilation_state()->SetError();
compile_scope.compilation_state()->OnBackgroundTaskStopped( compile_scope.compilation_state()->OnBackgroundTaskStopped(
...@@ -714,14 +742,13 @@ class BackgroundCompileTask : public CancelableTask { ...@@ -714,14 +742,13 @@ class BackgroundCompileTask : public CancelableTask {
compilation_failed = true; compilation_failed = true;
break; break;
} }
WasmCode* code = // Publish TurboFan units immediately to reduce peak memory consumption.
compile_scope.native_module()->AddCompiledCode(std::move(result)); if (result.requested_tier == ExecutionTier::kOptimized) {
DCHECK_NOT_NULL(code); publish_results(&compile_scope);
}
// Successfully finished one unit.
compile_scope.compilation_state()->OnFinishedUnit(result.requested_tier,
code);
if (deadline < MonotonicallyIncreasingTimeInMs()) { if (deadline < MonotonicallyIncreasingTimeInMs()) {
publish_results(&compile_scope);
compile_scope.compilation_state()->ReportDetectedFeatures( compile_scope.compilation_state()->ReportDetectedFeatures(
detected_features); detected_features);
compile_scope.compilation_state()->RestartBackgroundCompileTask(); compile_scope.compilation_state()->RestartBackgroundCompileTask();
...@@ -731,6 +758,7 @@ class BackgroundCompileTask : public CancelableTask { ...@@ -731,6 +758,7 @@ class BackgroundCompileTask : public CancelableTask {
// Get next unit. // Get next unit.
unit = compile_scope.compilation_state()->GetNextCompilationUnit(); unit = compile_scope.compilation_state()->GetNextCompilationUnit();
if (unit == nullptr) { if (unit == nullptr) {
publish_results(&compile_scope);
compile_scope.compilation_state()->OnBackgroundTaskStopped( compile_scope.compilation_state()->OnBackgroundTaskStopped(
detected_features); detected_features);
return; return;
......
...@@ -393,8 +393,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled, ...@@ -393,8 +393,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
uint32_t num_wasm_functions = module_->num_declared_functions; uint32_t num_wasm_functions = module_->num_declared_functions;
if (num_wasm_functions > 0) { if (num_wasm_functions > 0) {
code_table_.reset(new WasmCode*[num_wasm_functions]); code_table_.reset(new WasmCode* [num_wasm_functions] {});
memset(code_table_.get(), 0, num_wasm_functions * sizeof(WasmCode*));
jump_table_ = CreateEmptyJumpTable( jump_table_ = CreateEmptyJumpTable(
JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions)); JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions));
...@@ -403,8 +402,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled, ...@@ -403,8 +402,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
void NativeModule::ReserveCodeTableForTesting(uint32_t max_functions) { void NativeModule::ReserveCodeTableForTesting(uint32_t max_functions) {
DCHECK_LE(num_functions(), max_functions); DCHECK_LE(num_functions(), max_functions);
WasmCode** new_table = new WasmCode*[max_functions]; WasmCode** new_table = new WasmCode* [max_functions] {};
memset(new_table, 0, max_functions * sizeof(*new_table));
if (module_->num_declared_functions > 0) { if (module_->num_declared_functions > 0) {
memcpy(new_table, code_table_.get(), memcpy(new_table, code_table_.get(),
module_->num_declared_functions * sizeof(*new_table)); module_->num_declared_functions * sizeof(*new_table));
...@@ -534,10 +532,12 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code, ...@@ -534,10 +532,12 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code,
const size_t code_comments_offset = const size_t code_comments_offset =
static_cast<size_t>(code->code_comments_offset()); static_cast<size_t>(code->code_comments_offset());
Vector<uint8_t> code_space = AllocateForCode(instructions.size());
memcpy(code_space.begin(), instructions.start(), instructions.size());
std::unique_ptr<WasmCode> new_code{new WasmCode{ std::unique_ptr<WasmCode> new_code{new WasmCode{
this, // native_module this, // native_module
WasmCode::kAnonymousFuncIndex, // index WasmCode::kAnonymousFuncIndex, // index
AllocateForCode(instructions.size()), // code_space code_space, // instructions
stack_slots, // stack_slots stack_slots, // stack_slots
0, // tagged_parameter_slots 0, // tagged_parameter_slots
safepoint_table_offset, // safepoint_table_offset safepoint_table_offset, // safepoint_table_offset
...@@ -551,8 +551,6 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code, ...@@ -551,8 +551,6 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code,
kind, // kind kind, // kind
WasmCode::kOther}}; // tier WasmCode::kOther}}; // tier
memcpy(new_code->instructions().start(), instructions.start(),
instructions.size());
// Apply the relocation delta by iterating over the RelocInfo. // Apply the relocation delta by iterating over the RelocInfo.
intptr_t delta = new_code->instruction_start() - code->InstructionStart(); intptr_t delta = new_code->instruction_start() - code->InstructionStart();
int mode_mask = RelocInfo::kApplyMask | int mode_mask = RelocInfo::kApplyMask |
...@@ -588,6 +586,18 @@ std::unique_ptr<WasmCode> NativeModule::AddCode( ...@@ -588,6 +586,18 @@ std::unique_ptr<WasmCode> NativeModule::AddCode(
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions, OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
OwnedVector<const byte> source_position_table, WasmCode::Kind kind, OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
WasmCode::Tier tier) { WasmCode::Tier tier) {
return AddCodeWithCodeSpace(index, desc, stack_slots, tagged_parameter_slots,
std::move(protected_instructions),
std::move(source_position_table), kind, tier,
AllocateForCode(desc.instr_size));
}
std::unique_ptr<WasmCode> NativeModule::AddCodeWithCodeSpace(
uint32_t index, const CodeDesc& desc, uint32_t stack_slots,
uint32_t tagged_parameter_slots,
OwnedVector<ProtectedInstructionData> protected_instructions,
OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
WasmCode::Tier tier, Vector<uint8_t> code_space) {
OwnedVector<byte> reloc_info; OwnedVector<byte> reloc_info;
if (desc.reloc_size > 0) { if (desc.reloc_size > 0) {
reloc_info = OwnedVector<byte>::New(desc.reloc_size); reloc_info = OwnedVector<byte>::New(desc.reloc_size);
...@@ -608,15 +618,13 @@ std::unique_ptr<WasmCode> NativeModule::AddCode( ...@@ -608,15 +618,13 @@ std::unique_ptr<WasmCode> NativeModule::AddCode(
static_cast<size_t>(desc.code_comments_offset); static_cast<size_t>(desc.code_comments_offset);
const size_t instr_size = static_cast<size_t>(desc.instr_size); const size_t instr_size = static_cast<size_t>(desc.instr_size);
std::unique_ptr<WasmCode> code{new WasmCode{ memcpy(code_space.begin(), desc.buffer, static_cast<size_t>(desc.instr_size));
this, index, AllocateForCode(desc.instr_size), stack_slots,
tagged_parameter_slots, safepoint_table_offset, handler_table_offset,
constant_pool_offset, code_comments_offset, instr_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_position_table), kind, tier}};
memcpy(code->instructions().start(), desc.buffer, std::unique_ptr<WasmCode> code{new WasmCode{
static_cast<size_t>(desc.instr_size)); this, index, code_space, stack_slots, tagged_parameter_slots,
safepoint_table_offset, handler_table_offset, constant_pool_offset,
code_comments_offset, instr_size, std::move(protected_instructions),
std::move(reloc_info), std::move(source_position_table), kind, tier}};
// Apply the relocation delta by iterating over the RelocInfo. // Apply the relocation delta by iterating over the RelocInfo.
intptr_t delta = code->instructions().start() - desc.buffer; intptr_t delta = code->instructions().start() - desc.buffer;
...@@ -655,6 +663,12 @@ std::unique_ptr<WasmCode> NativeModule::AddCode( ...@@ -655,6 +663,12 @@ std::unique_ptr<WasmCode> NativeModule::AddCode(
WasmCode* NativeModule::PublishCode(std::unique_ptr<WasmCode> code) { WasmCode* NativeModule::PublishCode(std::unique_ptr<WasmCode> code) {
base::MutexGuard lock(&allocation_mutex_); base::MutexGuard lock(&allocation_mutex_);
return PublishCodeLocked(std::move(code));
}
WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) {
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
DCHECK(!allocation_mutex_.TryLock());
// Skip publishing code if there is an active redirection to the interpreter // Skip publishing code if there is an active redirection to the interpreter
// for the given function index, in order to preserve the redirection. // for the given function index, in order to preserve the redirection.
if (!code->IsAnonymous() && !has_interpreter_redirection(code->index())) { if (!code->IsAnonymous() && !has_interpreter_redirection(code->index())) {
...@@ -691,16 +705,16 @@ WasmCode* NativeModule::AddDeserializedCode( ...@@ -691,16 +705,16 @@ WasmCode* NativeModule::AddDeserializedCode(
OwnedVector<const byte> reloc_info, OwnedVector<const byte> reloc_info,
OwnedVector<const byte> source_position_table, WasmCode::Kind kind, OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
WasmCode::Tier tier) { WasmCode::Tier tier) {
Vector<uint8_t> code_space = AllocateForCode(instructions.size());
memcpy(code_space.begin(), instructions.start(), instructions.size());
std::unique_ptr<WasmCode> code{new WasmCode{ std::unique_ptr<WasmCode> code{new WasmCode{
this, index, AllocateForCode(instructions.size()), stack_slots, this, index, code_space, stack_slots, tagged_parameter_slots,
tagged_parameter_slots, safepoint_table_offset, handler_table_offset, safepoint_table_offset, handler_table_offset, constant_pool_offset,
constant_pool_offset, code_comments_offset, unpadded_binary_size, code_comments_offset, unpadded_binary_size,
std::move(protected_instructions), std::move(reloc_info), std::move(protected_instructions), std::move(reloc_info),
std::move(source_position_table), kind, tier}}; std::move(source_position_table), kind, tier}};
memcpy(code->instructions().start(), instructions.start(),
instructions.size());
code->RegisterTrapHandlerData(); code->RegisterTrapHandlerData();
// Note: we do not flush the i-cache here, since the code needs to be // Note: we do not flush the i-cache here, since the code needs to be
...@@ -720,10 +734,12 @@ std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const { ...@@ -720,10 +734,12 @@ std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const {
WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) { WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) {
// Only call this if we really need a jump table. // Only call this if we really need a jump table.
DCHECK_LT(0, jump_table_size); DCHECK_LT(0, jump_table_size);
Vector<uint8_t> code_space = AllocateForCode(jump_table_size);
memset(code_space.begin(), 0, code_space.size());
std::unique_ptr<WasmCode> code{new WasmCode{ std::unique_ptr<WasmCode> code{new WasmCode{
this, // native_module this, // native_module
WasmCode::kAnonymousFuncIndex, // index WasmCode::kAnonymousFuncIndex, // index
AllocateForCode(jump_table_size), // instructions code_space, // instructions
0, // stack_slots 0, // stack_slots
0, // tagged_parameter_slots 0, // tagged_parameter_slots
0, // safepoint_table_offset 0, // safepoint_table_offset
...@@ -736,7 +752,6 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) { ...@@ -736,7 +752,6 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) {
OwnedVector<const uint8_t>{}, // source_pos OwnedVector<const uint8_t>{}, // source_pos
WasmCode::kJumpTable, // kind WasmCode::kJumpTable, // kind
WasmCode::kOther}}; // tier WasmCode::kOther}}; // tier
memset(code->instructions().start(), 0, code->instructions().size());
return PublishCode(std::move(code)); return PublishCode(std::move(code));
} }
...@@ -1205,17 +1220,49 @@ WasmCode::Kind GetCodeKindForExecutionTier(ExecutionTier tier) { ...@@ -1205,17 +1220,49 @@ WasmCode::Kind GetCodeKindForExecutionTier(ExecutionTier tier) {
} // namespace } // namespace
WasmCode* NativeModule::AddCompiledCode(WasmCompilationResult result) { WasmCode* NativeModule::AddCompiledCode(WasmCompilationResult result) {
DCHECK(result.succeeded()); return AddCompiledCode({&result, 1})[0];
}
DCHECK_EQ(result.code_desc.buffer, result.instr_buffer.get()); std::vector<WasmCode*> NativeModule::AddCompiledCode(
std::unique_ptr<WasmCode> code = AddCode( Vector<WasmCompilationResult> results) {
result.func_index, result.code_desc, result.frame_slot_count, DCHECK(!results.is_empty());
result.tagged_parameter_slots, std::move(result.protected_instructions), // First, allocate code space for all the results.
std::move(result.source_positions), size_t total_code_space = 0;
GetCodeKindForExecutionTier(result.result_tier), for (auto& result : results) {
GetCodeTierForExecutionTier(result.result_tier)); DCHECK(result.succeeded());
total_code_space += RoundUp<kCodeAlignment>(result.code_desc.instr_size);
}
Vector<byte> code_space = AllocateForCode(total_code_space);
std::vector<std::unique_ptr<WasmCode>> generated_code;
generated_code.reserve(results.size());
// Now copy the generated code into the code space and relocate it.
for (auto& result : results) {
DCHECK_EQ(result.code_desc.buffer, result.instr_buffer.get());
size_t code_size = RoundUp<kCodeAlignment>(result.code_desc.instr_size);
Vector<byte> this_code_space = code_space.SubVector(0, code_size);
code_space += code_size;
generated_code.emplace_back(AddCodeWithCodeSpace(
result.func_index, result.code_desc, result.frame_slot_count,
result.tagged_parameter_slots, std::move(result.protected_instructions),
std::move(result.source_positions),
GetCodeKindForExecutionTier(result.result_tier),
GetCodeTierForExecutionTier(result.result_tier), this_code_space));
}
DCHECK_EQ(0, code_space.size());
// Under the {allocation_mutex_}, publish the code.
std::vector<WasmCode*> returned_code;
returned_code.reserve(results.size());
{
base::MutexGuard lock(&allocation_mutex_);
for (auto& result : generated_code) {
returned_code.push_back(PublishCodeLocked(std::move(result)));
}
}
return PublishCode(std::move(code)); return returned_code;
} }
void WasmCodeManager::FreeNativeModule(NativeModule* native_module) { void WasmCodeManager::FreeNativeModule(NativeModule* native_module) {
......
...@@ -245,6 +245,8 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -245,6 +245,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
// the code table and patching the jump table. It returns a raw pointer to the // the code table and patching the jump table. It returns a raw pointer to the
// given {WasmCode} object. // given {WasmCode} object.
WasmCode* PublishCode(std::unique_ptr<WasmCode>); WasmCode* PublishCode(std::unique_ptr<WasmCode>);
// Hold the {allocation_mutex_} when calling {PublishCodeLocked}.
WasmCode* PublishCodeLocked(std::unique_ptr<WasmCode>);
WasmCode* AddDeserializedCode( WasmCode* AddDeserializedCode(
uint32_t index, Vector<const byte> instructions, uint32_t stack_slots, uint32_t index, Vector<const byte> instructions, uint32_t stack_slots,
...@@ -367,6 +369,7 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -367,6 +369,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
void SampleCodeSize(Counters*, CodeSamplingTime) const; void SampleCodeSize(Counters*, CodeSamplingTime) const;
WasmCode* AddCompiledCode(WasmCompilationResult); WasmCode* AddCompiledCode(WasmCompilationResult);
std::vector<WasmCode*> AddCompiledCode(Vector<WasmCompilationResult>);
private: private:
friend class WasmCode; friend class WasmCode;
...@@ -380,6 +383,14 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -380,6 +383,14 @@ class V8_EXPORT_PRIVATE NativeModule final {
std::shared_ptr<Counters> async_counters, std::shared_ptr<Counters> async_counters,
std::shared_ptr<NativeModule>* shared_this); std::shared_ptr<NativeModule>* shared_this);
std::unique_ptr<WasmCode> AddCodeWithCodeSpace(
uint32_t index, const CodeDesc& desc, uint32_t stack_slots,
uint32_t tagged_parameter_slots,
OwnedVector<trap_handler::ProtectedInstructionData>
protected_instructions,
OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
WasmCode::Tier tier, Vector<uint8_t> code_space);
// Add and publish anonymous code. // Add and publish anonymous code.
WasmCode* AddAndPublishAnonymousCode(Handle<Code>, WasmCode::Kind kind, WasmCode* AddAndPublishAnonymousCode(Handle<Code>, WasmCode::Kind kind,
const char* name = nullptr); const char* name = nullptr);
......
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