Files
sdk/runtime/vm/kernel_binary_flowgraph.cc
T
Kevin Millikin 2e794c4e16 Move Kernel strings into the VM's heap.
- Instead of a pointer to new'd memory, Kernel strings now have an
  offset from the start of the string data.

- When the streaming reader encounters the string data it records the
  offset from the start of the binary.  This offset is stored in the
  Kernel Program and is used to compute the offset for strings.

- When a KernelReader is constructed, the string data is copied into a
  Uint8 array in the VM's heap.

- A pointer to the string data is put into every Kernel script so it
  can be used for constructing VM strings at compile time.

The source table does not use Kernel strings any more because those
strings are not found in the raw string data.  Instead, the source
table uses new'd buffers for strings (but this will be cleaned up
separately).

R=jensj@google.com

Review-Url: https://codereview.chromium.org/2820363002 .
2017-04-21 10:32:48 +02:00

649 lines
20 KiB
C++

// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/kernel_binary_flowgraph.h"
#include "vm/object_store.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
namespace kernel {
#define Z (zone_)
#define H (translation_helper_)
#define I Isolate::Current()
StreamingConstantEvaluator::StreamingConstantEvaluator(
StreamingFlowGraphBuilder* builder,
Zone* zone,
TranslationHelper* h,
DartTypeTranslator* type_translator)
: builder_(builder),
isolate_(Isolate::Current()),
zone_(zone),
translation_helper_(*h),
// type_translator_(*type_translator),
script_(Script::Handle(
zone,
builder == NULL ? Script::null()
: builder_->parsed_function()->function().script())),
result_(Instance::Handle(zone)) {}
Instance& StreamingConstantEvaluator::EvaluateExpression() {
intptr_t offset = builder_->ReaderOffset();
if (!GetCachedConstant(offset, &result_)) {
uint8_t payload = 0;
Tag tag = builder_->ReadTag(&payload);
switch (tag) {
case kStaticGet:
EvaluateStaticGet();
break;
case kSymbolLiteral:
EvaluateSymbolLiteral();
break;
case kDoubleLiteral:
EvaluateDoubleLiteral();
break;
default:
UNREACHABLE();
}
CacheConstantValue(offset, result_);
}
// We return a new `ZoneHandle` here on purpose: The intermediate language
// instructions do not make a copy of the handle, so we do it.
return dart::Instance::ZoneHandle(Z, result_.raw());
}
void StreamingConstantEvaluator::EvaluateStaticGet() {
builder_->ReadPosition();
int canonical_name_index = builder_->ReadUInt();
CanonicalName* target = builder_->GetCanonicalName(canonical_name_index);
if (H.IsField(target)) {
const dart::Field& field =
dart::Field::Handle(Z, H.LookupFieldByKernelField(target));
if (field.StaticValue() == Object::sentinel().raw() ||
field.StaticValue() == Object::transition_sentinel().raw()) {
field.EvaluateInitializer();
result_ = field.StaticValue();
result_ = H.Canonicalize(result_);
field.SetStaticValue(result_, true);
} else {
result_ = field.StaticValue();
}
} else if (H.IsProcedure(target)) {
const Function& function =
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
if (H.IsMethod(target)) {
Function& closure_function =
Function::ZoneHandle(Z, function.ImplicitClosureFunction());
closure_function.set_kernel_function(function.kernel_function());
result_ = closure_function.ImplicitStaticClosure();
result_ = H.Canonicalize(result_);
} else if (H.IsGetter(target)) {
UNIMPLEMENTED();
} else {
UNIMPLEMENTED();
}
}
}
void StreamingConstantEvaluator::EvaluateSymbolLiteral() {
int str_index = builder_->ReadUInt();
const dart::String& symbol_value = builder_->DartSymbol(str_index);
const dart::Class& symbol_class =
dart::Class::ZoneHandle(Z, I->object_store()->symbol_class());
ASSERT(!symbol_class.IsNull());
const dart::Function& symbol_constructor = Function::ZoneHandle(
Z, symbol_class.LookupConstructor(Symbols::SymbolCtor()));
ASSERT(!symbol_constructor.IsNull());
result_ ^= EvaluateConstConstructorCall(
symbol_class, TypeArguments::Handle(Z), symbol_constructor, symbol_value);
}
void StreamingConstantEvaluator::EvaluateDoubleLiteral() {
int str_index = builder_->ReadUInt();
result_ = dart::Double::New(builder_->DartString(str_index), Heap::kOld);
result_ = H.Canonicalize(result_);
}
RawObject* StreamingConstantEvaluator::EvaluateConstConstructorCall(
const dart::Class& type_class,
const TypeArguments& type_arguments,
const Function& constructor,
const Object& argument) {
// Factories have one extra argument: the type arguments.
// Constructors have 1 extra arguments: receiver.
const int kNumArgs = 1;
const int kNumExtraArgs = 1;
const int num_arguments = kNumArgs + kNumExtraArgs;
const Array& arg_values =
Array::Handle(Z, Array::New(num_arguments, Heap::kOld));
Instance& instance = Instance::Handle(Z);
if (!constructor.IsFactory()) {
instance = Instance::New(type_class, Heap::kOld);
if (!type_arguments.IsNull()) {
ASSERT(type_arguments.IsInstantiated());
instance.SetTypeArguments(
TypeArguments::Handle(Z, type_arguments.Canonicalize()));
}
arg_values.SetAt(0, instance);
} else {
// Prepend type_arguments to list of arguments to factory.
ASSERT(type_arguments.IsZoneHandle());
arg_values.SetAt(0, type_arguments);
}
arg_values.SetAt((0 + kNumExtraArgs), argument);
const Array& args_descriptor = Array::Handle(
Z, ArgumentsDescriptor::New(num_arguments, Object::empty_array()));
const Object& result = Object::Handle(
Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor));
ASSERT(!result.IsError());
if (constructor.IsFactory()) {
// The factory method returns the allocated object.
instance ^= result.raw();
}
return H.Canonicalize(instance);
}
bool StreamingConstantEvaluator::GetCachedConstant(intptr_t kernel_offset,
Instance* value) {
if (builder_ == NULL) return false;
const Function& function = builder_->parsed_function()->function();
if (function.kind() == RawFunction::kImplicitStaticFinalGetter) {
// Don't cache constants in initializer expressions. They get
// evaluated only once.
return false;
}
bool is_present = false;
ASSERT(!script_.InVMHeap());
if (script_.compile_time_constants() == Array::null()) {
return false;
}
KernelConstantsMap constants(script_.compile_time_constants());
*value ^= constants.GetOrNull(kernel_offset, &is_present);
// Mutator compiler thread may add constants while background compiler
// is running, and thus change the value of 'compile_time_constants';
// do not assert that 'compile_time_constants' has not changed.
constants.Release();
if (FLAG_compiler_stats && is_present) {
H.thread()->compiler_stats()->num_const_cache_hits++;
}
return is_present;
}
void StreamingConstantEvaluator::CacheConstantValue(intptr_t kernel_offset,
const Instance& value) {
ASSERT(Thread::Current()->IsMutatorThread());
if (builder_ == NULL) return;
const Function& function = builder_->parsed_function()->function();
if (function.kind() == RawFunction::kImplicitStaticFinalGetter) {
// Don't cache constants in initializer expressions. They get
// evaluated only once.
return;
}
const intptr_t kInitialConstMapSize = 16;
ASSERT(!script_.InVMHeap());
if (script_.compile_time_constants() == Array::null()) {
const Array& array = Array::Handle(
HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew));
script_.set_compile_time_constants(array);
}
KernelConstantsMap constants(script_.compile_time_constants());
constants.InsertNewOrGetValue(kernel_offset, value);
script_.set_compile_time_constants(constants.Release());
}
Fragment StreamingFlowGraphBuilder::BuildAt(intptr_t kernel_offset) {
SetOffset(kernel_offset);
uint8_t payload = 0;
Tag tag = ReadTag(&payload);
switch (tag) {
case kInvalidExpression:
return BuildInvalidExpression();
// case kVariableGet:
// return VariableGet::ReadFrom(reader_);
// case kSpecializedVariableGet:
// return VariableGet::ReadFrom(reader_, payload);
// case kVariableSet:
// return VariableSet::ReadFrom(reader_);
// case kSpecializedVariableSet:
// return VariableSet::ReadFrom(reader_, payload);
// case kPropertyGet:
// return PropertyGet::ReadFrom(reader_);
// case kPropertySet:
// return PropertySet::ReadFrom(reader_);
// case kDirectPropertyGet:
// return DirectPropertyGet::ReadFrom(reader_);
// case kDirectPropertySet:
// return DirectPropertySet::ReadFrom(reader_);
case kStaticGet:
return BuildStaticGet();
// case kStaticSet:
// return StaticSet::ReadFrom(reader_);
// case kMethodInvocation:
// return MethodInvocation::ReadFrom(reader_);
// case kDirectMethodInvocation:
// return DirectMethodInvocation::ReadFrom(reader_);
// case kStaticInvocation:
// return StaticInvocation::ReadFrom(reader_, false);
// case kConstStaticInvocation:
// return StaticInvocation::ReadFrom(reader_, true);
// case kConstructorInvocation:
// return ConstructorInvocation::ReadFrom(reader_, false);
// case kConstConstructorInvocation:
// return ConstructorInvocation::ReadFrom(reader_, true);
// case kNot:
// return Not::ReadFrom(reader_);
// case kLogicalExpression:
// return LogicalExpression::ReadFrom(reader_);
// case kConditionalExpression:
// return ConditionalExpression::ReadFrom(reader_);
// case kStringConcatenation:
// return StringConcatenation::ReadFrom(reader_);
// case kIsExpression:
// return IsExpression::ReadFrom(reader_);
// case kAsExpression:
// return AsExpression::ReadFrom(reader_);
case kSymbolLiteral:
return BuildSymbolLiteral();
// case kTypeLiteral:
// return TypeLiteral::ReadFrom(reader_);
case kThisExpression:
return BuildThisExpression();
case kRethrow:
return BuildRethrow();
// case kThrow:
// return Throw::ReadFrom(reader_);
// case kListLiteral:
// return ListLiteral::ReadFrom(reader_, false);
// case kConstListLiteral:
// return ListLiteral::ReadFrom(reader_, true);
// case kMapLiteral:
// return MapLiteral::ReadFrom(reader_, false);
// case kConstMapLiteral:
// return MapLiteral::ReadFrom(reader_, true);
// case kAwaitExpression:
// return AwaitExpression::ReadFrom(reader_);
// case kFunctionExpression:
// return FunctionExpression::ReadFrom(reader_);
// case kLet:
// return Let::ReadFrom(reader_);
case kBigIntLiteral:
return BuildBigIntLiteral();
case kStringLiteral:
return BuildStringLiteral();
case kSpecialIntLiteral:
return BuildIntLiteral(payload);
case kNegativeIntLiteral:
return BuildIntLiteral(true);
case kPositiveIntLiteral:
return BuildIntLiteral(false);
case kDoubleLiteral:
return BuildDoubleLiteral();
case kTrueLiteral:
return BuildBoolLiteral(true);
case kFalseLiteral:
return BuildBoolLiteral(false);
case kNullLiteral:
return BuildNullLiteral();
default:
UNREACHABLE();
}
return Fragment();
}
intptr_t StreamingFlowGraphBuilder::GetStringOffset(intptr_t index) {
if (string_offsets_ == NULL) {
intptr_t saved_offset = ReaderOffset();
reader_->set_offset(4); // Skip kMagicProgramFile to the string table.
string_offset_count_ = ReadListLength() + 1;
string_offsets_ = new intptr_t[string_offset_count_];
// Build a table of the 0-based string start and end offsets.
string_offsets_[0] = 0;
for (intptr_t i = 1; i < string_offset_count_; ++i) {
string_offsets_[i] = ReadUInt();
}
// Mark the start of the string data to use for decoding strings.
reader_->MarkStringDataOffset();
SetOffset(saved_offset);
}
return string_offsets_[index];
}
CanonicalName* StreamingFlowGraphBuilder::GetCanonicalName(intptr_t index) {
if (index == 0) return NULL;
--index;
if (canonical_names_ != NULL && canonical_names_entries_read_ > index) {
return canonical_names_[index];
}
intptr_t saved_offset = ReaderOffset();
if (canonical_names_ == NULL) {
// Find offset from where to read canonical names table.
// Skip the magic number.
reader_->set_offset(4);
// Skip the string table. The last offset is the end offset of the last
// string which gives the length of the string data.
intptr_t list_length = ReadListLength();
intptr_t end_offset = 0;
for (intptr_t i = 0; i < list_length; ++i) {
end_offset = ReadUInt();
}
SkipBytes(end_offset);
// There is another string table for the source URIs. Skip it as well.
list_length = ReadListLength();
end_offset = 0;
for (intptr_t i = 0; i < list_length; ++i) {
end_offset = ReadUInt();
}
SkipBytes(end_offset);
// Source code table
for (intptr_t i = 0; i < list_length; ++i) {
// Source code
intptr_t bytes = ReadUInt();
SkipBytes(bytes);
// Line starts table
intptr_t line_count = ReadUInt();
for (intptr_t j = 0; j < line_count; ++j) {
ReadUInt();
}
}
// Now at canonical names table.
canonical_names_size_ = ReadUInt();
canonical_names_ = new CanonicalName*[canonical_names_size_];
canonical_names_next_offset_ = ReaderOffset();
}
SetOffset(canonical_names_next_offset_);
for (; canonical_names_entries_read_ <= index;
++canonical_names_entries_read_) {
intptr_t biased_parent_index = ReadUInt();
CanonicalName* parent;
if (biased_parent_index != 0) {
parent = canonical_names_[biased_parent_index - 1];
} else {
if (canonical_names_entries_read_ == 0) {
parent = CanonicalName::NewRoot();
} else {
parent = canonical_names_[0]->parent();
}
}
ASSERT(parent != NULL);
intptr_t name_index = ReadUInt();
String* name = KernelString(name_index);
CanonicalName* canonical_name = parent->AddChild(name);
canonical_names_[canonical_names_entries_read_] = canonical_name;
}
canonical_names_next_offset_ = ReaderOffset();
SetOffset(saved_offset);
return canonical_names_[index];
}
intptr_t StreamingFlowGraphBuilder::ReaderOffset() {
return reader_->offset();
}
void StreamingFlowGraphBuilder::SetOffset(intptr_t offset) {
reader_->set_offset(offset);
}
void StreamingFlowGraphBuilder::SkipBytes(intptr_t bytes) {
reader_->set_offset(ReaderOffset() + bytes);
}
uint32_t StreamingFlowGraphBuilder::ReadUInt() {
return reader_->ReadUInt();
}
intptr_t StreamingFlowGraphBuilder::ReadListLength() {
return reader_->ReadListLength();
}
TokenPosition StreamingFlowGraphBuilder::ReadPosition(bool record) {
return reader_->ReadPosition(record);
}
Tag StreamingFlowGraphBuilder::ReadTag(uint8_t* payload) {
return reader_->ReadTag(payload);
}
CatchBlock* StreamingFlowGraphBuilder::catch_block() {
return flow_graph_builder_->catch_block_;
}
ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() {
return flow_graph_builder_->scopes_;
}
ParsedFunction* StreamingFlowGraphBuilder::parsed_function() {
return flow_graph_builder_->parsed_function_;
}
dart::String& StreamingFlowGraphBuilder::DartSymbol(intptr_t index) {
intptr_t start = GetStringOffset(index);
intptr_t end = GetStringOffset(index + 1);
return H.DartSymbol(reader_->buffer() + reader_->string_data_offset() + start,
end - start);
}
dart::String& StreamingFlowGraphBuilder::DartString(intptr_t index) {
intptr_t start = GetStringOffset(index);
intptr_t end = GetStringOffset(index + 1);
return H.DartString(reader_->buffer() + reader_->string_data_offset() + start,
end - start);
}
String* StreamingFlowGraphBuilder::KernelString(intptr_t index) {
intptr_t start = GetStringOffset(index);
intptr_t end = GetStringOffset(index + 1);
return new String(start, end - start);
}
Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
return flow_graph_builder_->DebugStepCheck(position);
}
Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) {
return flow_graph_builder_->LoadLocal(variable);
}
Fragment StreamingFlowGraphBuilder::PushArgument() {
return flow_graph_builder_->PushArgument();
}
Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position,
int catch_try_index) {
return flow_graph_builder_->RethrowException(position, catch_try_index);
}
Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError() {
return flow_graph_builder_->ThrowNoSuchMethodError();
}
Fragment StreamingFlowGraphBuilder::Constant(const Object& value) {
return flow_graph_builder_->Constant(value);
}
Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) {
return flow_graph_builder_->IntConstant(value);
}
Fragment StreamingFlowGraphBuilder::LoadStaticField() {
return flow_graph_builder_->LoadStaticField();
}
Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
const Function& target,
intptr_t argument_count) {
return flow_graph_builder_->StaticCall(position, target, argument_count);
}
Fragment StreamingFlowGraphBuilder::BuildInvalidExpression() {
// The frontend will take care of emitting normal errors (like
// [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special
// situations (e.g. an invalid annotation).
return ThrowNoSuchMethodError();
}
Fragment StreamingFlowGraphBuilder::BuildStaticGet() {
intptr_t saved_offset = ReaderOffset() - 1; // Include the tag.
TokenPosition position = ReadPosition();
int canonical_name_index = ReadUInt();
CanonicalName* target = GetCanonicalName(canonical_name_index);
if (H.IsField(target)) {
const dart::Field& field =
dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
if (field.is_const()) {
SetOffset(saved_offset); // EvaluateExpression needs the tag.
return Constant(constant_evaluator_.EvaluateExpression());
} else {
const dart::Class& owner = dart::Class::Handle(Z, field.Owner());
const dart::String& getter_name = H.DartGetterName(target);
const Function& getter =
Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name));
if (getter.IsNull() || !field.has_initializer()) {
Fragment instructions = Constant(field);
return instructions + LoadStaticField();
} else {
return StaticCall(position, getter, 0);
}
}
} else {
const Function& function =
Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
if (H.IsGetter(target)) {
return StaticCall(position, function, 0);
} else if (H.IsMethod(target)) {
SetOffset(saved_offset); // EvaluateExpression needs the tag.
return Constant(constant_evaluator_.EvaluateExpression());
} else {
UNIMPLEMENTED();
}
}
return Fragment();
}
Fragment StreamingFlowGraphBuilder::BuildSymbolLiteral() {
SkipBytes(-1); // EvaluateExpression needs the tag.
return Constant(constant_evaluator_.EvaluateExpression());
}
Fragment StreamingFlowGraphBuilder::BuildThisExpression() {
return LoadLocal(scopes()->this_variable);
}
Fragment StreamingFlowGraphBuilder::BuildRethrow() {
TokenPosition position = ReadPosition();
Fragment instructions = DebugStepCheck(position);
instructions += LoadLocal(catch_block()->exception_var());
instructions += PushArgument();
instructions += LoadLocal(catch_block()->stack_trace_var());
instructions += PushArgument();
instructions += RethrowException(position, catch_block()->catch_try_index());
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral() {
const dart::String& value = DartString(ReadUInt());
return Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)));
}
Fragment StreamingFlowGraphBuilder::BuildStringLiteral() {
intptr_t str_index = ReadUInt();
return Constant(DartSymbol(str_index));
}
Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload) {
int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
return IntConstant(value);
}
Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative) {
int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt()) : ReadUInt();
return IntConstant(value);
}
Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral() {
SkipBytes(-1); // EvaluateExpression needs the tag.
return Constant(constant_evaluator_.EvaluateExpression());
}
Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value) {
return Constant(Bool::Get(value));
}
Fragment StreamingFlowGraphBuilder::BuildNullLiteral() {
return Constant(Instance::ZoneHandle(Z, Instance::null()));
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)