From 449446571f2211ecdbda997452a6ce61cf016bc9 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Tue, 13 Aug 2019 20:32:49 +0000 Subject: [PATCH] [vm/bytecode] Collect context levels for compiled code generated from bytecode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia2047440e05fce3263f8feb38be4cedd4357ab10 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112920 Reviewed-by: RĂ©gis Crelier Commit-Queue: Alexander Markov --- .../frontend/base_flow_graph_builder.cc | 12 ++ .../frontend/base_flow_graph_builder.h | 15 +++ .../frontend/bytecode_flow_graph_builder.cc | 46 +++++++- .../frontend/bytecode_flow_graph_builder.h | 6 + .../vm/compiler/frontend/bytecode_reader.cc | 25 ++-- runtime/vm/compiler/jit/compiler.cc | 41 ++++--- runtime/vm/debugger.cc | 6 +- runtime/vm/scopes.cc | 108 ++++++++++-------- runtime/vm/scopes.h | 38 +++++- 9 files changed, 205 insertions(+), 92 deletions(-) diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc index a717c3b1747..2b70b9dfef6 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc @@ -993,6 +993,18 @@ Fragment BaseFlowGraphBuilder::StringInterpolate(TokenPosition position) { return Fragment(interpolate); } +void BaseFlowGraphBuilder::reset_context_depth_for_deopt_id(intptr_t deopt_id) { + if (is_recording_context_levels()) { + for (intptr_t i = 0, n = context_level_array_->length(); i < n; i += 2) { + if (context_level_array_->At(i) == deopt_id) { + (*context_level_array_)[i + 1] = context_depth_; + return; + } + ASSERT(context_level_array_->At(i) < deopt_id); + } + } +} + } // namespace kernel } // namespace dart diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h index 8f79aebca5f..dbd2b681bdd 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h @@ -361,6 +361,21 @@ class BaseFlowGraphBuilder { // _StringBase._interpolate call. Fragment StringInterpolate(TokenPosition position); + // Returns true if we're currently recording deopt_id -> context level + // mapping. + bool is_recording_context_levels() const { + return context_level_array_ != nullptr; + } + + // Sets current context level. It will be recorded for all subsequent + // deopt ids (until it is adjusted again). + void set_context_depth(intptr_t context_level) { + context_depth_ = context_level; + } + + // Reset context level for the given deopt id (which was allocated earlier). + void reset_context_depth_for_deopt_id(intptr_t deopt_id); + protected: intptr_t AllocateBlockId() { return ++last_used_block_id_; } diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc index f0e7b05e803..f7704900ca2 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc @@ -966,6 +966,7 @@ void BytecodeFlowGraphBuilder::BuildDynamicCall() { } else { ASSERT(ic_data_array_->At(deopt_id)->Original() == icdata.raw()); } + B->reset_context_depth_for_deopt_id(deopt_id); const intptr_t argc = DecodeOperandF().value(); const ArgumentsDescriptor arg_desc( @@ -1806,14 +1807,13 @@ static bool IsICDataEntry(const ObjectPool& object_pool, intptr_t index) { // pre-populate ic_data_array_. void BytecodeFlowGraphBuilder::ProcessICDataInObjectPool( const ObjectPool& object_pool) { - CompilerState& compiler_state = thread()->compiler_state(); - ASSERT(compiler_state.deopt_id() == 0); + ASSERT(thread()->compiler_state().deopt_id() == 0); const intptr_t pool_length = object_pool.Length(); for (intptr_t i = 0; i < pool_length; ++i) { if (IsICDataEntry(object_pool, i)) { const ICData& icdata = ICData::CheckedHandle(Z, object_pool.ObjectAt(i)); - const intptr_t deopt_id = compiler_state.GetNextDeoptId(); + const intptr_t deopt_id = B->GetNextDeoptId(); ASSERT(icdata.deopt_id() == deopt_id); } } @@ -2017,6 +2017,35 @@ void BytecodeFlowGraphBuilder::CreateParameterVariables() { } } +#if !defined(PRODUCT) +intptr_t BytecodeFlowGraphBuilder::UpdateContextLevel(const Bytecode& bytecode, + intptr_t pc) { + ASSERT(B->is_recording_context_levels()); + + kernel::BytecodeLocalVariablesIterator iter(Z, bytecode); + intptr_t context_level = 0; + intptr_t next_pc = bytecode_length_; + while (iter.MoveNext()) { + if (iter.IsScope()) { + if (iter.StartPC() <= pc) { + if (pc < iter.EndPC()) { + // Found enclosing scope. Keep looking as we might find more + // scopes (the last one is the most specific). + context_level = iter.ContextLevel(); + next_pc = iter.EndPC(); + } + } else { + next_pc = Utils::Minimum(next_pc, iter.StartPC()); + break; + } + } + } + + B->set_context_depth(context_level); + return next_pc; +} +#endif // !defined(PRODUCT) + FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() { const Bytecode& bytecode = Bytecode::Handle(Z, function().bytecode()); @@ -2041,6 +2070,11 @@ FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() { kernel::BytecodeSourcePositionsIterator source_pos_iter(Z, bytecode); bool update_position = source_pos_iter.MoveNext(); +#if !defined(PRODUCT) + intptr_t next_pc_to_update_context_level = + B->is_recording_context_levels() ? 0 : bytecode_length_; +#endif + code_ = Fragment(normal_entry); for (pc_ = 0; pc_ < bytecode_length_; pc_ = next_pc_) { @@ -2073,6 +2107,12 @@ FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() { update_position = source_pos_iter.MoveNext(); } +#if !defined(PRODUCT) + if (pc_ >= next_pc_to_update_context_level) { + next_pc_to_update_context_level = UpdateContextLevel(bytecode, pc_); + } +#endif + BuildInstruction(KernelBytecode::DecodeOpcode(bytecode_instr_)); if (code_.is_closed()) { diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h index 1373875ec89..92aeca3ae1f 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h @@ -167,6 +167,12 @@ class BytecodeFlowGraphBuilder { const ExceptionHandlers& handlers, GraphEntryInstr* graph_entry); +#if !defined(PRODUCT) + // Update context level for the given bytecode PC. returns + // next PC where context level might need an update. + intptr_t UpdateContextLevel(const Bytecode& bytecode, intptr_t pc); +#endif + // Figure out entry points style. UncheckedEntryPointStyle ChooseEntryPointStyle( const KBCInstr* jump_if_unchecked); diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc index c9db0cbe543..0da39c6f996 100644 --- a/runtime/vm/compiler/frontend/bytecode_reader.cc +++ b/runtime/vm/compiler/frontend/bytecode_reader.cc @@ -17,6 +17,7 @@ #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/reusable_handles.h" +#include "vm/scopes.h" #include "vm/stack_frame_kbc.h" #include "vm/timeline.h" @@ -3459,11 +3460,7 @@ RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors( ASSERT(!bytecode.IsNull()); ASSERT(function.bytecode() == bytecode.raw()); - struct VarDesc { - const String* name; - RawLocalVarDescriptors::VarInfo info; - }; - GrowableArray vars(8); + LocalVarDescriptorsBuilder vars; if (function.IsLocalFunction()) { const auto& parent = Function::Handle(zone, function.parent_function()); @@ -3484,8 +3481,8 @@ RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors( function.token_pos() <= var_info.end_pos) || (function.token_pos() <= var_info.begin_pos && var_info.begin_pos <= function.end_token_pos()))) { - vars.Add( - VarDesc{&String::Handle(zone, parent_vars.GetName(i)), var_info}); + vars.Add(LocalVarDescriptorsBuilder::VarDesc{ + &String::Handle(zone, parent_vars.GetName(i)), var_info}); } } } @@ -3501,7 +3498,7 @@ RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors( context_level = local_vars.ContextLevel(); } break; case BytecodeLocalVariablesIterator::kVariableDeclaration: { - VarDesc desc; + LocalVarDescriptorsBuilder::VarDesc desc; desc.name = &String::Handle(zone, local_vars.Name()); if (local_vars.IsCaptured()) { desc.info.set_kind(RawLocalVarDescriptors::kContextVar); @@ -3526,7 +3523,7 @@ RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors( case BytecodeLocalVariablesIterator::kContextVariable: { ASSERT(local_vars.Index() >= 0); const intptr_t context_variable_index = -local_vars.Index(); - VarDesc desc; + LocalVarDescriptorsBuilder::VarDesc desc; desc.name = &Symbols::CurrentContextVar(); desc.info.set_kind(RawLocalVarDescriptors::kSavedCurrentContext); desc.info.scope_id = 0; @@ -3540,15 +3537,7 @@ RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors( } } - if (vars.is_empty()) { - return Object::empty_var_descriptors().raw(); - } - const LocalVarDescriptors& var_desc = LocalVarDescriptors::Handle( - zone, LocalVarDescriptors::New(vars.length())); - for (intptr_t i = 0; i < vars.length(); i++) { - var_desc.SetVar(i, *(vars[i].name), &vars[i].info); - } - return var_desc.raw(); + return vars.Done(); } #endif // !defined(PRODUCT) diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc index 05a9a88c68f..bf052f9de3a 100644 --- a/runtime/vm/compiler/jit/compiler.cc +++ b/runtime/vm/compiler/jit/compiler.cc @@ -998,38 +998,43 @@ void Compiler::ComputeLocalVarDescriptors(const Code& code) { ASSERT(code.var_descriptors() == Object::null()); // IsIrregexpFunction have eager var descriptors generation. ASSERT(!function.IsIrregexpFunction()); - if (function.is_declared_in_bytecode()) { - auto& var_descs = LocalVarDescriptors::Handle(); - if (function.HasBytecode()) { - const auto& bytecode = Bytecode::Handle(function.bytecode()); - var_descs = bytecode.GetLocalVarDescriptors(); - } else { - var_descs = Object::empty_var_descriptors().raw(); - } - code.set_var_descriptors(var_descs); - return; - } // In background compilation, parser can produce 'errors": bailouts // if state changed while compiling in background. - CompilerState state(Thread::Current()); + Thread* thread = Thread::Current(); + Zone* zone = thread->zone(); + CompilerState state(thread); LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { - ParsedFunction* parsed_function = new ParsedFunction( - Thread::Current(), Function::ZoneHandle(function.raw())); + ParsedFunction* parsed_function = + new ParsedFunction(thread, Function::ZoneHandle(zone, function.raw())); ZoneGrowableArray* ic_data_array = new ZoneGrowableArray(); ZoneGrowableArray* context_level_array = new ZoneGrowableArray(); - parsed_function->EnsureKernelScopes(); kernel::FlowGraphBuilder builder( parsed_function, ic_data_array, context_level_array, /* not inlining */ NULL, false, Compiler::kNoOSRDeoptId); builder.BuildGraph(); - const LocalVarDescriptors& var_descs = - LocalVarDescriptors::Handle(parsed_function->scope()->GetVarDescriptors( - function, context_level_array)); + auto& var_descs = LocalVarDescriptors::Handle(zone); + + if (function.is_declared_in_bytecode()) { + if (function.HasBytecode()) { + const auto& bytecode = Bytecode::Handle(zone, function.bytecode()); + var_descs = bytecode.GetLocalVarDescriptors(); + LocalVarDescriptorsBuilder builder; + builder.AddDeoptIdToContextLevelMappings(context_level_array); + builder.AddAll(zone, var_descs); + var_descs = builder.Done(); + } else { + var_descs = Object::empty_var_descriptors().raw(); + } + } else { + var_descs = parsed_function->scope()->GetVarDescriptors( + function, context_level_array); + } + ASSERT(!var_descs.IsNull()); code.set_var_descriptors(var_descs); } else { diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 406e1e087ef..0788350addb 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -789,8 +789,10 @@ intptr_t ActivationFrame::ContextLevel() { while (local_vars.MoveNext()) { if (local_vars.Kind() == kernel::BytecodeLocalVariablesIterator::kScope) { - if (local_vars.StartPC() <= pc_offset && - pc_offset <= local_vars.EndPC()) { + if (local_vars.StartPC() > pc_offset) { + break; + } + if (pc_offset <= local_vars.EndPC()) { ASSERT(context_level_ <= local_vars.ContextLevel()); context_level_ = local_vars.ContextLevel(); } diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc index b29e821d2be..626403168d9 100644 --- a/runtime/vm/scopes.cc +++ b/runtime/vm/scopes.cc @@ -296,38 +296,8 @@ static bool IsFilteredIdentifier(const String& str) { RawLocalVarDescriptors* LocalScope::GetVarDescriptors( const Function& func, ZoneGrowableArray* context_level_array) { - GrowableArray vars(8); - - // Record deopt-id -> context-level mappings, using ranges of deopt-ids with - // the same context-level. [context_level_array] contains (deopt_id, - // context_level) tuples. - for (intptr_t start = 0; start < context_level_array->length();) { - intptr_t start_deopt_id = (*context_level_array)[start]; - intptr_t start_context_level = (*context_level_array)[start + 1]; - intptr_t end = start; - intptr_t end_deopt_id = start_deopt_id; - for (intptr_t peek = start + 2; peek < context_level_array->length(); - peek += 2) { - intptr_t peek_deopt_id = (*context_level_array)[peek]; - intptr_t peek_context_level = (*context_level_array)[peek + 1]; - // The range encoding assumes the tuples have ascending deopt_ids. - ASSERT(peek_deopt_id > end_deopt_id); - if (peek_context_level != start_context_level) break; - end = peek; - end_deopt_id = peek_deopt_id; - } - - VarDesc desc; - desc.name = &Symbols::Empty(); // No name. - desc.info.set_kind(RawLocalVarDescriptors::kContextLevel); - desc.info.scope_id = 0; - desc.info.begin_pos = TokenPosition(start_deopt_id); - desc.info.end_pos = TokenPosition(end_deopt_id); - desc.info.set_index(start_context_level); - vars.Add(desc); - - start = end + 2; - } + LocalVarDescriptorsBuilder vars; + vars.AddDeoptIdToContextLevelMappings(context_level_array); // First enter all variables from scopes of outer functions. const ContextScope& context_scope = @@ -343,7 +313,7 @@ RawLocalVarDescriptors* LocalScope::GetVarDescriptors( continue; } - VarDesc desc; + LocalVarDescriptorsBuilder::VarDesc desc; desc.name = &name; desc.info.set_kind(kind); desc.info.scope_id = context_scope.ContextLevelAt(i); @@ -359,20 +329,12 @@ RawLocalVarDescriptors* LocalScope::GetVarDescriptors( int16_t scope_id = 0; CollectLocalVariables(&vars, &scope_id); - if (vars.length() == 0) { - return Object::empty_var_descriptors().raw(); - } - const LocalVarDescriptors& var_desc = - LocalVarDescriptors::Handle(LocalVarDescriptors::New(vars.length())); - for (int i = 0; i < vars.length(); i++) { - var_desc.SetVar(i, *(vars[i].name), &vars[i].info); - } - return var_desc.raw(); + return vars.Done(); } // Add visible variables that are declared in this scope to vars, then // collect visible variables of children, followed by siblings. -void LocalScope::CollectLocalVariables(GrowableArray* vars, +void LocalScope::CollectLocalVariables(LocalVarDescriptorsBuilder* vars, int16_t* scope_id) { (*scope_id)++; for (int i = 0; i < this->variables_.length(); i++) { @@ -381,7 +343,7 @@ void LocalScope::CollectLocalVariables(GrowableArray* vars, if (var->name().raw() == Symbols::CurrentContextVar().raw()) { // This is the local variable in which the function saves its // own context before calling a closure function. - VarDesc desc; + LocalVarDescriptorsBuilder::VarDesc desc; desc.name = &var->name(); desc.info.set_kind(RawLocalVarDescriptors::kSavedCurrentContext); desc.info.scope_id = 0; @@ -392,7 +354,7 @@ void LocalScope::CollectLocalVariables(GrowableArray* vars, vars->Add(desc); } else if (!IsFilteredIdentifier(var->name())) { // This is a regular Dart variable, either stack-based or captured. - VarDesc desc; + LocalVarDescriptorsBuilder::VarDesc desc; desc.name = &var->name(); if (var->is_captured()) { desc.info.set_kind(RawLocalVarDescriptors::kContextVar); @@ -713,6 +675,62 @@ bool LocalVariable::Equals(const LocalVariable& other) const { return false; } +void LocalVarDescriptorsBuilder::AddAll(Zone* zone, + const LocalVarDescriptors& var_descs) { + for (intptr_t i = 0, n = var_descs.Length(); i < n; ++i) { + VarDesc desc; + desc.name = &String::Handle(zone, var_descs.GetName(i)); + var_descs.GetInfo(i, &desc.info); + Add(desc); + } +} + +void LocalVarDescriptorsBuilder::AddDeoptIdToContextLevelMappings( + ZoneGrowableArray* context_level_array) { + // Record deopt-id -> context-level mappings, using ranges of deopt-ids with + // the same context-level. [context_level_array] contains (deopt_id, + // context_level) tuples. + for (intptr_t start = 0; start < context_level_array->length();) { + intptr_t start_deopt_id = (*context_level_array)[start]; + intptr_t start_context_level = (*context_level_array)[start + 1]; + intptr_t end = start; + intptr_t end_deopt_id = start_deopt_id; + for (intptr_t peek = start + 2; peek < context_level_array->length(); + peek += 2) { + intptr_t peek_deopt_id = (*context_level_array)[peek]; + intptr_t peek_context_level = (*context_level_array)[peek + 1]; + // The range encoding assumes the tuples have ascending deopt_ids. + ASSERT(peek_deopt_id > end_deopt_id); + if (peek_context_level != start_context_level) break; + end = peek; + end_deopt_id = peek_deopt_id; + } + + VarDesc desc; + desc.name = &Symbols::Empty(); // No name. + desc.info.set_kind(RawLocalVarDescriptors::kContextLevel); + desc.info.scope_id = 0; + desc.info.begin_pos = TokenPosition(start_deopt_id); + desc.info.end_pos = TokenPosition(end_deopt_id); + desc.info.set_index(start_context_level); + Add(desc); + + start = end + 2; + } +} + +RawLocalVarDescriptors* LocalVarDescriptorsBuilder::Done() { + if (vars_.is_empty()) { + return Object::empty_var_descriptors().raw(); + } + const LocalVarDescriptors& var_desc = + LocalVarDescriptors::Handle(LocalVarDescriptors::New(vars_.length())); + for (int i = 0; i < vars_.length(); i++) { + var_desc.SetVar(i, *(vars_[i].name), &vars_[i].info); + } + return var_desc.raw(); +} + } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h index 7a0bde63cdf..71ef849540f 100644 --- a/runtime/vm/scopes.h +++ b/runtime/vm/scopes.h @@ -215,6 +215,36 @@ class LocalVariable : public ZoneAllocated { DISALLOW_COPY_AND_ASSIGN(LocalVariable); }; +// Accumulates local variable descriptors while building +// LocalVarDescriptors object. +class LocalVarDescriptorsBuilder : public ValueObject { + public: + struct VarDesc { + const String* name; + RawLocalVarDescriptors::VarInfo info; + }; + + LocalVarDescriptorsBuilder() : vars_(8) {} + + // Add variable descriptor. + void Add(const VarDesc& var_desc) { vars_.Add(var_desc); } + + // Add all variable descriptors from given [LocalVarDescriptors] object. + void AddAll(Zone* zone, const LocalVarDescriptors& var_descs); + + // Record deopt-id -> context-level mappings, using ranges of deopt-ids with + // the same context-level. [context_level_array] contains (deopt_id, + // context_level) tuples. + void AddDeoptIdToContextLevelMappings( + ZoneGrowableArray* context_level_array); + + // Finish building LocalVarDescriptor object. + RawLocalVarDescriptors* Done(); + + private: + GrowableArray vars_; +}; + class NameReference : public ZoneAllocated { public: NameReference(TokenPosition token_pos, const String& name) @@ -439,11 +469,6 @@ class LocalScope : public ZoneAllocated { static RawContextScope* CreateImplicitClosureScope(const Function& func); private: - struct VarDesc { - const String* name; - RawLocalVarDescriptors::VarInfo info; - }; - // Allocate the variable in the current context, possibly updating the current // context owner scope, if the variable is the first one to be allocated at // this loop level. @@ -452,7 +477,8 @@ class LocalScope : public ZoneAllocated { void AllocateContextVariable(LocalVariable* variable, LocalScope** context_owner); - void CollectLocalVariables(GrowableArray* vars, int16_t* scope_id); + void CollectLocalVariables(LocalVarDescriptorsBuilder* vars, + int16_t* scope_id); NameReference* FindReference(const String& name) const;