[vm, interpreter] Handle closure case of invoke-field-dispatchers as bytecode.

This will appear to regress the "interp-only" mode on Golem because it will no longer cheat by inlining into a machine code version of the closure call method.

Bug: https://github.com/dart-lang/sdk/issues/36410
Change-Id: I5daa2cffb6d18b6dd2da61ce08d5bc90092a250a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99561
Reviewed-by: RĂ©gis Crelier <regis@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 6e26e97..73012f7 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4693,6 +4693,8 @@
                 "<implicit static getter>");
   AddBaseObject(Object::method_extractor_bytecode().raw(), "Bytecode",
                 "<method extractor>");
+  AddBaseObject(Object::invoke_closure_bytecode().raw(), "Bytecode",
+                "<invoke closure>");
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
@@ -5152,6 +5154,7 @@
   AddBaseObject(Object::implicit_setter_bytecode().raw());
   AddBaseObject(Object::implicit_static_getter_bytecode().raw());
   AddBaseObject(Object::method_extractor_bytecode().raw());
+  AddBaseObject(Object::invoke_closure_bytecode().raw());
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i]);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index a9b468a..726069c 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -67,6 +67,12 @@
     case RawFunction::kMethodExtractor:
       function.AttachBytecode(Object::method_extractor_bytecode());
       return;
+    case RawFunction::kInvokeFieldDispatcher:
+      if (Class::Handle(function.Owner()).id() != kClosureCid) {
+        break;
+      }
+      function.AttachBytecode(Object::invoke_closure_bytecode());
+      return;
     default: {
     }
   }
@@ -110,7 +116,8 @@
       (function.kind() != RawFunction::kImplicitGetter) &&
       (function.kind() != RawFunction::kImplicitSetter) &&
       (function.kind() != RawFunction::kImplicitStaticGetter) &&
-      (function.kind() != RawFunction::kMethodExtractor)) {
+      (function.kind() != RawFunction::kMethodExtractor) &&
+      (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
     return;
   }
 
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 5380d10..d1ea633 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -969,7 +969,8 @@
         (function.kind() != RawFunction::kImplicitGetter) &&
         (function.kind() != RawFunction::kImplicitSetter) &&
         (function.kind() != RawFunction::kImplicitStaticGetter) &&
-        (function.kind() != RawFunction::kMethodExtractor)) {
+        (function.kind() != RawFunction::kMethodExtractor) &&
+        (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
       BytecodeFlowGraphBuilder bytecode_compiler(
           flow_graph_builder_, parsed_function(),
           &(flow_graph_builder_->ic_data_array_));
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 79886bf..eee357c 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -533,6 +533,7 @@
   V(VMInternal_ImplicitSetter,             0, ___, ___, ___)                   \
   V(VMInternal_ImplicitStaticGetter,       0, ___, ___, ___)                   \
   V(VMInternal_MethodExtractor,            0, ___, ___, ___)                   \
+  V(VMInternal_InvokeClosure,              0, ___, ___, ___)                   \
 
 #define KERNEL_BYTECODES_LIST(V)                                               \
   PUBLIC_KERNEL_BYTECODES_LIST(V)                                              \
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index e427b9a..fc8a28d 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -3370,6 +3370,83 @@
     DISPATCH();
   }
 
+  {
+    BYTECODE(VMInternal_InvokeClosure, 0);
+
+    const intptr_t type_args_len =
+        InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
+    const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
+    const intptr_t argc =
+        InterpreterHelpers::ArgDescArgCount(argdesc_) + receiver_idx;
+
+    RawClosure* receiver =
+        Closure::RawCast(FrameArguments(FP, argc)[receiver_idx]);
+    RawFunction* function = receiver->ptr()->function_;
+
+    if (LIKELY(Function::HasBytecode(function))) {
+      goto TailCallBytecode;
+    }
+    if (LIKELY(Function::HasCode(function))) {
+      goto CallMachineCode;
+    }
+
+    {
+      // Compile the function to either generate code or load bytecode.
+      SP[1] = argdesc_;
+      SP[2] = 0;  // Code result.
+      SP[3] = function;
+      Exit(thread, FP, SP + 4, pc);
+      NativeArguments native_args(thread, 1, /* argv */ SP + 3,
+                                  /* retval */ SP + 2);
+      if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
+        HANDLE_EXCEPTION;
+      }
+      function = Function::RawCast(SP[3]);
+      argdesc_ = Array::RawCast(SP[1]);
+    }
+
+    if (LIKELY(Function::HasBytecode(function))) {
+      goto TailCallBytecode;
+    }
+    if (LIKELY(Function::HasCode(function))) {
+      goto CallMachineCode;
+    }
+
+    UNREACHABLE();
+
+    {
+    TailCallBytecode:
+      ASSERT(function->IsFunction());
+      RawBytecode* bytecode = function->ptr()->bytecode_;
+      ASSERT(bytecode->IsBytecode());
+      FP[kKBCFunctionSlotFromFp] = function;
+      FP[kKBCPcMarkerSlotFromFp] = bytecode;
+      pp_ = bytecode->ptr()->object_pool_;
+      pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->instructions_);
+      NOT_IN_PRODUCT(pc_ = pc);  // For the profiler.
+      DISPATCH();
+    }
+
+    {
+    CallMachineCode:
+      RawObject** argv = FrameArguments(FP, argc);
+      for (intptr_t i = 0; i < argc; i++) {
+        *++SP = argv[i];
+      }
+
+      RawObject** call_base = SP - argc + 1;
+      RawObject** call_top = SP + 1;
+      call_top[0] = function;
+      if (!InvokeCompiled(thread, function, call_base, call_top, &pc, &FP,
+                          &SP)) {
+        HANDLE_EXCEPTION;
+      } else {
+        HANDLE_RETURN;
+      }
+      DISPATCH();
+    }
+  }
+
   // Helper used to handle noSuchMethod on closures.
   {
   ClosureNoSuchMethod:
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 557002e..0a8d677 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -925,6 +925,17 @@
   method_extractor_bytecode_->set_exception_handlers(
       Object::empty_exception_handlers());
 
+  static const KBCInstr invoke_closure_instr[2] = {
+      KernelBytecode::Encode(KernelBytecode::kVMInternal_InvokeClosure),
+      KernelBytecode::Encode(KernelBytecode::kReturnTOS),
+  };
+  *invoke_closure_bytecode_ = Bytecode::New(
+      reinterpret_cast<uword>(invoke_closure_instr),
+      sizeof(invoke_closure_instr), -1, Object::empty_object_pool());
+  invoke_closure_bytecode_->set_pc_descriptors(Object::empty_descriptors());
+  invoke_closure_bytecode_->set_exception_handlers(
+      Object::empty_exception_handlers());
+
   // Some thread fields need to be reinitialized as null constants have not been
   // initialized until now.
   Thread* thr = Thread::Current();
@@ -990,6 +1001,8 @@
   ASSERT(implicit_static_getter_bytecode_->IsBytecode());
   ASSERT(!method_extractor_bytecode_->IsSmi());
   ASSERT(method_extractor_bytecode_->IsBytecode());
+  ASSERT(!invoke_closure_bytecode_->IsSmi());
+  ASSERT(invoke_closure_bytecode_->IsBytecode());
 }
 
 void Object::FinishInit(Isolate* isolate) {
@@ -5707,12 +5720,13 @@
   }
   switch (kind()) {
     case RawFunction::kNoSuchMethodDispatcher:
-    case RawFunction::kInvokeFieldDispatcher:
     case RawFunction::kDynamicInvocationForwarder:
     case RawFunction::kImplicitClosureFunction:
     case RawFunction::kIrregexpFunction:
     case RawFunction::kFfiTrampoline:
       return false;
+    case RawFunction::kInvokeFieldDispatcher:
+      return Class::Handle(zone, Owner()).id() == kClosureCid;
     default:
       return true;
   }
@@ -15168,6 +15182,8 @@
     return "[Bytecode Stub] VMInternal_ImplicitStaticGetter";
   } else if (bytecode.raw() == Object::method_extractor_bytecode().raw()) {
     return "[Bytecode Stub] VMInternal_MethodExtractor";
+  } else if (bytecode.raw() == Object::invoke_closure_bytecode().raw()) {
+    return "[Bytecode Stub] VMInternal_InvokeClosure";
   }
   return "[unknown stub]";
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 354c7940..8a191b4 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -400,6 +400,7 @@
   V(Bytecode, implicit_setter_bytecode)                                        \
   V(Bytecode, implicit_static_getter_bytecode)                                 \
   V(Bytecode, method_extractor_bytecode)                                       \
+  V(Bytecode, invoke_closure_bytecode)                                         \
   V(Instance, sentinel)                                                        \
   V(Instance, transition_sentinel)                                             \
   V(Instance, unknown_constant)                                                \