[vm] Include bytecode in core-jit and app-jit snapshots
when the --enable_interpreter flag is passed to gen_snapshot/dart.
Change-Id: I6e9481e1793e4c97fd6b14b56e55aafcb044fc23
Reviewed-on: https://dart-review.googlesource.com/c/78150
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Zach Anderson <zra@google.com>
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index a177658..24da95d 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -549,6 +549,14 @@
dependencies->Clear();
}
+static void LoadBytecode() {
+ if (Dart_IsVMFlagSet("enable_interpreter") &&
+ ((snapshot_kind == kCoreJIT) || (snapshot_kind == kAppJIT))) {
+ Dart_Handle result = Dart_ReadAllBytecode();
+ CHECK_RESULT(result);
+ }
+}
+
static void LoadCompilationTrace() {
if ((load_compilation_trace_filename != NULL) &&
((snapshot_kind == kCoreJIT) || (snapshot_kind == kAppJIT))) {
@@ -925,10 +933,12 @@
CreateAndWriteCoreSnapshot();
break;
case kCoreJIT:
+ LoadBytecode();
LoadCompilationTrace();
CreateAndWriteCoreJITSnapshot();
break;
case kAppJIT:
+ LoadBytecode();
LoadCompilationTrace();
CreateAndWriteAppJITSnapshot();
break;
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 0843f44..4c2a515 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -797,6 +797,13 @@
file->Release();
}
+static void LoadBytecode() {
+ if (Dart_IsVMFlagSet("enable_interpreter")) {
+ Dart_Handle result = Dart_ReadAllBytecode();
+ CHECK_RESULT(result);
+ }
+}
+
bool RunMainIsolate(const char* script_name, CommandLineOptions* dart_options) {
// Call CreateIsolateAndSetup which creates an isolate and loads up
// the specified application script.
@@ -879,6 +886,7 @@
if (Options::gen_snapshot_kind() == kAppJIT) {
result = Dart_SortClasses();
CHECK_RESULT(result);
+ LoadBytecode();
}
if (Options::load_compilation_trace_filename() != NULL) {
diff --git a/runtime/observatory/lib/src/elements/objectpool_view.dart b/runtime/observatory/lib/src/elements/objectpool_view.dart
index 85b546d..71ce7a0 100644
--- a/runtime/observatory/lib/src/elements/objectpool_view.dart
+++ b/runtime/observatory/lib/src/elements/objectpool_view.dart
@@ -159,6 +159,7 @@
List<Element> _createEntry(M.ObjectPoolEntry entry) {
switch (entry.kind) {
+ case M.ObjectPoolEntryKind.nativeEntryData:
case M.ObjectPoolEntryKind.object:
return [anyRef(_isolate, entry.asObject, _objects, queue: _r.queue)];
case M.ObjectPoolEntryKind.immediate:
diff --git a/runtime/observatory/lib/src/models/objects/objectpool.dart b/runtime/observatory/lib/src/models/objects/objectpool.dart
index fb99d60..ba65961 100644
--- a/runtime/observatory/lib/src/models/objects/objectpool.dart
+++ b/runtime/observatory/lib/src/models/objects/objectpool.dart
@@ -12,7 +12,7 @@
Iterable<ObjectPoolEntry> get entries;
}
-enum ObjectPoolEntryKind { object, immediate, nativeEntry }
+enum ObjectPoolEntryKind { object, immediate, nativeEntryData, nativeEntry }
abstract class ObjectPoolEntry {
int get offset;
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 0c67adc..a0dcf7a4 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -3987,6 +3987,7 @@
M.ObjectPoolEntryKind kind = stringToObjectPoolEntryKind(map['kind']);
int offset = map['offset'];
switch (kind) {
+ case M.ObjectPoolEntryKind.nativeEntryData:
case M.ObjectPoolEntryKind.object:
return new ObjectPoolEntry._fromObject(map['value'], offset);
default:
@@ -4008,6 +4009,8 @@
return M.ObjectPoolEntryKind.object;
case 'Immediate':
return M.ObjectPoolEntryKind.immediate;
+ case 'NativeEntryData':
+ return M.ObjectPoolEntryKind.nativeEntryData;
case 'NativeFunction':
case 'NativeFunctionWrapper':
return M.ObjectPoolEntryKind.nativeEntry;
diff --git a/runtime/tests/vm/dart/appjit_bytecode_simple_test.dart b/runtime/tests/vm/dart/appjit_bytecode_simple_test.dart
new file mode 100644
index 0000000..6600482
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_bytecode_simple_test.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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.
+
+// Verify that app-jit snapshot contains dependencies between classes and CHA
+// optimized code.
+
+import 'dart:async';
+
+import 'snapshot_test_helper.dart';
+
+Future<void> main() => runAppJitBytecodeTest();
diff --git a/runtime/tests/vm/dart/appjit_bytecode_simple_test_body.dart b/runtime/tests/vm/dart/appjit_bytecode_simple_test_body.dart
new file mode 100644
index 0000000..a882e9a
--- /dev/null
+++ b/runtime/tests/vm/dart/appjit_bytecode_simple_test_body.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, 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.
+
+void main(List<String> args) {
+ final isTraining = args.contains("--train");
+ print(isTraining ? 'OK(Trained)' : 'OK(Run)');
+}
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 2eced21..ef65c1f 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -116,3 +116,27 @@
await temp.delete(recursive: true);
}
}
+
+Future<void> runAppJitBytecodeTest() async {
+ final Directory temp = Directory.systemTemp.createTempSync();
+ final snapshotPath = p.join(temp.path, 'app.jit');
+ final testPath = Platform.script
+ .toFilePath()
+ .replaceAll(new RegExp(r'_test.dart$'), '_test_body.dart');
+
+ try {
+ final trainingResult = await runDartBinary('TRAINING RUN', [
+ '--enable_interpreter',
+ '--snapshot=$snapshotPath',
+ '--snapshot-kind=app-jit',
+ testPath,
+ '--train'
+ ]);
+ expectOutput("OK(Trained)", trainingResult);
+ final runResult = await runDartBinary(
+ 'RUN FROM SNAPSHOT', ['--enable_interpreter', snapshotPath]);
+ expectOutput("OK(Run)", runResult);
+ } finally {
+ await temp.delete(recursive: true);
+ }
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 4e31d0e..793274a 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -104,6 +104,7 @@
cc/Service_Profile: Skip
cc/GenKernelKernelLoadKernel: Skip # Issue 34542.
cc/GenKernelKernelReadAllBytecode: Skip # Issue 34393.
+dart/appjit_bytecode_simple_test: Skip # Issue 34393.
[ !$strong ]
dart/callee_side_type_checks_test: SkipByDesign
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 657af7e..2d39211 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -558,6 +558,7 @@
s->Push(func->ptr()->code_);
} else if (s->kind() == Snapshot::kFullJIT) {
NOT_IN_PRECOMPILED(s->Push(func->ptr()->unoptimized_code_));
+ NOT_IN_PRECOMPILED(s->Push(func->ptr()->bytecode_));
s->Push(func->ptr()->code_);
s->Push(func->ptr()->ic_data_array_);
}
@@ -587,6 +588,7 @@
s->WriteRef(func->ptr()->code_);
} else if (s->kind() == Snapshot::kFullJIT) {
NOT_IN_PRECOMPILED(s->WriteRef(func->ptr()->unoptimized_code_));
+ NOT_IN_PRECOMPILED(s->WriteRef(func->ptr()->bytecode_));
s->WriteRef(func->ptr()->code_);
s->WriteRef(func->ptr()->ic_data_array_);
}
@@ -646,6 +648,8 @@
} else if (kind == Snapshot::kFullJIT) {
NOT_IN_PRECOMPILED(func->ptr()->unoptimized_code_ =
reinterpret_cast<RawCode*>(d->ReadRef()));
+ NOT_IN_PRECOMPILED(func->ptr()->bytecode_ =
+ reinterpret_cast<RawCode*>(d->ReadRef()));
func->ptr()->code_ = reinterpret_cast<RawCode*>(d->ReadRef());
func->ptr()->ic_data_array_ = reinterpret_cast<RawArray*>(d->ReadRef());
}
@@ -705,6 +709,10 @@
if (func.HasCode() && !code.IsDisabled()) {
func.SetInstructions(code); // Set entrypoint.
func.SetWasCompiled(true);
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ } else if (func.HasBytecode() && !code.IsDisabled()) {
+ func.SetInstructions(code); // Set entrypoint.
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
} else {
func.ClearCode(); // Set code and entrypoint to lazy compile stub.
}
@@ -1854,7 +1862,8 @@
uint8_t* entry_bits = pool->ptr()->entry_bits();
for (intptr_t i = 0; i < length; i++) {
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
- if (entry_type == ObjectPool::kTaggedObject) {
+ if ((entry_type == ObjectPool::kTaggedObject) ||
+ (entry_type == ObjectPool::kNativeEntryData)) {
s->Push(pool->ptr()->data()[i].raw_obj_);
}
}
@@ -1903,6 +1912,22 @@
s->Write<intptr_t>(entry.raw_value_);
break;
}
+ case ObjectPool::kNativeEntryData: {
+ RawObject* raw = entry.raw_obj_;
+ RawTypedData* raw_data = reinterpret_cast<RawTypedData*>(raw);
+ // kNativeEntryData object pool entries are for linking natives for
+ // the interpreter. Before writing these entries into the snapshot,
+ // we need to unlink them by nulling out the 'trampoline' and
+ // 'native_function' fields.
+ NativeEntryData::Payload* payload =
+ NativeEntryData::FromTypedArray(raw_data);
+ if (payload->kind == MethodRecognizer::kUnknown) {
+ payload->trampoline = NULL;
+ payload->native_function = NULL;
+ }
+ s->WriteRef(raw);
+ break;
+ }
case ObjectPool::kNativeFunction:
case ObjectPool::kNativeFunctionWrapper: {
// Write nothing. Will initialize with the lazy link entry.
@@ -1950,6 +1975,7 @@
pool->ptr()->entry_bits()[j] = entry_bits;
RawObjectPool::Entry& entry = pool->ptr()->data()[j];
switch (ObjectPool::TypeBits::decode(entry_bits)) {
+ case ObjectPool::kNativeEntryData:
case ObjectPool::kTaggedObject:
entry.raw_obj_ = d->ReadRef();
break;
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 81efa83..f1455d5 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -38,6 +38,8 @@
#if !defined(PRODUCT)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadMetadata");
+ tds.SetNumArguments(1);
+ tds.CopyArgument(0, "Function", function.ToQualifiedCString());
#endif // !defined(PRODUCT)
const intptr_t node_offset = function.kernel_offset();
@@ -540,7 +542,11 @@
case ConstantPoolTag::kNativeEntry: {
name = H.DartString(helper_->ReadStringReference()).raw();
obj = NativeEntry(function, name);
- } break;
+ pool.SetTypeAt(i, ObjectPool::kNativeEntryData,
+ ObjectPool::kNotPatchable);
+ pool.SetObjectAt(i, obj);
+ continue;
+ }
case ConstantPoolTag::kSubtypeTestCache: {
obj = SubtypeTestCache::New();
} break;
@@ -722,11 +728,7 @@
NativeFunction native_function = NULL;
intptr_t argc_tag = 0;
if (kind == MethodRecognizer::kUnknown) {
- if (FLAG_link_natives_lazily) {
- trampoline = &NativeEntry::BootstrapNativeCallWrapper;
- native_function =
- reinterpret_cast<NativeFunction>(&NativeEntry::LinkNativeCall);
- } else {
+ if (!FLAG_link_natives_lazily) {
const Class& cls = Class::Handle(zone, function.Owner());
const Library& library = Library::Handle(zone, cls.library());
Dart_NativeEntryResolver resolver = library.native_entry_resolver();
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index c7e30ef..87d29ac 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -2263,21 +2263,29 @@
*--SP = null_value;
} break;
default: {
- NativeFunctionWrapper trampoline = NativeEntryData::GetTrampoline(data);
- NativeFunction function = NativeEntryData::GetNativeFunction(data);
+ NativeEntryData::Payload* payload =
+ NativeEntryData::FromTypedArray(data);
intptr_t argc_tag = NativeEntryData::GetArgcTag(data);
const intptr_t num_arguments =
NativeArguments::ArgcBits::decode(argc_tag);
+ if (payload->trampoline == NULL) {
+ ASSERT(payload->native_function == NULL);
+ payload->trampoline = &NativeEntry::BootstrapNativeCallWrapper;
+ payload->native_function =
+ reinterpret_cast<NativeFunction>(&NativeEntry::LinkNativeCall);
+ }
+
*++SP = null_value; // Result slot.
RawObject** incoming_args = SP - num_arguments;
RawObject** return_slot = SP;
Exit(thread, FP, SP, pc);
NativeArguments args(thread, argc_tag, incoming_args, return_slot);
- INVOKE_NATIVE(trampoline,
- reinterpret_cast<Dart_NativeFunction>(function),
- reinterpret_cast<Dart_NativeArguments>(&args));
+ INVOKE_NATIVE(
+ payload->trampoline,
+ reinterpret_cast<Dart_NativeFunction>(payload->native_function),
+ reinterpret_cast<Dart_NativeArguments>(&args));
*(SP - num_arguments) = *return_slot;
SP -= num_arguments;
diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h
index 2b4ebb0..c0abf55 100644
--- a/runtime/vm/native_entry.h
+++ b/runtime/vm/native_entry.h
@@ -192,6 +192,9 @@
static Payload* FromTypedArray(RawTypedData* data);
const TypedData& data_;
+
+ friend class Interpreter;
+ friend class ObjectPoolSerializationCluster;
DISALLOW_COPY_AND_ASSIGN(NativeEntryData);
};
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 2270dcb..e48782a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -13646,7 +13646,7 @@
for (intptr_t i = 0; i < Length(); i++) {
intptr_t offset = OffsetFromIndex(i);
THR_Print(" %" Pd " PP+0x%" Px ": ", i, offset);
- if (TypeAt(i) == kTaggedObject) {
+ if ((TypeAt(i) == kTaggedObject) || (TypeAt(i) == kNativeEntryData)) {
RawObject* obj = ObjectAt(i);
THR_Print("0x%" Px " %s (obj)\n", reinterpret_cast<uword>(obj),
Object::Handle(obj).ToCString());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index aa479fe..4f2f070 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4309,6 +4309,7 @@
kImmediate,
kNativeFunction,
kNativeFunctionWrapper,
+ kNativeEntryData,
};
enum Patchability {
@@ -4360,11 +4361,13 @@
}
RawObject* ObjectAt(intptr_t index) const {
- ASSERT(TypeAt(index) == kTaggedObject);
+ ASSERT((TypeAt(index) == kTaggedObject) ||
+ (TypeAt(index) == kNativeEntryData));
return EntryAddr(index)->raw_obj_;
}
void SetObjectAt(intptr_t index, const Object& obj) const {
- ASSERT(TypeAt(index) == kTaggedObject);
+ ASSERT((TypeAt(index) == kTaggedObject) ||
+ (TypeAt(index) == kNativeEntryData));
StorePointer(&EntryAddr(index)->raw_obj_, obj.raw());
}
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 502e0ed..c618ac6 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -657,6 +657,11 @@
jsentry.AddProperty("kind", "Immediate");
jsentry.AddProperty64("value", imm);
break;
+ case ObjectPool::kNativeEntryData:
+ obj = ObjectAt(i);
+ jsentry.AddProperty("kind", "NativeEntryData");
+ jsentry.AddProperty("value", obj);
+ break;
case ObjectPool::kNativeFunction:
imm = RawValueAt(i);
jsentry.AddProperty("kind", "NativeFunction");
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 0e69083..2dea940 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -553,7 +553,8 @@
for (intptr_t i = 0; i < length; ++i) {
ObjectPool::EntryType entry_type =
ObjectPool::TypeBits::decode(entry_bits[i]);
- if (entry_type == ObjectPool::kTaggedObject) {
+ if ((entry_type == ObjectPool::kTaggedObject) ||
+ (entry_type == ObjectPool::kNativeEntryData)) {
visitor->VisitPointer(&entries[i].raw_obj_);
}
}