[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_);
     }
   }