[vm] Account for direct code calls from unoptimized code for --reused_instructions.

For example, constructors directly reference an allocation stub.

Change-Id: I5779c476c7721d2cc4b6ceb9348a1ffeaa5e082d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97272
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 4ca767d..37b9f7d 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -823,7 +823,11 @@
   class DropCodeVisitor : public FunctionVisitor, public ClassVisitor {
    public:
     explicit DropCodeVisitor(const void* reused_instructions)
-        : code_(Code::Handle()), instructions_(Instructions::Handle()) {
+        : code_(Code::Handle()),
+          instructions_(Instructions::Handle()),
+          pool_(ObjectPool::Handle()),
+          table_(Array::Handle()),
+          entry_(Object::Handle()) {
       ImageWriter::SetupShared(&reused_instructions_, reused_instructions);
       if (FLAG_trace_reused_instructions) {
         OS::PrintErr("%" Pd " reusable instructions\n",
@@ -833,18 +837,20 @@
 
     void Visit(const Class& cls) {
       code_ = cls.allocation_stub();
-      if (!code_.IsNull() && !IsAvailable(code_)) {
-        if (FLAG_trace_reused_instructions) {
-          OS::PrintErr("No reusable instructions for %s\n", cls.ToCString());
+      if (!code_.IsNull()) {
+        if (!CanKeep(code_)) {
+          if (FLAG_trace_reused_instructions) {
+            OS::PrintErr("No reusable instructions for %s\n", cls.ToCString());
+          }
+          cls.DisableAllocationStub();
         }
-        cls.DisableAllocationStub();
       }
     }
 
     void Visit(const Function& func) {
       if (func.HasCode()) {
         code_ = func.CurrentCode();
-        if (!IsAvailable(code_)) {
+        if (!CanKeep(code_)) {
           if (FLAG_trace_reused_instructions) {
             OS::PrintErr("No reusable instructions for %s\n", func.ToCString());
           }
@@ -854,16 +860,44 @@
         }
       }
       code_ = func.unoptimized_code();
-      if (!code_.IsNull() && !IsAvailable(code_)) {
+      if (!code_.IsNull() && !CanKeep(code_)) {
         if (FLAG_trace_reused_instructions) {
           OS::PrintErr("No reusable instructions for %s\n", func.ToCString());
         }
         func.ClearCode();
         func.ClearICDataArray();
-        return;
       }
     }
 
+    bool CanKeep(const Code& code) {
+      if (!IsAvailable(code)) {
+        return false;
+      }
+
+      pool_ = code.object_pool();
+      for (intptr_t i = 0; i < pool_.Length(); i++) {
+        if (pool_.TypeAt(i) == ObjectPool::EntryType::kTaggedObject) {
+          entry_ = pool_.ObjectAt(i);
+          if (entry_.IsCode() && !IsAvailable(Code::Cast(entry_))) {
+            return false;
+          }
+        }
+      }
+
+      table_ = code.static_calls_target_table();
+      if (!table_.IsNull()) {
+        StaticCallsTable static_calls(table_);
+        for (auto& view : static_calls) {
+          entry_ = view.Get<Code::kSCallTableCodeTarget>();
+          if (entry_.IsCode() && !IsAvailable(Code::Cast(entry_))) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    }
+
    private:
     bool IsAvailable(const Code& code) {
       ObjectOffsetPair* pair = reused_instructions_.Lookup(code.instructions());
@@ -873,6 +907,9 @@
     ObjectOffsetMap reused_instructions_;
     Code& code_;
     Instructions& instructions_;
+    ObjectPool& pool_;
+    Array& table_;
+    Object& entry_;
 
     DISALLOW_COPY_AND_ASSIGN(DropCodeVisitor);
   };
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index df88871..5d6d587 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -9435,7 +9435,6 @@
    public:
     TupleView(const Array& array, intptr_t index)
         : array_(array), index_(index) {
-      ASSERT(!array.IsNull());
     }
 
     template <EnumType kElement>
@@ -9483,6 +9482,7 @@
   };
 
   explicit ArrayOfTuplesView(const Array& array) : array_(array), index_(-1) {
+    ASSERT(!array.IsNull());
     ASSERT(array.Length() >= kStartOffset);
     ASSERT((array.Length() - kStartOffset) % EntrySize == kStartOffset);
   }