VM: [Kernel] Split kernel API into 3 steps: ([read binary], parse-binary, bootstrap, load program)

This avoids reading and parsing the kernel binary multiple times and avois
having it multiple times in memory (we don't free the 'kernel::Program*' object
ATM).

R=kmillikin@google.com

Review URL: https://codereview.chromium.org/2525623002 .
This commit is contained in:
Martin Kustermann 2016-11-23 09:26:15 +01:00
parent 656be7a7aa
commit 1aff626804
14 changed files with 131 additions and 144 deletions

View file

@ -1296,10 +1296,17 @@ int main(int argc, char** argv) {
intptr_t kernel_length = 0;
const bool is_kernel_file =
TryReadKernel(app_script_name, &kernel, &kernel_length);
void* kernel_program = NULL;
if (is_kernel_file) {
kernel_program = Dart_ReadKernelBinary(kernel, kernel_length);
free(const_cast<uint8_t*>(kernel));
}
Dart_Isolate isolate =
is_kernel_file
? Dart_CreateIsolateFromKernel(NULL, NULL, kernel, kernel_length,
NULL, isolate_data, &error)
? Dart_CreateIsolateFromKernel(NULL, NULL, kernel_program, NULL,
isolate_data, &error)
: Dart_CreateIsolate(NULL, NULL, NULL, NULL, isolate_data, &error);
if (isolate == NULL) {
Log::PrintErr("%s", error);
@ -1319,8 +1326,7 @@ int main(int argc, char** argv) {
ParseEntryPointsManifestIfPresent();
if (is_kernel_file) {
Dart_Handle library = Dart_LoadKernel(kernel, kernel_length);
free(const_cast<uint8_t*>(kernel));
Dart_Handle library = Dart_LoadKernel(kernel_program);
if (Dart_IsError(library)) FATAL("Failed to load app from Kernel IR");
} else {
// Set up the library tag handler in such a manner that it will use the

View file

@ -370,8 +370,6 @@ bool Loader::ProcessResultLocked(Loader* loader, Loader::IOResult* result) {
if (payload_type == DartUtils::kSnapshotMagicNumber) {
dart_result = Dart_LoadScriptFromSnapshot(payload, payload_length);
reload_extensions = true;
} else if (payload_type == DartUtils::kKernelMagicNumber) {
dart_result = Dart_LoadKernel(payload, payload_length);
} else {
dart_result = Dart_LoadScript(uri, resolved_uri, source, 0, 0);
}

View file

@ -806,18 +806,19 @@ static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri,
!run_app_snapshot &&
TryReadKernel(script_uri, &kernel_file, &kernel_length);
IsolateData* isolate_data =
new IsolateData(script_uri, package_root, packages_config);
Dart_Isolate isolate =
is_kernel ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_file,
kernel_length, flags,
isolate_data, error)
: Dart_CreateIsolate(script_uri, main, isolate_snapshot_buffer,
flags, isolate_data, error);
void* kernel_program = NULL;
if (is_kernel) {
kernel_program = Dart_ReadKernelBinary(kernel_file, kernel_length);
free(const_cast<uint8_t*>(kernel_file));
}
IsolateData* isolate_data =
new IsolateData(script_uri, package_root, packages_config);
Dart_Isolate isolate =
is_kernel ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_program,
flags, isolate_data, error)
: Dart_CreateIsolate(script_uri, main, isolate_snapshot_buffer,
flags, isolate_data, error);
if (isolate == NULL) {
delete isolate_data;
return NULL;
@ -830,12 +831,7 @@ static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri,
CHECK_RESULT(result);
if (is_kernel) {
// TODO(27590): We should not read the kernel file again!
if (!TryReadKernel(script_uri, &kernel_file, &kernel_length)) {
FATAL("Failed to read kernel second time");
}
Dart_Handle result = Dart_LoadKernel(kernel_file, kernel_length);
free(const_cast<uint8_t*>(kernel_file));
Dart_Handle result = Dart_LoadKernel(kernel_program);
CHECK_RESULT(result);
}
if (is_kernel || (isolate_snapshot_buffer != NULL)) {

View file

@ -890,12 +890,14 @@ DART_EXPORT Dart_Isolate Dart_CreateIsolate(const char* script_uri,
*
* Requires there to be no current isolate.
*
* After this call, the `kernel_program` needs to be supplied to a call to
* `Dart_LoadKernel()` which will then take ownership of the memory.
*
* \param script_uri The name of the script this isolate will load.
* Provided only for advisory purposes to improve debugging messages.
* \param main The name of the main entry point this isolate will run.
* Provided only for advisory purposes to improve debugging messages.
* \param kernel A buffer containing the Dart Kernel binary.
* \param kernel_length The length of the Kernel buffer.
* \param kernel_program The `dart::kernel::Program` object.
* \param flags Pointer to VM specific flags or NULL for default flags.
* \param callback_data Embedder data. This data will be passed to
* the Dart_IsolateCreateCallback when new isolates are spawned from
@ -907,8 +909,7 @@ DART_EXPORT Dart_Isolate Dart_CreateIsolate(const char* script_uri,
*/
DART_EXPORT Dart_Isolate Dart_CreateIsolateFromKernel(const char* script_uri,
const char* main,
const uint8_t* kernel,
intptr_t kernel_length,
void* kernel_program,
Dart_IsolateFlags* flags,
void* callback_data,
char** error);
@ -2863,15 +2864,28 @@ DART_EXPORT Dart_Handle Dart_LoadScriptFromSnapshot(const uint8_t* buffer,
intptr_t buffer_len);
/**
* Loads a dart application which was compiled to a Kernel binary.
* Loads a dart application via an in-memory kernel program.
*
* \param buffer A buffer which contains a Kernel binary.
* \param buffer_len Length of the passed in buffer.
* \param kernel_program The kernel program obtained via
* `Dart_ReadKernelBinary`.
*
* The VM will take ownership of the `kernel_program` object.
*
* \return If no error occurs, the Library object corresponding to the root
* script is returned. Otherwise an error handle is returned.
*/
DART_EXPORT Dart_Handle Dart_LoadKernel(const uint8_t* buffer,
DART_EXPORT Dart_Handle Dart_LoadKernel(void* kernel_program);
/**
* Constructs an in-memory kernel program form a binary.
*
* \param buffer The start of a memory buffer containing the binary format.
* \param buffer_len The length of the memory buffer.
*
* \return kernel_program The `dart::kernel::Program` object.
*/
DART_EXPORT void* Dart_ReadKernelBinary(const uint8_t* buffer,
intptr_t buffer_len);
/**

View file

@ -317,17 +317,9 @@ static RawError* BootstrapFromSource(Thread* thread) {
#if !defined(DART_PRECOMPILED_RUNTIME)
static RawError* BootstrapFromKernel(Thread* thread,
const uint8_t* buffer,
intptr_t buffer_size) {
static RawError* BootstrapFromKernel(Thread* thread, kernel::Program* program) {
Zone* zone = thread->zone();
kernel::KernelReader reader(buffer, buffer_size);
kernel::Program* program = reader.ReadPrecompiledProgram();
if (program == NULL) {
const String& message =
String::Handle(zone, String::New("Failed to read Kernel file"));
return ApiError::New(message);
}
kernel::KernelReader reader(program);
Isolate* isolate = thread->isolate();
// Mark the already-pending classes. This mark bit will be used to avoid
@ -363,17 +355,14 @@ static RawError* BootstrapFromKernel(Thread* thread,
return Error::null();
}
#else
static RawError* BootstrapFromKernel(Thread* thread,
const uint8_t* buffer,
intptr_t buffer_size) {
static RawError* BootstrapFromKernel(Thread* thread, kernel::Program* program) {
UNREACHABLE();
return Error::null();
}
#endif
RawError* Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length) {
RawError* Bootstrap::DoBootstrapping(kernel::Program* kernel_program) {
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
Zone* zone = thread->zone();
@ -396,9 +385,8 @@ RawError* Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
}
}
return (kernel_buffer == NULL)
? BootstrapFromSource(thread)
: BootstrapFromKernel(thread, kernel_buffer, kernel_buffer_length);
return (kernel_program == NULL) ? BootstrapFromSource(thread)
: BootstrapFromKernel(thread, kernel_program);
}
} // namespace dart

View file

@ -12,17 +12,19 @@ namespace dart {
// Forward declarations.
class RawError;
namespace kernel {
class Program;
}
class Bootstrap : public AllStatic {
public:
// Compile the bootstrap libraries, either from sources or a Kernel binary.
// If kernel_buffer is NULL, compile from sources or source paths linked into
// the VM. If it is non-NULL it represents a buffer holding a Kernel binary.
// Compile the bootstrap libraries, either from sources or a Kernel program.
// If program is NULL, compile from sources or source paths linked into
// the VM. If it is non-NULL it represents the Kernel program to use for
// bootstrapping.
// The caller of this function is responsible for managing the kernel
// buffer's memory, and is welcome to deallocate it after this function
// returns.
static RawError* DoBootstrapping(const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length);
// program's memory.
static RawError* DoBootstrapping(kernel::Program* program);
static void SetupNativeResolver();
static bool IsBootstapResolver(Dart_NativeEntryResolver resolver);

View file

@ -55,18 +55,9 @@ void Finish(Thread* thread, bool from_kernel) {
}
RawError* BootstrapFromKernel(Thread* thread,
const uint8_t* buffer,
intptr_t buffer_length) {
RawError* BootstrapFromKernel(Thread* thread, kernel::Program* program) {
Zone* zone = thread->zone();
kernel::KernelReader reader(buffer, buffer_length);
kernel::Program* program = reader.ReadPrecompiledProgram();
if (program == NULL) {
const String& message =
String::Handle(zone, String::New("Failed to read Kernel file"));
return ApiError::New(message);
}
kernel::KernelReader reader(program);
Isolate* isolate = thread->isolate();
// Mark the already-pending classes. This mark bit will be used to avoid
// adding classes to the list more than once.
@ -102,8 +93,7 @@ RawError* BootstrapFromKernel(Thread* thread,
}
RawError* Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length) {
RawError* Bootstrap::DoBootstrapping(kernel::Program* program) {
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
Zone* zone = thread->zone();
@ -126,12 +116,10 @@ RawError* Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
}
}
ASSERT(kernel_buffer != NULL);
return BootstrapFromKernel(thread, kernel_buffer, kernel_buffer_length);
return BootstrapFromKernel(thread, program);
}
#else
RawError* Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length) {
RawError* Bootstrap::DoBootstrapping(kernel::Program* program) {
UNREACHABLE();
return Error::null();
}

View file

@ -493,7 +493,7 @@ Isolate* Dart::CreateIsolate(const char* name_prefix,
RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer,
intptr_t snapshot_length,
bool from_kernel,
kernel::Program* kernel_program,
void* data) {
// Initialize the new isolate.
Thread* T = Thread::Current();
@ -512,17 +512,11 @@ RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer,
}
Error& error = Error::Handle(T->zone());
if (from_kernel) {
ASSERT(snapshot_buffer != NULL);
ASSERT(snapshot_length > 0);
error = Object::Init(I, snapshot_buffer, snapshot_length);
} else {
error = Object::Init(I, NULL, -1);
}
error = Object::Init(I, kernel_program);
if (!error.IsNull()) {
return error.raw();
}
if ((snapshot_buffer != NULL) && !from_kernel) {
if ((snapshot_buffer != NULL) && kernel_program == NULL) {
// Read the snapshot and setup the initial state.
NOT_IN_PRODUCT(TimelineDurationScope tds(T, Timeline::GetIsolateStream(),
"IsolateSnapshotReader"));
@ -563,7 +557,7 @@ RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer,
MegamorphicCacheTable::PrintSizes(I);
}
} else {
if ((snapshot_kind_ != Snapshot::kNone) && !from_kernel) {
if ((snapshot_kind_ != Snapshot::kNone) && kernel_program == NULL) {
const String& message =
String::Handle(String::New("Missing isolate snapshot"));
return ApiError::New(message);
@ -591,7 +585,7 @@ RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer,
Code::Handle(I->object_store()->megamorphic_miss_code());
I->set_ic_miss_code(miss_code);
if ((snapshot_buffer == NULL) || from_kernel) {
if ((snapshot_buffer == NULL) || (kernel_program != NULL)) {
const Error& error = Error::Handle(I->object_store()->PreallocateObjects());
if (!error.IsNull()) {
return error.raw();

View file

@ -18,6 +18,9 @@ class LocalHandle;
class RawError;
class ReadOnlyHandles;
class ThreadPool;
namespace kernel {
class Program;
}
class Dart : public AllStatic {
public:
@ -44,7 +47,7 @@ class Dart : public AllStatic {
// from_kernel. Otherwise, initialize from sources.
static RawError* InitializeIsolate(const uint8_t* snapshot_buffer,
intptr_t snapshot_length,
bool from_kernel,
kernel::Program* kernel_program,
void* data);
static void RunShutdownCallback();
static void ShutdownIsolate(Isolate* isolate);

View file

@ -1240,11 +1240,10 @@ static Dart_Isolate CreateIsolate(const char* script_uri,
const char* main,
const uint8_t* snapshot_buffer,
intptr_t snapshot_length,
bool from_kernel,
kernel::Program* kernel_program,
Dart_IsolateFlags* flags,
void* callback_data,
char** error) {
ASSERT(!from_kernel || (snapshot_buffer != NULL));
CHECK_NO_ISOLATE(Isolate::Current());
char* isolate_name = BuildIsolateName(script_uri, main);
@ -1270,10 +1269,10 @@ static Dart_Isolate CreateIsolate(const char* script_uri,
Dart_EnterScope();
const Error& error_obj = Error::Handle(
Z, Dart::InitializeIsolate(snapshot_buffer, snapshot_length,
from_kernel, callback_data));
kernel_program, callback_data));
if (error_obj.IsNull()) {
#if defined(DART_NO_SNAPSHOT) && !defined(PRODUCT)
if (FLAG_check_function_fingerprints && !from_kernel) {
if (FLAG_check_function_fingerprints && kernel_program == NULL) {
Library::CheckFunctionFingerprints();
}
#endif // defined(DART_NO_SNAPSHOT) && !defined(PRODUCT).
@ -1302,20 +1301,19 @@ DART_EXPORT Dart_Isolate Dart_CreateIsolate(const char* script_uri,
Dart_IsolateFlags* flags,
void* callback_data,
char** error) {
return CreateIsolate(script_uri, main, snapshot_buffer, -1, false, flags,
return CreateIsolate(script_uri, main, snapshot_buffer, -1, NULL, flags,
callback_data, error);
}
DART_EXPORT Dart_Isolate
Dart_CreateIsolateFromKernel(const char* script_uri,
const char* main,
const uint8_t* kernel_file,
intptr_t kernel_length,
Dart_IsolateFlags* flags,
void* callback_data,
char** error) {
return CreateIsolate(script_uri, main, kernel_file, kernel_length, true,
DART_EXPORT Dart_Isolate Dart_CreateIsolateFromKernel(const char* script_uri,
const char* main,
void* kernel_program,
Dart_IsolateFlags* flags,
void* callback_data,
char** error) {
return CreateIsolate(script_uri, main, NULL, -1,
reinterpret_cast<kernel::Program*>(kernel_program),
flags, callback_data, error);
}
@ -5350,9 +5348,23 @@ DART_EXPORT Dart_Handle Dart_LoadScriptFromSnapshot(const uint8_t* buffer,
}
DART_EXPORT Dart_Handle Dart_LoadKernel(const uint8_t* buffer,
DART_EXPORT void* Dart_ReadKernelBinary(const uint8_t* buffer,
intptr_t buffer_len) {
API_TIMELINE_DURATION;
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
return NULL;
#else
kernel::Program* program =
ReadPrecompiledKernelFromBuffer(buffer, buffer_len);
return program;
#endif
}
DART_EXPORT Dart_Handle Dart_LoadKernel(void* kernel_program) {
API_TIMELINE_DURATION;
DARTSCOPE(Thread::Current());
StackZone zone(T);
@ -5371,9 +5383,12 @@ DART_EXPORT Dart_Handle Dart_LoadKernel(const uint8_t* buffer,
CHECK_CALLBACK_STATE(T);
CHECK_COMPILATION_ALLOWED(I);
// TODO(27588): Memory leak!
kernel::KernelReader* reader = new kernel::KernelReader(buffer, buffer_len);
const Object& tmp = reader->ReadProgram();
// NOTE: Now the VM owns the [kernel_program] memory! Currently we do not
// free it because (similar to the token stream) it will be used to repeatedly
// run the `kernel::FlowGraphBuilder()`.
kernel::KernelReader reader(
reinterpret_cast<kernel::Program*>(kernel_program));
const Object& tmp = reader.ReadProgram();
if (tmp.IsError()) {
return Api::NewHandle(T, tmp.raw());
}

View file

@ -93,35 +93,34 @@ RawClass* BuildingTranslationHelper::LookupClassByKernelClass(Class* klass) {
return reader_->LookupClass(klass).raw();
}
Program* KernelReader::ReadPrecompiledProgram() {
Program* program = ReadPrecompiledKernelFromBuffer(buffer_, buffer_length_);
if (program == NULL) return NULL;
intptr_t source_file_count = program->line_starting_table().size();
KernelReader::KernelReader(Program* program)
: program_(program),
thread_(dart::Thread::Current()),
zone_(thread_->zone()),
isolate_(thread_->isolate()),
scripts_(Array::ZoneHandle(zone_)),
translation_helper_(this, thread_, zone_, isolate_),
type_translator_(&translation_helper_,
&active_class_,
/*finalize=*/false) {
intptr_t source_file_count = program_->line_starting_table().size();
scripts_ = Array::New(source_file_count);
program_ = program;
return program;
}
Object& KernelReader::ReadProgram() {
Program* program = ReadPrecompiledProgram();
if (program == NULL) {
const dart::String& error = H.DartString("Failed to read .kernell file");
return Object::Handle(Z, ApiError::New(error));
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Procedure* main = program->main_method();
Procedure* main = program_->main_method();
Library* kernel_main_library = Library::Cast(main->parent());
intptr_t length = program->libraries().length();
intptr_t length = program_->libraries().length();
for (intptr_t i = 0; i < length; i++) {
Library* kernel_library = program->libraries()[i];
Library* kernel_library = program_->libraries()[i];
ReadLibrary(kernel_library);
}
for (intptr_t i = 0; i < length; i++) {
dart::Library& library = LookupLibrary(program->libraries()[i]);
dart::Library& library = LookupLibrary(program_->libraries()[i]);
if (!library.Loaded()) library.SetLoaded();
}

View file

@ -54,21 +54,7 @@ class Mapping {
class KernelReader {
public:
KernelReader(const uint8_t* buffer, intptr_t len)
: thread_(dart::Thread::Current()),
zone_(thread_->zone()),
isolate_(thread_->isolate()),
scripts_(Array::ZoneHandle(zone_)),
program_(NULL),
translation_helper_(this, thread_, zone_, isolate_),
type_translator_(&translation_helper_,
&active_class_,
/*finalize=*/false),
buffer_(buffer),
buffer_length_(len) {}
// Returns either pointer to a program or null.
Program* ReadPrecompiledProgram();
explicit KernelReader(Program* program);
// Returns either a library or a failure object.
dart::Object& ReadProgram();
@ -112,18 +98,16 @@ class KernelReader {
dart::RawFunction::Kind GetFunctionType(Procedure* kernel_procedure);
Program* program_;
dart::Thread* thread_;
dart::Zone* zone_;
dart::Isolate* isolate_;
Array& scripts_;
Program* program_;
ActiveClass active_class_;
BuildingTranslationHelper translation_helper_;
DartTypeTranslator type_translator_;
const uint8_t* buffer_;
intptr_t buffer_length_;
Mapping<Library, dart::Library> libraries_;
Mapping<Class, dart::Class> classes_;
};

View file

@ -1136,14 +1136,12 @@ void Object::RegisterPrivateClass(const Class& cls,
// A non-NULL kernel argument indicates (1). A NULL kernel indicates (2) or
// (3), depending on whether the VM is compiled with DART_NO_SNAPSHOT defined or
// not.
RawError* Object::Init(Isolate* isolate,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length) {
RawError* Object::Init(Isolate* isolate, kernel::Program* kernel_program) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(isolate == thread->isolate());
#if !defined(DART_PRECOMPILED_RUNTIME)
const bool is_kernel = (kernel_buffer != NULL);
const bool is_kernel = (kernel_program != NULL);
#endif
NOT_IN_PRODUCT(TimelineDurationScope tds(thread, Timeline::GetIsolateStream(),
"Object::Init");)
@ -1619,8 +1617,8 @@ RawError* Object::Init(Isolate* isolate,
// Finish the initialization by compiling the bootstrap scripts containing
// the base interfaces and the implementation of the internal classes.
const Error& error = Error::Handle(
zone, Bootstrap::DoBootstrapping(kernel_buffer, kernel_buffer_length));
const Error& error =
Error::Handle(zone, Bootstrap::DoBootstrapping(kernel_program));
if (!error.IsNull()) {
return error.raw();
}

View file

@ -29,6 +29,10 @@
namespace dart {
// Forward declarations.
namespace kernel {
class Program;
}
#define DEFINE_FORWARD_DECLARATION(clazz) class clazz;
CLASS_LIST(DEFINE_FORWARD_DECLARATION)
#undef DEFINE_FORWARD_DECLARATION
@ -527,9 +531,7 @@ class Object {
// Initialize a new isolate either from a Kernel IR, from source, or from a
// snapshot.
static RawError* Init(Isolate* isolate,
const uint8_t* kernel,
intptr_t kernel_length);
static RawError* Init(Isolate* isolate, kernel::Program* program);
static void MakeUnusedSpaceTraversable(const Object& obj,
intptr_t original_size,