[vm/interpreter] Support closures in bytecode.

Support reordered bytecode metadata sections.
For now, read but ignore exceptions table in bytecode metadata.

Change-Id: I86202acc8ea22d746deaa894afd685d2f1daaaf8
Reviewed-on: https://dart-review.googlesource.com/56036
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Régis Crelier 2018-05-22 02:37:01 +00:00 committed by commit-bot@chromium.org
parent e1cabfd1d4
commit 63ff7ee922
2 changed files with 267 additions and 44 deletions

View file

@ -914,8 +914,7 @@ InferredTypeMetadata InferredTypeMetadataHelper::GetInferredType(
}
#if defined(DART_USE_INTERPRETER)
void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
// TODO(regis): Avoid copying bytecode from mapped kernel binary.
void BytecodeMetadataHelper::ReadMetadata(const Function& function) {
const intptr_t node_offset = function.kernel_offset();
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
@ -925,14 +924,51 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
md_offset);
// Read bytecode.
intptr_t bytecode_size = builder_->reader_.ReadUInt();
intptr_t bytecode_offset = builder_->reader_.offset();
uint8_t* bytecode_data = builder_->reader_.CopyDataIntoZone(
builder_->zone_, bytecode_offset, bytecode_size);
// Create object pool and read pool entries.
const intptr_t obj_count = builder_->reader_.ReadListLength();
const ObjectPool& pool =
ObjectPool::Handle(builder_->zone_, ObjectPool::New(obj_count));
ReadPoolEntries(function, function, pool, 0);
// This enum and the code below reading the constant pool from kernel must be
// kept in sync with pkg/vm/lib/bytecode/constant_pool.dart.
// Read bytecode and attach to function.
const Code& bytecode = Code::Handle(builder_->zone_, ReadBytecode(pool));
function.AttachBytecode(bytecode);
// Read exceptions table.
ReadExceptionsTable(bytecode);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(function);
}
// Read closures.
Function& closure = Function::Handle(builder_->zone_);
Code& closure_bytecode = Code::Handle(builder_->zone_);
intptr_t num_closures = builder_->ReadListLength();
for (intptr_t i = 0; i < num_closures; i++) {
intptr_t closure_index = builder_->ReadUInt();
ASSERT(closure_index < obj_count);
closure ^= pool.ObjectAt(closure_index);
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
// Read closure exceptions table.
ReadExceptionsTable(closure_bytecode);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
}
}
intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function,
const Function& inner_function,
const ObjectPool& pool,
intptr_t from_index) {
// These enums and the code below reading the constant pool from kernel must
// be kept in sync with pkg/vm/lib/bytecode/constant_pool.dart.
enum ConstantPoolTag {
kInvalid,
kNull,
@ -954,19 +990,25 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
kInstance,
kSymbol,
kTypeArgumentsForInstanceAllocation,
kContextOffset,
kClosureFunction,
kEndClosureFunctionScope,
};
enum InvocationKind {
method, // x.foo(...) or foo(...)
getter, // x.foo
setter // x.foo = ...
};
// Read object pool.
builder_->reader_.set_offset(bytecode_offset + bytecode_size);
intptr_t obj_count = builder_->reader_.ReadListLength();
const ObjectPool& obj_pool =
ObjectPool::Handle(builder_->zone_, ObjectPool::New(obj_count));
Object& obj = Object::Handle(builder_->zone_);
Object& elem = Object::Handle(builder_->zone_);
Array& array = Array::Handle(builder_->zone_);
Field& field = Field::Handle(builder_->zone_);
Class& cls = Class::Handle(builder_->zone_);
String& name = String::Handle(builder_->zone_);
for (intptr_t i = 0; i < obj_count; ++i) {
const intptr_t obj_count = pool.Length();
for (intptr_t i = from_index; i < obj_count; ++i) {
const intptr_t tag = builder_->ReadTag();
switch (tag) {
case ConstantPoolTag::kInvalid:
@ -1018,7 +1060,7 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
name = H.DartSymbolPlain(target).raw();
intptr_t arg_desc_index = builder_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= obj_pool.ObjectAt(arg_desc_index);
array ^= pool.ObjectAt(arg_desc_index);
// TODO(regis): Should num_args_tested be explicitly provided?
obj = ICData::New(function, name,
array, // Arguments descriptor.
@ -1029,18 +1071,34 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
#endif
} break;
case ConstantPoolTag::kStaticICData: {
InvocationKind kind = static_cast<InvocationKind>(builder_->ReadByte());
NameIndex target = builder_->ReadCanonicalNameReference();
if (H.IsConstructor(target)) {
name = H.DartConstructorName(target).raw();
elem = H.LookupConstructorByKernelConstructor(target);
} else if (H.IsField(target)) {
if (kind == InvocationKind::getter) {
name = H.DartGetterName(target).raw();
} else if (kind == InvocationKind::setter) {
name = H.DartSetterName(target).raw();
} else {
ASSERT(kind == InvocationKind::method);
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
field = H.LookupFieldByKernelField(target);
cls = field.Owner();
elem = cls.LookupStaticFunction(name);
} else {
if ((kind == InvocationKind::method) && H.IsGetter(target)) {
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
name = H.DartProcedureName(target).raw();
elem = H.LookupStaticMethodByKernelProcedure(target);
}
ASSERT(elem.IsFunction());
intptr_t arg_desc_index = builder_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= obj_pool.ObjectAt(arg_desc_index);
array ^= pool.ObjectAt(arg_desc_index);
obj = ICData::New(function, name,
array, // Arguments descriptor.
Thread::kNoDeoptId, 0 /* num_args_tested */,
@ -1067,11 +1125,9 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
ASSERT(obj.IsClass());
break;
case ConstantPoolTag::kTypeArgumentsFieldOffset:
obj =
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsClass());
obj = Smi::New(Class::Cast(obj).type_arguments_field_offset() /
kWordSize);
obj = Smi::New(cls.type_arguments_field_offset() / kWordSize);
break;
case ConstantPoolTag::kTearOff:
obj = H.LookupStaticMethodByKernelProcedure(
@ -1101,20 +1157,19 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
for (intptr_t j = 0; j < length; j++) {
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = obj_pool.ObjectAt(elem_index);
elem = pool.ObjectAt(elem_index);
array.SetAt(j, elem);
}
obj = H.Canonicalize(Array::Cast(obj));
ASSERT(!obj.IsNull());
} break;
case ConstantPoolTag::kInstance: {
obj =
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsClass());
obj = Instance::New(Class::Cast(obj), Heap::kOld);
obj = Instance::New(cls, Heap::kOld);
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = obj_pool.ObjectAt(elem_index);
elem = pool.ObjectAt(elem_index);
if (!elem.IsNull()) {
ASSERT(elem.IsTypeArguments());
Instance::Cast(obj).SetTypeArguments(TypeArguments::Cast(elem));
@ -1126,7 +1181,7 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
field = H.LookupFieldByKernelField(field_name);
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = obj_pool.ObjectAt(elem_index);
elem = pool.ObjectAt(elem_index);
Instance::Cast(obj).SetField(field, elem);
}
obj = H.Canonicalize(Instance::Cast(obj));
@ -1136,34 +1191,193 @@ void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
ASSERT(String::Cast(obj).IsSymbol());
break;
case kTypeArgumentsForInstanceAllocation: {
obj =
cls =
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
ASSERT(obj.IsClass());
intptr_t elem_index = builder_->ReadUInt();
ASSERT(elem_index < i);
elem = obj_pool.ObjectAt(elem_index);
elem = pool.ObjectAt(elem_index);
ASSERT(elem.IsNull() || elem.IsTypeArguments());
elem = Type::New(Class::Cast(obj), TypeArguments::Cast(elem),
TokenPosition::kNoSource);
elem = ClassFinalizer::FinalizeType(Class::Cast(obj), Type::Cast(elem));
elem =
Type::New(cls, TypeArguments::Cast(elem), TokenPosition::kNoSource);
elem = ClassFinalizer::FinalizeType(cls, Type::Cast(elem));
obj = Type::Cast(elem).arguments();
} break;
case ConstantPoolTag::kContextOffset: {
intptr_t index = builder_->ReadUInt();
if (i == 0) {
obj = Smi::New(Context::parent_offset() / kWordSize);
} else {
obj = Smi::New(Context::variable_offset(index - 1) / kWordSize);
}
} break;
case ConstantPoolTag::kClosureFunction: {
name = H.DartSymbolPlain(builder_->ReadStringReference()).raw();
const Function& closure = Function::Handle(
builder_->zone_,
Function::NewClosureFunction(name, inner_function,
TokenPosition::kNoSource));
FunctionNodeHelper function_node_helper(builder_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kTypeParameters);
builder_->LoadAndSetupTypeParameters(builder_->active_class(), closure,
builder_->ReadListLength(),
closure);
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
// Scope remains opened until ConstantPoolTag::kEndClosureFunctionScope.
ActiveTypeParametersScope scope(
builder_->active_class(), &closure,
TypeArguments::Handle(builder_->zone_, closure.type_parameters()),
builder_->zone_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t required_parameter_count =
function_node_helper.required_parameter_count_;
intptr_t total_parameter_count =
function_node_helper.total_parameter_count_;
intptr_t positional_parameter_count = builder_->ReadListLength();
intptr_t named_parameter_count =
total_parameter_count - positional_parameter_count;
const intptr_t extra_parameters = 1;
closure.set_num_fixed_parameters(extra_parameters +
required_parameter_count);
if (named_parameter_count > 0) {
closure.SetNumOptionalParameters(named_parameter_count, false);
} else {
closure.SetNumOptionalParameters(
positional_parameter_count - required_parameter_count, true);
}
intptr_t parameter_count = extra_parameters + total_parameter_count;
closure.set_parameter_types(Array::Handle(
builder_->zone_, Array::New(parameter_count, Heap::kOld)));
closure.set_parameter_names(Array::Handle(
builder_->zone_, Array::New(parameter_count, Heap::kOld)));
intptr_t pos = 0;
closure.SetParameterTypeAt(pos, AbstractType::dynamic_type());
closure.SetParameterNameAt(pos, Symbols::ClosureParameter());
pos++;
for (intptr_t j = 0; j < positional_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(builder_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type =
builder_->type_translator_.BuildVariableType();
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
builder_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartSymbolObfuscate(helper.name_index_));
}
intptr_t named_parameter_count_check = builder_->ReadListLength();
ASSERT(named_parameter_count_check == named_parameter_count);
for (intptr_t j = 0; j < named_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(builder_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type =
builder_->type_translator_.BuildVariableType();
Tag tag = builder_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
builder_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartSymbolObfuscate(helper.name_index_));
}
function_node_helper.SetJustRead(FunctionNodeHelper::kNamedParameters);
const AbstractType& return_type =
builder_->type_translator_.BuildVariableType();
closure.set_result_type(return_type);
function_node_helper.SetJustRead(FunctionNodeHelper::kReturnType);
// The closure has no body.
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, closure);
// Continue reading the constant pool entries inside the opened
// ActiveTypeParametersScope until the scope gets closed by a
// kEndClosureFunctionScope tag, in which case control returns here.
i = ReadPoolEntries(function, closure, pool, i + 1);
// Pool entry at index i has been set to null, because it was a
// kEndClosureFunctionScope.
ASSERT(pool.ObjectAt(i) == Object::null());
continue;
}
case ConstantPoolTag::kEndClosureFunctionScope: {
// Entry is not used and set to null.
obj = Object::null();
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, obj);
return i; // The caller will close the scope.
} break;
default:
UNREACHABLE();
}
obj_pool.SetTypeAt(i, ObjectPool::kTaggedObject);
obj_pool.SetObjectAt(i, obj);
pool.SetTypeAt(i, ObjectPool::kTaggedObject);
pool.SetObjectAt(i, obj);
}
// Return the index of the last read pool entry.
return obj_count - 1;
}
const Code& bytecode = Code::Handle(
builder_->zone_,
Code::FinalizeBytecode(reinterpret_cast<void*>(bytecode_data),
bytecode_size, obj_pool));
function.AttachBytecode(bytecode);
RawCode* BytecodeMetadataHelper::ReadBytecode(const ObjectPool& pool) {
// TODO(regis): Avoid copying bytecode from mapped kernel binary.
intptr_t size = builder_->reader_.ReadUInt();
intptr_t offset = builder_->reader_.offset();
uint8_t* data =
builder_->reader_.CopyDataIntoZone(builder_->zone_, offset, size);
builder_->reader_.set_offset(offset + size);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(function);
// Create and return code object.
return Code::FinalizeBytecode(reinterpret_cast<void*>(data), size, pool);
}
void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode) {
const ObjectPool& pool =
ObjectPool::Handle(builder_->zone_, bytecode.object_pool());
AbstractType& handled_type = AbstractType::Handle(builder_->zone_);
// Encoding of ExceptionsTable is described in
// pkg/vm/lib/bytecode/exceptions.dart.
intptr_t try_block_count = builder_->reader_.ReadListLength();
for (intptr_t i = 0; i < try_block_count; i++) {
intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt();
intptr_t outer_try_index = outer_try_index_plus1 - 1;
USE(outer_try_index);
intptr_t start_pc = builder_->reader_.ReadUInt();
USE(start_pc);
intptr_t end_pc = builder_->reader_.ReadUInt();
USE(end_pc);
intptr_t handler_pc = builder_->reader_.ReadUInt();
USE(handler_pc);
uint8_t flags = builder_->reader_.ReadByte();
// flagNeedsStackTrace = 1 << 0;
// flagIsSynthetic = 1 << 1;
USE(flags);
intptr_t type_count = builder_->reader_.ReadListLength();
for (intptr_t j = 0; j < type_count; j++) {
intptr_t type_index = builder_->reader_.ReadUInt();
ASSERT(type_index < pool.Length());
handled_type ^= pool.ObjectAt(type_index);
}
}
// TODO(regis): Generate exception handlers (as well as pc descriptors)
// and store in bytecode: bytecode.set_exception_handlers(exception_handlers);
}
#endif // defined(DART_USE_INTERPRETER)
@ -6171,7 +6385,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
// TODO(regis): Clean up this logic of when to compile.
// If the bytecode was previously loaded, we really want to compile.
if (!function.HasBytecode()) {
bytecode_metadata_helper_.CopyBytecode(function);
bytecode_metadata_helper_.ReadMetadata(function);
if (function.HasBytecode()) {
return NULL;
}

View file

@ -670,7 +670,16 @@ class BytecodeMetadataHelper : public MetadataHelper {
: MetadataHelper(builder) {}
#if defined(DART_USE_INTERPRETER)
void CopyBytecode(const Function& function);
void ReadMetadata(const Function& function);
private:
// Returns the index of the last read pool entry.
intptr_t ReadPoolEntries(const Function& function,
const Function& inner_function,
const ObjectPool& pool,
intptr_t from_index);
RawCode* ReadBytecode(const ObjectPool& pool);
void ReadExceptionsTable(const Code& bytecode);
#endif
};