[vm,dyn_modules] Initial support for hot reloading bytecode
TEST=ci Change-Id: I879ff1c085ee06dda7836d01e2018d93e075f132 Cq-Include-Trybots: luci.dart.try:vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-linux-debug-x64-try,vm-reload-linux-debug-x64-try,vm-reload-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/439803 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
8a38216560
commit
bbb5e731c3
@@ -29,9 +29,11 @@ Uint8List _generateBytecode(
|
||||
hierarchy: hierarchy,
|
||||
target: target,
|
||||
options: BytecodeOptions(
|
||||
enableAsserts: enableAsserts,
|
||||
emitSourcePositions: true,
|
||||
emitLocalVarInfo: true));
|
||||
enableAsserts: enableAsserts,
|
||||
emitSourcePositions: true,
|
||||
emitLocalVarInfo: true,
|
||||
emitInstanceFieldInitializers: true,
|
||||
));
|
||||
return byteSink.builder.takeBytes();
|
||||
}
|
||||
|
||||
|
||||
@@ -84,8 +84,10 @@ BytecodeLoader::~BytecodeLoader() {
|
||||
FunctionPtr BytecodeLoader::LoadBytecode() {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
|
||||
BytecodeReaderHelper component_reader(thread_, binary_);
|
||||
bytecode_component_array_ = component_reader.ReadBytecodeComponent();
|
||||
if (bytecode_component_array_.IsNull()) {
|
||||
BytecodeReaderHelper component_reader(thread_, binary_);
|
||||
bytecode_component_array_ = component_reader.ReadBytecodeComponent();
|
||||
}
|
||||
|
||||
BytecodeComponentData bytecode_component(bytecode_component_array_);
|
||||
BytecodeReaderHelper bytecode_reader(thread_, &bytecode_component);
|
||||
@@ -108,7 +110,7 @@ void BytecodeLoader::SetOffset(const Object& obj, intptr_t offset) {
|
||||
bytecode_offsets_map_ = map.Release().ptr();
|
||||
}
|
||||
|
||||
intptr_t BytecodeLoader::GetOffset(const Object& obj) {
|
||||
intptr_t BytecodeLoader::GetOffset(const Object& obj) const {
|
||||
BytecodeOffsetsMap map(bytecode_offsets_map_.ptr());
|
||||
const auto value = map.GetOrNull(obj);
|
||||
ASSERT(value != Object::null());
|
||||
@@ -117,6 +119,41 @@ intptr_t BytecodeLoader::GetOffset(const Object& obj) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool BytecodeLoader::HasOffset(const Object& obj) const {
|
||||
BytecodeOffsetsMap map(bytecode_offsets_map_.ptr());
|
||||
const auto value = map.GetOrNull(obj);
|
||||
ASSERT(map.Release().ptr() == bytecode_offsets_map_.ptr());
|
||||
return value != Object::null();
|
||||
}
|
||||
|
||||
void BytecodeLoader::FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) {
|
||||
if (bytecode_component_array_.IsNull()) {
|
||||
BytecodeReaderHelper component_reader(thread_, binary_);
|
||||
bytecode_component_array_ = component_reader.ReadBytecodeComponent();
|
||||
}
|
||||
|
||||
BytecodeComponentData bytecode_component(bytecode_component_array_);
|
||||
BytecodeReaderHelper bytecode_reader(thread_, &bytecode_component);
|
||||
AlternativeReadingScope alt(&bytecode_reader.reader(),
|
||||
bytecode_component.GetLibraryIndexOffset());
|
||||
|
||||
bytecode_reader.FindModifiedLibraries(modified_libs,
|
||||
bytecode_component.GetNumLibraries());
|
||||
|
||||
if (p_num_libraries != nullptr) {
|
||||
*p_num_libraries = bytecode_component.GetNumLibraries();
|
||||
}
|
||||
if (p_num_classes != nullptr) {
|
||||
*p_num_classes = bytecode_component.GetNumClasses();
|
||||
}
|
||||
if (p_num_procedures != nullptr) {
|
||||
*p_num_procedures = bytecode_component.GetNumCodes();
|
||||
}
|
||||
}
|
||||
|
||||
BytecodeReaderHelper::BytecodeReaderHelper(Thread* thread,
|
||||
const TypedDataBase& typed_data)
|
||||
: reader_(typed_data),
|
||||
@@ -755,8 +792,6 @@ void BytecodeReaderHelper::ReadLocalVariables(const Bytecode& bytecode,
|
||||
}
|
||||
|
||||
ArrayPtr BytecodeReaderHelper::ReadBytecodeComponent() {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
|
||||
AlternativeReadingScope alt(&reader_, 0);
|
||||
|
||||
const intptr_t start_offset = reader_.offset();
|
||||
@@ -1779,7 +1814,7 @@ void BytecodeReaderHelper::ReadFieldDeclarations(const Class& cls,
|
||||
/* is_reflectable = */ false,
|
||||
/* is_late = */ false, cls, Object::dynamic_type(),
|
||||
TokenPosition::kNoSource, TokenPosition::kNoSource);
|
||||
|
||||
IG->RegisterStaticField(field, Object::null_object());
|
||||
fields.SetAt(num_fields, field);
|
||||
}
|
||||
|
||||
@@ -2284,17 +2319,35 @@ void BytecodeReaderHelper::ReadLibraryDeclarations(intptr_t num_libraries) {
|
||||
members = cls.fields();
|
||||
for (intptr_t j = 0, m = members.Length(); j < m; ++j) {
|
||||
field ^= members.At(j);
|
||||
if ((field.is_static() || field.is_late()) &&
|
||||
field.has_nontrivial_initializer()) {
|
||||
function = field.EnsureInitializerFunction();
|
||||
if (!function.HasBytecode()) {
|
||||
ReadCode(function, thread_->bytecode_loader()->GetOffset(field));
|
||||
if (field.has_nontrivial_initializer()) {
|
||||
if (field.is_static() || field.is_late() ||
|
||||
thread_->bytecode_loader()->HasOffset(field)) {
|
||||
function = field.EnsureInitializerFunction();
|
||||
if (!function.HasBytecode()) {
|
||||
ReadCode(function, thread_->bytecode_loader()->GetOffset(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeReaderHelper::FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t num_libraries) {
|
||||
auto& uri = String::Handle(Z);
|
||||
auto& lib = Library::Handle(Z);
|
||||
for (intptr_t i = 0; i < num_libraries; ++i) {
|
||||
uri ^= ReadObject();
|
||||
reader_.ReadUInt(); // Skip offset.
|
||||
|
||||
lib = Library::LookupLibrary(thread_, uri);
|
||||
if (!lib.IsNull() && !lib.is_dart_scheme()) {
|
||||
// This is a library that already exists so mark it as being modified.
|
||||
modified_libs->Add(lib.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeReaderHelper::ReadParameterCovariance(
|
||||
const Function& function,
|
||||
intptr_t code_offset,
|
||||
|
||||
@@ -31,7 +31,13 @@ class BytecodeLoader {
|
||||
}
|
||||
|
||||
void SetOffset(const Object& obj, intptr_t offset);
|
||||
intptr_t GetOffset(const Object& obj);
|
||||
intptr_t GetOffset(const Object& obj) const;
|
||||
bool HasOffset(const Object& obj) const;
|
||||
|
||||
void FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures);
|
||||
|
||||
private:
|
||||
Thread* thread_;
|
||||
@@ -204,6 +210,7 @@ class BytecodeReaderHelper : public ValueObject {
|
||||
void ReadLibraryDeclaration(const Library& library,
|
||||
const GrowableObjectArray& pending_classes);
|
||||
void ReadLibraryDeclarations(intptr_t num_libraries);
|
||||
void FindModifiedLibraries(BitVector* modified_libs, intptr_t num_libraries);
|
||||
|
||||
LibraryPtr ReadMain();
|
||||
|
||||
|
||||
+202
-54
@@ -5,8 +5,10 @@
|
||||
#include "vm/isolate_reload.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "vm/bit_vector.h"
|
||||
#include "vm/bytecode_reader.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/dart_api_impl.h"
|
||||
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
@@ -688,6 +690,97 @@ static ObjectPtr RejectCompilation(Thread* thread) {
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
class DeltaProgram {
|
||||
public:
|
||||
DeltaProgram() {}
|
||||
virtual ~DeltaProgram() {}
|
||||
|
||||
static std::unique_ptr<DeltaProgram> ReadFromTypedData(
|
||||
const ExternalTypedData& typed_data);
|
||||
|
||||
virtual void FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) = 0;
|
||||
|
||||
virtual ObjectPtr Load() = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(DeltaProgram);
|
||||
};
|
||||
|
||||
class KernelDeltaProgram : public DeltaProgram {
|
||||
public:
|
||||
explicit KernelDeltaProgram(std::unique_ptr<kernel::Program> kernel_program)
|
||||
: kernel_program_(std::move(kernel_program)) {
|
||||
ASSERT(kernel_program_ != nullptr);
|
||||
}
|
||||
|
||||
void FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) override {
|
||||
kernel::KernelLoader::FindModifiedLibraries(
|
||||
kernel_program_.get(), modified_libs, p_num_libraries, p_num_classes,
|
||||
p_num_procedures);
|
||||
}
|
||||
|
||||
ObjectPtr Load() override {
|
||||
return kernel::KernelLoader::LoadEntireProgram(kernel_program_.get()).ptr();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<kernel::Program> kernel_program_;
|
||||
};
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
class BytecodeDeltaProgram : public DeltaProgram {
|
||||
public:
|
||||
explicit BytecodeDeltaProgram(const ExternalTypedData& typed_data)
|
||||
: loader_(Thread::Current(), typed_data) {}
|
||||
|
||||
void FindModifiedLibraries(BitVector* modified_libs,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) override {
|
||||
loader_.FindModifiedLibraries(modified_libs, p_num_libraries, p_num_classes,
|
||||
p_num_procedures);
|
||||
}
|
||||
|
||||
ObjectPtr Load() override {
|
||||
Thread* thread = Thread::Current();
|
||||
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
const auto& function = Function::Handle(loader_.LoadBytecode());
|
||||
if (!function.IsNull()) {
|
||||
return Class::Handle(function.Owner()).library();
|
||||
}
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
private:
|
||||
bytecode::BytecodeLoader loader_;
|
||||
};
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
std::unique_ptr<DeltaProgram> DeltaProgram::ReadFromTypedData(
|
||||
const ExternalTypedData& typed_data) {
|
||||
if (Dart_IsKernel(reinterpret_cast<const uint8_t*>(typed_data.DataAddr(0)),
|
||||
typed_data.LengthInBytes())) {
|
||||
auto kernel_program = kernel::Program::ReadFromTypedData(typed_data);
|
||||
if (!kernel_program) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<KernelDeltaProgram>(std::move(kernel_program));
|
||||
}
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (Dart_IsBytecode(reinterpret_cast<const uint8_t*>(typed_data.DataAddr(0)),
|
||||
typed_data.LengthInBytes())) {
|
||||
return std::make_unique<BytecodeDeltaProgram>(typed_data);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If [root_script_url] is null, attempt to load from [kernel_buffer].
|
||||
bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
const char* root_script_url,
|
||||
@@ -706,7 +799,7 @@ bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
// Grab root library before calling CheckpointBeforeReload.
|
||||
GetRootLibUrl(root_script_url);
|
||||
|
||||
std::unique_ptr<kernel::Program> kernel_program;
|
||||
std::unique_ptr<DeltaProgram> delta_program;
|
||||
|
||||
// Reset stats.
|
||||
num_received_libs_ = 0;
|
||||
@@ -718,20 +811,24 @@ bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
bool skip_reload = false;
|
||||
{
|
||||
// Load the kernel program and figure out the modified libraries.
|
||||
intptr_t* p_num_received_classes = nullptr;
|
||||
intptr_t* p_num_received_procedures = nullptr;
|
||||
auto& program_binary = ExternalTypedData::Handle(Z);
|
||||
bool collect_stats = false;
|
||||
|
||||
// ReadKernelFromFile checks to see if the file at
|
||||
// root_script_url is a valid .dill file. If that's the case, a Program*
|
||||
// is returned. Otherwise, this is likely a source file that needs to be
|
||||
// compiled, so ReadKernelFromFile returns nullptr.
|
||||
kernel_program = kernel::Program::ReadFromFile(root_script_url);
|
||||
if (kernel_program != nullptr) {
|
||||
num_received_libs_ = kernel_program->library_count();
|
||||
bytes_received_libs_ = kernel_program->binary().LengthInBytes();
|
||||
p_num_received_classes = &num_received_classes_;
|
||||
p_num_received_procedures = &num_received_procedures_;
|
||||
} else {
|
||||
// Check if root_script_url is a valid program binary file.
|
||||
// Otherwise treat it as a source file that needs to be compiled.
|
||||
if (root_script_url != nullptr) {
|
||||
ASSERT((kernel_buffer == nullptr) && (kernel_buffer_size == 0));
|
||||
program_binary = ReadFile(root_script_url);
|
||||
if (!program_binary.IsNull()) {
|
||||
delta_program = DeltaProgram::ReadFromTypedData(program_binary);
|
||||
if (delta_program != nullptr) {
|
||||
// Collect statistics only when loading a binary from script URI.
|
||||
bytes_received_libs_ = program_binary.LengthInBytes();
|
||||
collect_stats = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (delta_program == nullptr) {
|
||||
if (kernel_buffer == nullptr || kernel_buffer_size == 0) {
|
||||
char* error = CompileToKernel(force_reload, packages_url,
|
||||
&kernel_buffer, &kernel_buffer_size);
|
||||
@@ -749,22 +846,27 @@ bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto& typed_data = ExternalTypedData::Handle(
|
||||
Z, ExternalTypedData::NewFinalizeWithFree(
|
||||
const_cast<uint8_t*>(kernel_buffer), kernel_buffer_size));
|
||||
kernel_program = kernel::Program::ReadFromTypedData(typed_data);
|
||||
program_binary = ExternalTypedData::NewFinalizeWithFree(
|
||||
const_cast<uint8_t*>(kernel_buffer), kernel_buffer_size);
|
||||
delta_program = DeltaProgram::ReadFromTypedData(program_binary);
|
||||
RELEASE_ASSERT(delta_program != nullptr);
|
||||
}
|
||||
|
||||
NoActiveIsolateScope no_active_isolate_scope(thread);
|
||||
|
||||
IsolateGroupSource* source = IsolateGroup::Current()->source();
|
||||
source->add_loaded_blob(Z,
|
||||
ExternalTypedData::Cast(kernel_program->binary()));
|
||||
source->add_loaded_blob(Z, program_binary);
|
||||
|
||||
modified_libs_ = new (Z) BitVector(Z, num_old_libs_);
|
||||
kernel::KernelLoader::FindModifiedLibraries(
|
||||
kernel_program.get(), IG, modified_libs_, force_reload, &skip_reload,
|
||||
p_num_received_classes, p_num_received_procedures);
|
||||
if (force_reload) {
|
||||
MarkAllLibrariesAsModified(modified_libs_);
|
||||
} else {
|
||||
delta_program->FindModifiedLibraries(
|
||||
modified_libs_, &num_received_libs_,
|
||||
collect_stats ? &num_received_classes_ : nullptr,
|
||||
collect_stats ? &num_received_procedures_ : nullptr);
|
||||
skip_reload = (num_received_libs_ == 0);
|
||||
}
|
||||
modified_libs_transitive_ = new (Z) BitVector(Z, num_old_libs_);
|
||||
BuildModifiedLibrariesClosure(modified_libs_);
|
||||
|
||||
@@ -856,14 +958,14 @@ bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
heap->CollectAllGarbage(GCReason::kDebugging, /*compact=*/true);
|
||||
}
|
||||
|
||||
// We synchronously load the hot-reload kernel diff (which includes changed
|
||||
// We synchronously load the delta program (which includes changed
|
||||
// libraries and any libraries transitively depending on them).
|
||||
//
|
||||
// If loading the hot-reload diff succeeded we'll finalize the loading, which
|
||||
// If loading the delta program succeeded we'll finalize the loading, which
|
||||
// will either commit or reject the reload request.
|
||||
const auto& result =
|
||||
Object::Handle(Z, IG->program_reload_context()->ReloadPhase2LoadKernel(
|
||||
kernel_program.get(), root_lib_url_));
|
||||
const auto& result = Object::Handle(
|
||||
Z, IG->program_reload_context()->ReloadPhase2LoadDeltaProgram(
|
||||
std::move(delta_program), root_lib_url_));
|
||||
|
||||
if (result.IsError()) {
|
||||
TIR_Print("---- LOAD FAILED, ABORTING RELOAD\n");
|
||||
@@ -1034,6 +1136,20 @@ bool IsolateGroupReloadContext::Reload(bool force_reload,
|
||||
return success;
|
||||
}
|
||||
|
||||
// If a reload is being forced we mark all libraries as having been modified.
|
||||
void IsolateGroupReloadContext::MarkAllLibrariesAsModified(
|
||||
BitVector* modified_libs) {
|
||||
const auto& libs =
|
||||
GrowableObjectArray::Handle(Z, IG->object_store()->libraries());
|
||||
auto& lib = Library::Handle(Z);
|
||||
for (intptr_t i = 0, n = libs.Length(); i < n; ++i) {
|
||||
lib ^= libs.At(i);
|
||||
if (!lib.is_dart_scheme()) {
|
||||
modified_libs->Add(lib.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copied in from https://dart-review.googlesource.com/c/sdk/+/77722.
|
||||
static void PropagateLibraryModified(
|
||||
const ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>* imported_by,
|
||||
@@ -1161,6 +1277,20 @@ void IsolateGroupReloadContext::GetRootLibUrl(const char* root_script_url) {
|
||||
}
|
||||
}
|
||||
|
||||
ExternalTypedDataPtr IsolateGroupReloadContext::ReadFile(
|
||||
const char* script_uri) {
|
||||
if (!IG->HasTagHandler()) {
|
||||
return ExternalTypedData::null();
|
||||
}
|
||||
const String& uri = String::Handle(Z, String::New(script_uri));
|
||||
const Object& ret = Object::Handle(
|
||||
Z, IG->CallTagHandler(Dart_kKernelTag, Object::null_object(), uri));
|
||||
if (ret.IsExternalTypedData()) {
|
||||
return ExternalTypedData::Cast(ret).ptr();
|
||||
}
|
||||
return ExternalTypedData::null();
|
||||
}
|
||||
|
||||
char* IsolateGroupReloadContext::CompileToKernel(bool force_reload,
|
||||
const char* packages_url,
|
||||
const uint8_t** kernel_buffer,
|
||||
@@ -1212,27 +1342,25 @@ void ProgramReloadContext::ReloadPhase1AllocateStorageMapsAndCheckpoint() {
|
||||
}
|
||||
}
|
||||
|
||||
ObjectPtr ProgramReloadContext::ReloadPhase2LoadKernel(
|
||||
kernel::Program* program,
|
||||
ObjectPtr ProgramReloadContext::ReloadPhase2LoadDeltaProgram(
|
||||
std::unique_ptr<DeltaProgram> program,
|
||||
const String& root_lib_url) {
|
||||
Thread* thread = Thread::Current();
|
||||
|
||||
HANDLESCOPE(thread);
|
||||
LongJumpScope jump(thread);
|
||||
if (DART_SETJMP(*jump.Set()) == 0) {
|
||||
const Object& tmp = kernel::KernelLoader::LoadEntireProgram(program);
|
||||
if (tmp.IsError()) {
|
||||
return tmp.ptr();
|
||||
Object& result = Object::Handle(Z, program->Load());
|
||||
if (result.IsError()) {
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
// If main method disappeared or were not there to begin with then
|
||||
// KernelLoader will return null. In this case lookup library by
|
||||
// URL.
|
||||
auto& lib = Library::Handle(Library::RawCast(tmp.ptr()));
|
||||
if (lib.IsNull()) {
|
||||
lib = Library::LookupLibrary(thread, root_lib_url);
|
||||
// If main method disappeared or were not there to begin with,
|
||||
// then lookup root library by URL.
|
||||
if (result.IsNull()) {
|
||||
result = Library::LookupLibrary(thread, root_lib_url);
|
||||
}
|
||||
IG->object_store()->set_root_library(lib);
|
||||
IG->object_store()->set_root_library(Library::Cast(result));
|
||||
return Object::null();
|
||||
} else {
|
||||
return thread->StealStickyError();
|
||||
@@ -1355,7 +1483,7 @@ ErrorPtr ProgramReloadContext::EnsuredUnoptimizedCodeForStack() {
|
||||
Function& func = Function::Handle();
|
||||
while (it.HasNextFrame()) {
|
||||
StackFrame* frame = it.NextFrame();
|
||||
if (frame->IsDartFrame()) {
|
||||
if (frame->IsDartFrame() && !frame->is_interpreted()) {
|
||||
func = frame->LookupDartFunction();
|
||||
ASSERT(!func.IsNull());
|
||||
// Force-optimized functions don't need unoptimized code because their
|
||||
@@ -1972,19 +2100,21 @@ void ProgramReloadContext::ResetUnoptimizedICsOnStack() {
|
||||
StackFrameIterator::kAllowCrossThreadIteration);
|
||||
StackFrame* frame = iterator.NextFrame();
|
||||
while (frame != nullptr) {
|
||||
code = frame->LookupDartCode();
|
||||
if (code.is_optimized() && !code.is_force_optimized()) {
|
||||
// If this code is optimized, we need to reset the ICs in the
|
||||
// corresponding unoptimized code, which will be executed when the stack
|
||||
// unwinds to the optimized code.
|
||||
function = code.function();
|
||||
code = function.unoptimized_code();
|
||||
ASSERT(!code.IsNull());
|
||||
resetter.ResetSwitchableCalls(code);
|
||||
resetter.ResetCaches(code);
|
||||
} else {
|
||||
resetter.ResetSwitchableCalls(code);
|
||||
resetter.ResetCaches(code);
|
||||
if (!frame->is_interpreted()) {
|
||||
code = frame->LookupDartCode();
|
||||
if (code.is_optimized() && !code.is_force_optimized()) {
|
||||
// If this code is optimized, we need to reset the ICs in the
|
||||
// corresponding unoptimized code, which will be executed
|
||||
// when the stack unwinds to the optimized code.
|
||||
function = code.function();
|
||||
code = function.unoptimized_code();
|
||||
ASSERT(!code.IsNull());
|
||||
resetter.ResetSwitchableCalls(code);
|
||||
resetter.ResetCaches(code);
|
||||
} else {
|
||||
resetter.ResetSwitchableCalls(code);
|
||||
resetter.ResetCaches(code);
|
||||
}
|
||||
}
|
||||
frame = iterator.NextFrame();
|
||||
}
|
||||
@@ -2052,6 +2182,13 @@ ErrorPtr ProgramReloadContext::RunInvalidationVisitors() {
|
||||
StackZone stack_zone(thread);
|
||||
Zone* zone = stack_zone.GetZone();
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Interpreter* interpreter = thread->interpreter();
|
||||
if (interpreter != nullptr) {
|
||||
interpreter->ClearLookupCache();
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
GrowableArray<const Function*> functions(4 * KB);
|
||||
GrowableArray<const KernelProgramInfo*> kernel_infos(KB);
|
||||
GrowableArray<const Field*> fields(4 * KB);
|
||||
@@ -2126,6 +2263,10 @@ void ProgramReloadContext::InvalidateFunctions(
|
||||
Library& owning_lib = Library::Handle(zone);
|
||||
Code& code = Code::Handle(zone);
|
||||
Field& field = Field::Handle(zone);
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Bytecode& bytecode = Bytecode::Handle(zone);
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
for (intptr_t i = 0; i < functions.length(); i++) {
|
||||
const Function& func = *functions[i];
|
||||
@@ -2160,6 +2301,13 @@ void ProgramReloadContext::InvalidateFunctions(
|
||||
// they're held.
|
||||
resetter.ZeroEdgeCounters(func);
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (func.HasBytecode()) {
|
||||
bytecode = func.GetBytecode();
|
||||
resetter.RebindBytecode(bytecode);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
if (stub_code) {
|
||||
// Nothing to reset.
|
||||
} else if (clear_unoptimized_code) {
|
||||
|
||||
@@ -45,6 +45,7 @@ DECLARE_FLAG(bool, trace_reload_verbose);
|
||||
namespace dart {
|
||||
|
||||
class BitVector;
|
||||
class DeltaProgram;
|
||||
class GrowableObjectArray;
|
||||
class Isolate;
|
||||
class Library;
|
||||
@@ -202,10 +203,12 @@ class IsolateGroupReloadContext {
|
||||
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
||||
|
||||
void GetRootLibUrl(const char* root_script_url);
|
||||
ExternalTypedDataPtr ReadFile(const char* script_uri);
|
||||
char* CompileToKernel(bool force_reload,
|
||||
const char* packages_url,
|
||||
const uint8_t** kernel_buffer,
|
||||
intptr_t* kernel_buffer_size);
|
||||
void MarkAllLibrariesAsModified(BitVector* modified_libs);
|
||||
void BuildModifiedLibrariesClosure(BitVector* modified_libs);
|
||||
void FindModifiedSources(bool force_reload,
|
||||
Dart_SourceFile** modified_sources,
|
||||
@@ -324,8 +327,8 @@ class ProgramReloadContext {
|
||||
|
||||
void ReloadPhase1AllocateStorageMapsAndCheckpoint();
|
||||
void CheckpointClasses();
|
||||
ObjectPtr ReloadPhase2LoadKernel(kernel::Program* program,
|
||||
const String& root_lib_url);
|
||||
ObjectPtr ReloadPhase2LoadDeltaProgram(std::unique_ptr<DeltaProgram> program,
|
||||
const String& root_lib_url);
|
||||
void ReloadPhase3FinalizeLoading();
|
||||
void ReloadPhase4CommitPrepare();
|
||||
ErrorPtr ReloadPhase4CommitFinish();
|
||||
@@ -414,6 +417,7 @@ class CallSiteResetter : public ValueObject {
|
||||
void ResetCaches(const ObjectPool& pool);
|
||||
void Reset(const ICData& ic);
|
||||
void ResetSwitchableCalls(const Code& code);
|
||||
void RebindBytecode(const Bytecode& bytecode);
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
|
||||
@@ -32,8 +32,9 @@ int64_t SimpleInvoke(Dart_Handle lib, const char* method) {
|
||||
|
||||
const char* SimpleInvokeStr(Dart_Handle lib, const char* method) {
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, nullptr);
|
||||
const char* result_str = nullptr;
|
||||
EXPECT_VALID(result);
|
||||
EXPECT(Dart_IsString(result));
|
||||
const char* result_str = nullptr;
|
||||
EXPECT_VALID(Dart_StringToCString(result, &result_str));
|
||||
return result_str;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,6 @@ class Program {
|
||||
static std::unique_ptr<Program> ReadFrom(Reader* reader,
|
||||
const char** error = nullptr);
|
||||
|
||||
static std::unique_ptr<Program> ReadFromFile(const char* script_uri,
|
||||
const char** error = nullptr);
|
||||
static std::unique_ptr<Program> ReadFromBuffer(const uint8_t* buffer,
|
||||
intptr_t buffer_length,
|
||||
const char** error = nullptr);
|
||||
|
||||
@@ -173,37 +173,6 @@ std::unique_ptr<Program> Program::ReadFrom(Reader* reader, const char** error) {
|
||||
return program;
|
||||
}
|
||||
|
||||
std::unique_ptr<Program> Program::ReadFromFile(
|
||||
const char* script_uri,
|
||||
const char** error /* = nullptr */) {
|
||||
Thread* thread = Thread::Current();
|
||||
auto isolate_group = thread->isolate_group();
|
||||
if (script_uri == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isolate_group->HasTagHandler()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<kernel::Program> kernel_program;
|
||||
|
||||
const String& uri = String::Handle(String::New(script_uri));
|
||||
const Object& ret = Object::Handle(isolate_group->CallTagHandler(
|
||||
Dart_kKernelTag, Object::null_object(), uri));
|
||||
if (ret.IsExternalTypedData()) {
|
||||
const auto& typed_data = ExternalTypedData::Cast(ret);
|
||||
kernel_program = kernel::Program::ReadFromTypedData(typed_data);
|
||||
return kernel_program;
|
||||
} else if (error != nullptr) {
|
||||
Api::Scope api_scope(thread);
|
||||
Dart_Handle retval = Api::NewHandle(thread, ret.ptr());
|
||||
{
|
||||
TransitionVMToNative transition(thread);
|
||||
*error = Dart_GetError(retval);
|
||||
}
|
||||
}
|
||||
return kernel_program;
|
||||
}
|
||||
|
||||
std::unique_ptr<Program> Program::ReadFromBuffer(const uint8_t* buffer,
|
||||
intptr_t buffer_length,
|
||||
const char** error) {
|
||||
|
||||
+12
-25
@@ -626,32 +626,18 @@ ObjectPtr KernelLoader::LoadExpressionEvaluationFunction(
|
||||
}
|
||||
|
||||
void KernelLoader::FindModifiedLibraries(Program* program,
|
||||
IsolateGroup* isolate_group,
|
||||
BitVector* modified_libs,
|
||||
bool force_reload,
|
||||
bool* is_empty_program,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) {
|
||||
Thread* thread = Thread::Current();
|
||||
LongJumpScope jump(thread);
|
||||
if (DART_SETJMP(*jump.Set()) == 0) {
|
||||
Zone* zone = thread->zone();
|
||||
if (force_reload) {
|
||||
// If a reload is being forced we mark all libraries as having
|
||||
// been modified.
|
||||
const auto& libs = GrowableObjectArray::Handle(
|
||||
zone, isolate_group->object_store()->libraries());
|
||||
intptr_t num_libs = libs.Length();
|
||||
Library& lib = dart::Library::Handle(zone);
|
||||
for (intptr_t i = 0; i < num_libs; i++) {
|
||||
lib ^= libs.At(i);
|
||||
if (!lib.is_dart_scheme()) {
|
||||
modified_libs->Add(lib.index());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_num_libraries != nullptr) {
|
||||
*p_num_libraries = 0;
|
||||
}
|
||||
if (p_num_classes != nullptr) {
|
||||
*p_num_classes = 0;
|
||||
}
|
||||
@@ -661,10 +647,9 @@ void KernelLoader::FindModifiedLibraries(Program* program,
|
||||
|
||||
// Now go through all the libraries that are present in the incremental
|
||||
// kernel files, these will constitute the modified libraries.
|
||||
*is_empty_program = true;
|
||||
if (program->is_single_program()) {
|
||||
KernelLoader loader(program, /*uri_to_source_table=*/nullptr);
|
||||
loader.walk_incremental_kernel(modified_libs, is_empty_program,
|
||||
loader.walk_incremental_kernel(modified_libs, p_num_libraries,
|
||||
p_num_classes, p_num_procedures);
|
||||
}
|
||||
|
||||
@@ -689,24 +674,23 @@ void KernelLoader::FindModifiedLibraries(Program* program,
|
||||
}
|
||||
ASSERT(subprogram->is_single_program());
|
||||
KernelLoader loader(subprogram.get(), /*uri_to_source_table=*/nullptr);
|
||||
loader.walk_incremental_kernel(modified_libs, is_empty_program,
|
||||
loader.walk_incremental_kernel(modified_libs, p_num_libraries,
|
||||
p_num_classes, p_num_procedures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KernelLoader::walk_incremental_kernel(BitVector* modified_libs,
|
||||
bool* is_empty_program,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures) {
|
||||
intptr_t length = program_->library_count();
|
||||
*is_empty_program = *is_empty_program && (length == 0);
|
||||
const intptr_t num_libraries = program_->library_count();
|
||||
bool collect_library_stats =
|
||||
p_num_classes != nullptr || p_num_procedures != nullptr;
|
||||
intptr_t num_classes = 0;
|
||||
intptr_t num_procedures = 0;
|
||||
Library& lib = Library::Handle(Z);
|
||||
for (intptr_t i = 0; i < length; i++) {
|
||||
for (intptr_t i = 0; i < num_libraries; i++) {
|
||||
intptr_t kernel_offset = library_offset(i);
|
||||
helper_.SetOffset(kernel_offset);
|
||||
LibraryHelper library_helper(&helper_);
|
||||
@@ -725,6 +709,9 @@ void KernelLoader::walk_incremental_kernel(BitVector* modified_libs,
|
||||
num_procedures += library_index.procedure_count();
|
||||
}
|
||||
}
|
||||
if (p_num_libraries != nullptr) {
|
||||
*p_num_libraries += num_libraries;
|
||||
}
|
||||
if (p_num_classes != nullptr) {
|
||||
*p_num_classes += num_classes;
|
||||
}
|
||||
|
||||
@@ -192,14 +192,11 @@ class KernelLoader : public ValueObject {
|
||||
// Finds all libraries that have been modified in this incremental
|
||||
// version of the kernel program file.
|
||||
//
|
||||
// When [force_reload] is false and if [p_num_classes], [p_num_procedures] are
|
||||
// not nullptr, then they are populated with number of classes and top-level
|
||||
// procedures in [program].
|
||||
// Optionally populate [p_num_libraries], [p_num_classes], [p_num_procedures]
|
||||
// with number of libraries, classes and top-level procedures in [program].
|
||||
static void FindModifiedLibraries(Program* program,
|
||||
IsolateGroup* isolate_group,
|
||||
BitVector* modified_libs,
|
||||
bool force_reload,
|
||||
bool* is_empty_program,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures);
|
||||
|
||||
@@ -293,7 +290,7 @@ class KernelLoader : public ValueObject {
|
||||
uint8_t CharacterAt(StringIndex string_index, intptr_t index);
|
||||
|
||||
void walk_incremental_kernel(BitVector* modified_libs,
|
||||
bool* is_empty_program,
|
||||
intptr_t* p_num_libraries,
|
||||
intptr_t* p_num_classes,
|
||||
intptr_t* p_num_procedures);
|
||||
|
||||
|
||||
@@ -13134,6 +13134,18 @@ ObjectPtr Field::EvaluateInitializer() const {
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_static() && is_const()) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (is_declared_in_bytecode()) {
|
||||
const auto& initializer = Function::Handle(InitializerFunction());
|
||||
ASSERT(!initializer.IsNull());
|
||||
const auto& bytecode = Bytecode::Handle(initializer.GetBytecode());
|
||||
ASSERT(!bytecode.IsNull());
|
||||
const auto& pool = ObjectPool::Handle(bytecode.object_pool());
|
||||
ASSERT(!pool.IsNull());
|
||||
ASSERT(pool.Length() == 1);
|
||||
return pool.ObjectAt(0);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
return kernel::EvaluateStaticConstFieldInitializer(*this);
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
@@ -840,6 +840,71 @@ void CallSiteResetter::Reset(const ICData& ic) {
|
||||
}
|
||||
}
|
||||
|
||||
void CallSiteResetter::RebindBytecode(const Bytecode& bytecode) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
pool_ = bytecode.object_pool();
|
||||
ASSERT(!pool_.IsNull());
|
||||
|
||||
// Iterate over bytecode instructions and update
|
||||
// references to static methods and fields.
|
||||
const KBCInstr* instr =
|
||||
reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart());
|
||||
const KBCInstr* end = reinterpret_cast<const KBCInstr*>(
|
||||
bytecode.PayloadStart() + bytecode.Size());
|
||||
while (instr < end) {
|
||||
switch (KernelBytecode::DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kDirectCall:
|
||||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kUncheckedDirectCall:
|
||||
case KernelBytecode::kUncheckedDirectCall_Wide: {
|
||||
const intptr_t idx = KernelBytecode::DecodeD(instr);
|
||||
old_target_ ^= pool_.ObjectAt(idx);
|
||||
args_desc_array_ ^= pool_.ObjectAt(idx + 1);
|
||||
ArgumentsDescriptor args_desc(args_desc_array_);
|
||||
name_ = old_target_.name();
|
||||
new_cls_ = old_target_.Owner();
|
||||
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
|
||||
if (new_target_.ptr() != old_target_.ptr()) {
|
||||
if (!new_target_.IsNull() &&
|
||||
(new_target_.is_static() == old_target_.is_static()) &&
|
||||
(new_target_.kind() == old_target_.kind()) &&
|
||||
new_target_.AreValidArguments(args_desc, nullptr)) {
|
||||
pool_.SetObjectAt(idx, new_target_);
|
||||
} else {
|
||||
VTIR_Print("Cannot rebind function %s\n",
|
||||
old_target_.ToFullyQualifiedCString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KernelBytecode::kLoadStatic:
|
||||
case KernelBytecode::kLoadStatic_Wide:
|
||||
case KernelBytecode::kStoreStaticTOS:
|
||||
case KernelBytecode::kStoreStaticTOS_Wide: {
|
||||
const intptr_t idx = KernelBytecode::DecodeD(instr);
|
||||
object_ = pool_.ObjectAt(idx);
|
||||
const Field& old_field = Field::Cast(object_);
|
||||
name_ = old_field.name();
|
||||
new_cls_ = old_field.Owner();
|
||||
new_field_ = new_cls_.LookupField(name_);
|
||||
if (!new_field_.IsNull() &&
|
||||
(new_field_.is_static() == old_field.is_static())) {
|
||||
pool_.SetObjectAt(idx, new_field_);
|
||||
} else {
|
||||
VTIR_Print("Cannot rebind field %s\n", old_field.ToCString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
instr = KernelBytecode::Next(instr);
|
||||
}
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
} // namespace dart
|
||||
|
||||
Reference in New Issue
Block a user