[vm/bytecode] Collect context levels for compiled code generated from bytecode

Change-Id: Ia2047440e05fce3263f8feb38be4cedd4357ab10
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112920
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2019-08-13 20:32:49 +00:00 committed by commit-bot@chromium.org
parent 5852f5ae92
commit 449446571f
9 changed files with 205 additions and 92 deletions

View file

@ -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

View file

@ -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_; }

View file

@ -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()) {

View file

@ -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);

View file

@ -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<VarDesc> 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)

View file

@ -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<const ICData*>* ic_data_array =
new ZoneGrowableArray<const ICData*>();
ZoneGrowableArray<intptr_t>* context_level_array =
new ZoneGrowableArray<intptr_t>();
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 {

View file

@ -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();
}

View file

@ -296,38 +296,8 @@ static bool IsFilteredIdentifier(const String& str) {
RawLocalVarDescriptors* LocalScope::GetVarDescriptors(
const Function& func,
ZoneGrowableArray<intptr_t>* context_level_array) {
GrowableArray<VarDesc> 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<VarDesc>* 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<VarDesc>* 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<VarDesc>* 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<intptr_t>* 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)

View file

@ -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<intptr_t>* context_level_array);
// Finish building LocalVarDescriptor object.
RawLocalVarDescriptors* Done();
private:
GrowableArray<VarDesc> 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<VarDesc>* vars, int16_t* scope_id);
void CollectLocalVariables(LocalVarDescriptorsBuilder* vars,
int16_t* scope_id);
NameReference* FindReference(const String& name) const;