[vm/nnbd] Add separate Snapshot::Kind for core snapshots

Core snapshots should be agnostic to the sound null safety mode
(so they can be used both in weak and strong modes), and snapshot
writer verifies that.

Snapshot::kFull was previously used both for core snapshots and
app snapshots on ia32. However, app snapshots are not guaranteed to
be agnostic, which appeared as failures on a few test on ia32.
Also, VM should be able to detect null safety mode from app snapshots,
even if they do not contain code, but null safety mode was not
written into features string of kFull snapshots.

In order to disambiguate core snapshots, a new Snapshot::Kind is
added. Snapshot::kFullCore works exactly as Snapshot::kFull, except
for verification of agnostic null safety and snapshot features string
omitting null safety mode. All snapshots except kFullCore now have
null safety mode included into their features string.

Fixes https://github.com/dart-lang/sdk/issues/43626
Issue https://github.com/dart-lang/sdk/issues/43613

Change-Id: I8cd3b049ef4e428dd5e1ce666d4c7aa3b596d70c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/166308
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2020-10-08 19:59:15 +00:00 committed by commit-bot@chromium.org
parent c21b906182
commit d77fff7307
12 changed files with 52 additions and 26 deletions

View file

@ -446,7 +446,8 @@ static void CreateAndWriteCoreSnapshot() {
// First create a snapshot.
result = Dart_CreateSnapshot(&vm_snapshot_data_buffer, &vm_snapshot_data_size,
&isolate_snapshot_data_buffer,
&isolate_snapshot_data_size);
&isolate_snapshot_data_size,
/*is_core=*/true);
CHECK_RESULT(result);
// Now write the vm isolate and isolate snapshots out to the
@ -539,7 +540,7 @@ static void CreateAndWriteAppSnapshot() {
intptr_t isolate_snapshot_data_size = 0;
result = Dart_CreateSnapshot(NULL, NULL, &isolate_snapshot_data_buffer,
&isolate_snapshot_data_size);
&isolate_snapshot_data_size, /*is_core=*/false);
CHECK_RESULT(result);
WriteFile(isolate_snapshot_data_filename, isolate_snapshot_data_buffer,

View file

@ -489,8 +489,8 @@ void Snapshot::GenerateAppJIT(const char* snapshot_filename) {
uint8_t* isolate_buffer = NULL;
intptr_t isolate_size = 0;
Dart_Handle result =
Dart_CreateSnapshot(NULL, NULL, &isolate_buffer, &isolate_size);
Dart_Handle result = Dart_CreateSnapshot(NULL, NULL, &isolate_buffer,
&isolate_size, /*is_core=*/false);
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}

View file

@ -1242,6 +1242,8 @@ DART_EXPORT void Dart_ExitIsolate();
* snapshot. This buffer is scope allocated and is only valid
* until the next call to Dart_ExitScope.
* \param size Returns the size of the buffer.
* \param is_core Create a snapshot containing core libraries.
* Such snapshot should be agnostic to null safety mode.
*
* \return A valid handle if no error occurs during the operation.
*/
@ -1249,7 +1251,8 @@ DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,
intptr_t* vm_snapshot_data_size,
uint8_t** isolate_snapshot_data_buffer,
intptr_t* isolate_snapshot_data_size);
intptr_t* isolate_snapshot_data_size,
bool is_core);
/**
* Returns whether the buffer contains a kernel file.

View file

@ -528,12 +528,12 @@ BENCHMARK_SIZE(CoreSnapshotSize) {
MallocWriteStream vm_snapshot_data(FullSnapshotWriter::kInitialSize);
MallocWriteStream isolate_snapshot_data(FullSnapshotWriter::kInitialSize);
FullSnapshotWriter writer(
Snapshot::kFull, &vm_snapshot_data, &isolate_snapshot_data,
Snapshot::kFullCore, &vm_snapshot_data, &isolate_snapshot_data,
/*vm_image_writer=*/nullptr, /*iso_image_writer=*/nullptr);
writer.WriteFullSnapshot();
const Snapshot* snapshot =
Snapshot::SetupFromBuffer(isolate_snapshot_data.buffer());
ASSERT(snapshot->kind() == Snapshot::kFull);
ASSERT(snapshot->kind() == Snapshot::kFullCore);
benchmark->set_score(snapshot->length());
}
@ -566,12 +566,12 @@ BENCHMARK_SIZE(StandaloneSnapshotSize) {
MallocWriteStream vm_snapshot_data(FullSnapshotWriter::kInitialSize);
MallocWriteStream isolate_snapshot_data(FullSnapshotWriter::kInitialSize);
FullSnapshotWriter writer(
Snapshot::kFull, &vm_snapshot_data, &isolate_snapshot_data,
Snapshot::kFullCore, &vm_snapshot_data, &isolate_snapshot_data,
/*vm_image_writer=*/nullptr, /*iso_image_writer=*/nullptr);
writer.WriteFullSnapshot();
const Snapshot* snapshot =
Snapshot::SetupFromBuffer(isolate_snapshot_data.buffer());
ASSERT(snapshot->kind() == Snapshot::kFull);
ASSERT(snapshot->kind() == Snapshot::kFullCore);
benchmark->set_score(snapshot->length());
}

View file

@ -245,7 +245,8 @@ class ClassSerializationCluster : public SerializationCluster {
s->UnexpectedObject(cls, "Class with illegal cid");
}
s->WriteCid(class_id);
if (s->kind() == Snapshot::kFull && RequireLegacyErasureOfConstants(cls)) {
if (s->kind() == Snapshot::kFullCore &&
RequireLegacyErasureOfConstants(cls)) {
s->UnexpectedObject(cls, "Class with non mode agnostic constants");
}
if (s->kind() != Snapshot::kFullAOT) {
@ -596,7 +597,7 @@ class FunctionSerializationCluster : public SerializationCluster {
objects_.Add(func);
PushFromTo(func);
if (kind == Snapshot::kFull) {
if ((kind == Snapshot::kFull) || (kind == Snapshot::kFullCore)) {
NOT_IN_PRECOMPILED(s->Push(func->ptr()->bytecode_));
} else if (kind == Snapshot::kFullAOT) {
s->Push(func->ptr()->code_);
@ -625,7 +626,7 @@ class FunctionSerializationCluster : public SerializationCluster {
FunctionPtr func = objects_[i];
AutoTraceObjectName(func, MakeDisambiguatedFunctionName(s, func));
WriteFromTo(func);
if (kind == Snapshot::kFull) {
if ((kind == Snapshot::kFull) || (kind == Snapshot::kFullCore)) {
NOT_IN_PRECOMPILED(WriteField(func, bytecode_));
} else if (kind == Snapshot::kFullAOT) {
WriteField(func, code_);
@ -692,7 +693,7 @@ class FunctionDeserializationCluster : public DeserializationCluster {
Function::InstanceSize());
ReadFromTo(func);
if (kind == Snapshot::kFull) {
if ((kind == Snapshot::kFull) || (kind == Snapshot::kFullCore)) {
NOT_IN_PRECOMPILED(func->ptr()->bytecode_ =
static_cast<BytecodePtr>(d->ReadRef()));
} else if (kind == Snapshot::kFullAOT) {

View file

@ -782,12 +782,12 @@ bool Dart::DetectNullSafety(const char* script_uri,
// when generating the snapshot.
ASSERT(FLAG_sound_null_safety == kNullSafetyOptionUnspecified);
// If snapshot is an appJIT/AOT snapshot we will figure out the mode by
// If snapshot is not a core snapshot we will figure out the mode by
// sniffing the feature string in the snapshot.
if (snapshot_data != nullptr) {
// Read the snapshot and check for null safety option.
const Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_data);
if (Snapshot::IncludesCode(snapshot->kind())) {
if (!Snapshot::IsAgnosticToNullSafety(snapshot->kind())) {
return SnapshotHeaderReader::NullSafetyFromSnapshot(snapshot);
}
}
@ -1034,7 +1034,9 @@ const char* Dart::FeaturesString(Isolate* isolate,
#else
#error What architecture?
#endif
}
if (!Snapshot::IsAgnosticToNullSafety(kind)) {
if (isolate != NULL) {
if (isolate->null_safety()) {
buffer.AddString(" null-safety");

View file

@ -1934,7 +1934,8 @@ DART_EXPORT Dart_Handle
Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,
intptr_t* vm_snapshot_data_size,
uint8_t** isolate_snapshot_data_buffer,
intptr_t* isolate_snapshot_data_size) {
intptr_t* isolate_snapshot_data_size,
bool is_core) {
#if defined(DART_PRECOMPILED_RUNTIME)
return Api::NewError("Cannot create snapshots on an AOT runtime.");
#else
@ -1966,8 +1967,10 @@ Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,
FullSnapshotWriter::kInitialSize);
ZoneWriteStream isolate_snapshot_data(Api::TopScope(T)->zone(),
FullSnapshotWriter::kInitialSize);
const Snapshot::Kind snapshot_kind =
is_core ? Snapshot::kFullCore : Snapshot::kFull;
FullSnapshotWriter writer(
Snapshot::kFull, &vm_snapshot_data, &isolate_snapshot_data,
snapshot_kind, &vm_snapshot_data, &isolate_snapshot_data,
nullptr /* vm_image_writer */, nullptr /* isolate_image_writer */);
writer.WriteFullSnapshot();
if (vm_snapshot_data_buffer != nullptr) {

View file

@ -479,6 +479,7 @@ class ObjectStore {
ObjectPtr* to_snapshot(Snapshot::Kind kind) {
switch (kind) {
case Snapshot::kFull:
case Snapshot::kFullCore:
return reinterpret_cast<ObjectPtr*>(&global_object_pool_);
case Snapshot::kFullJIT:
case Snapshot::kFullAOT:

View file

@ -762,6 +762,7 @@ class ClassLayout : public ObjectLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&allocation_stub_);
case Snapshot::kFull:
case Snapshot::kFullCore:
return reinterpret_cast<ObjectPtr*>(&direct_subclasses_);
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&dependent_code_);
@ -836,6 +837,7 @@ class PatchClassLayout : public ObjectLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&script_);
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&library_kernel_data_);
case Snapshot::kMessage:
@ -1026,6 +1028,7 @@ class FunctionLayout : public ObjectLayout {
switch (kind) {
case Snapshot::kFullAOT:
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&data_);
case Snapshot::kMessage:
@ -1194,6 +1197,7 @@ class FieldLayout : public ObjectLayout {
ObjectPtr* to_snapshot(Snapshot::Kind kind) {
switch (kind) {
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&initializer_function_);
@ -1270,6 +1274,7 @@ class ScriptLayout : public ObjectLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&url_);
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&kernel_program_info_);
case Snapshot::kMessage:
@ -1343,6 +1348,7 @@ class LibraryLayout : public ObjectLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&exports_);
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&kernel_data_);
case Snapshot::kMessage:
@ -2067,6 +2073,7 @@ class ICDataLayout : public CallSiteDataLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&entries_);
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return to();
case Snapshot::kMessage:
@ -2174,6 +2181,7 @@ class LibraryPrefixLayout : public InstanceLayout {
case Snapshot::kFullAOT:
return reinterpret_cast<ObjectPtr*>(&imports_);
case Snapshot::kFull:
case Snapshot::kFullCore:
case Snapshot::kFullJIT:
return reinterpret_cast<ObjectPtr*>(&importer_);
case Snapshot::kMessage:

View file

@ -229,6 +229,8 @@ const char* Snapshot::KindToCString(Kind kind) {
switch (kind) {
case kFull:
return "full";
case kFullCore:
return "full-core";
case kFullJIT:
return "full-jit";
case kFullAOT:

View file

@ -91,11 +91,12 @@ enum SerializeState {
class Snapshot {
public:
enum Kind {
kFull, // Full snapshot of core libraries or an application.
kFullJIT, // Full + JIT code
kFullAOT, // Full + AOT code
kMessage, // A partial snapshot used only for isolate messaging.
kNone, // gen_snapshot
kFull, // Full snapshot of an application.
kFullCore, // Full snapshot of core libraries. Agnostic to null safety.
kFullJIT, // Full + JIT code
kFullAOT, // Full + AOT code
kMessage, // A partial snapshot used only for isolate messaging.
kNone, // gen_snapshot
kInvalid
};
static const char* KindToCString(Kind kind);
@ -130,13 +131,15 @@ class Snapshot {
void set_kind(Kind value) { return Write<int64_t>(kKindOffset, value); }
static bool IsFull(Kind kind) {
return (kind == kFull) || (kind == kFullJIT) || (kind == kFullAOT);
return (kind == kFull) || (kind == kFullCore) || (kind == kFullJIT) ||
(kind == kFullAOT);
}
static bool IsAgnosticToNullSafety(Kind kind) { return (kind == kFullCore); }
static bool IncludesCode(Kind kind) {
return (kind == kFullJIT) || (kind == kFullAOT);
}
static bool IncludesBytecode(Kind kind) {
return (kind == kFull) || (kind == kFullJIT);
return (kind == kFull) || (kind == kFullCore) || (kind == kFullJIT);
}
const uint8_t* Addr() const { return reinterpret_cast<const uint8_t*>(this); }
@ -171,7 +174,8 @@ class Snapshot {
inline static bool IsSnapshotCompatible(Snapshot::Kind vm_kind,
Snapshot::Kind isolate_kind) {
if (vm_kind == isolate_kind) return true;
if (vm_kind == Snapshot::kFull && isolate_kind == Snapshot::kFullJIT)
if (((vm_kind == Snapshot::kFull) || (vm_kind == Snapshot::kFullCore)) &&
isolate_kind == Snapshot::kFullJIT)
return true;
return Snapshot::IsFull(isolate_kind);
}

View file

@ -2073,7 +2073,8 @@ VM_UNIT_TEST_CASE(LegacyErasureDetectionInFullSnapshot) {
// Write snapshot with object content.
MallocWriteStream isolate_snapshot_data(FullSnapshotWriter::kInitialSize);
FullSnapshotWriter writer(
Snapshot::kFull, /*vm_snapshot_data=*/nullptr, &isolate_snapshot_data,
Snapshot::kFullCore, /*vm_snapshot_data=*/nullptr,
&isolate_snapshot_data,
/*vm_image_writer=*/nullptr, /*iso_image_writer=*/nullptr);
writer.WriteFullSnapshot();
}