Revert "[vm, interpreter] Invoke field as a bytecode."

This reverts commit c85631330dd8d2bb681b60100fd26702223bd9ce.

Reason for revert: precompilation build failure

Original change's description:
> [vm, interpreter] Invoke field as a bytecode.
> 
> Simplify invocation.
> 
> Change-Id: I2d605d5f2f17e0e98499a19b9d9b6059682100b5
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99884
> Reviewed-by: RĂ©gis Crelier <regis@google.com>
> Reviewed-by: Alexander Markov <alexmarkov@google.com>

TBR=rmacnak@google.com,alexmarkov@google.com,regis@google.com

Change-Id: If417cb83e55e392895cdbdd263757f3e877f5ee5
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99961
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index c06da27..73012f7 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4695,8 +4695,6 @@
                 "<method extractor>");
   AddBaseObject(Object::invoke_closure_bytecode().raw(), "Bytecode",
                 "<invoke closure>");
-  AddBaseObject(Object::invoke_field_bytecode().raw(), "Bytecode",
-                "<invoke field>");
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
@@ -5157,7 +5155,6 @@
   AddBaseObject(Object::implicit_static_getter_bytecode().raw());
   AddBaseObject(Object::method_extractor_bytecode().raw());
   AddBaseObject(Object::invoke_closure_bytecode().raw());
-  AddBaseObject(Object::invoke_field_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 8cdb6d1..726069c 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -68,11 +68,10 @@
       function.AttachBytecode(Object::method_extractor_bytecode());
       return;
     case RawFunction::kInvokeFieldDispatcher:
-      if (Class::Handle(function.Owner()).id() == kClosureCid) {
-        function.AttachBytecode(Object::invoke_closure_bytecode());
-      } else {
-        function.AttachBytecode(Object::invoke_field_bytecode());
+      if (Class::Handle(function.Owner()).id() != kClosureCid) {
+        break;
       }
+      function.AttachBytecode(Object::invoke_closure_bytecode());
       return;
     default: {
     }
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index dea5d07..eee357c 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -526,7 +526,7 @@
   V(CompareDoubleGe,                       0, ___, ___, ___)                   \
   V(CompareDoubleLe,                       0, ___, ___, ___)                   \
 
-  // These bytecodes are only generated within the VM. Reassigning their
+  // These bytecodes are only generated within the VM. Reassinging their
   // opcodes is not a breaking change.
 #define INTERNAL_KERNEL_BYTECODES_LIST(V)                                      \
   V(VMInternal_ImplicitGetter,             0, ___, ___, ___)                   \
@@ -534,11 +534,6 @@
   V(VMInternal_ImplicitStaticGetter,       0, ___, ___, ___)                   \
   V(VMInternal_MethodExtractor,            0, ___, ___, ___)                   \
   V(VMInternal_InvokeClosure,              0, ___, ___, ___)                   \
-  V(VMInternal_InvokeField,                0, ___, ___, ___)                   \
-  V(VMInternal_ForwardDynamicInvocation,   0, ___, ___, ___)                   \
-  V(VMInternal_DispatchNoSuchMethod,       0, ___, ___, ___)                   \
-  V(VMInternal_ImplicitStaticClosure,      0, ___, ___, ___)                   \
-  V(VMInternal_ImplicitInstanceClosure,    0, ___, ___, ___)                   \
 
 #define KERNEL_BYTECODES_LIST(V)                                               \
   PUBLIC_KERNEL_BYTECODES_LIST(V)                                              \
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 36c9524..fc8a28d 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -615,14 +615,194 @@
   return true;
 }
 
-DART_FORCE_INLINE bool Interpreter::InvokeBytecode(Thread* thread,
-                                                   RawFunction* function,
-                                                   RawObject** call_base,
-                                                   RawObject** call_top,
-                                                   uint32_t** pc,
-                                                   RawObject*** FP,
-                                                   RawObject*** SP) {
+DART_NOINLINE bool Interpreter::ProcessInvocation(bool* invoked,
+                                                  Thread* thread,
+                                                  RawFunction* function,
+                                                  RawObject** call_base,
+                                                  RawObject** call_top,
+                                                  uint32_t** pc,
+                                                  RawObject*** FP,
+                                                  RawObject*** SP) {
+  ASSERT(!Function::HasCode(function) && !Function::HasBytecode(function));
+  ASSERT(function == call_top[0]);
+  RawFunction::Kind kind = Function::kind(function);
+  switch (kind) {
+    case RawFunction::kInvokeFieldDispatcher: {
+      const intptr_t type_args_len =
+          InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
+      const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
+      RawObject* receiver = call_base[receiver_idx];
+      RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
+      ASSERT(function == FrameFunction(callee_fp));
+      RawFunction* call_function = Function::null();
+      if (function->ptr()->name_ == Symbols::Call().raw()) {
+        RawObject* owner = function->ptr()->owner_;
+        if (owner->GetClassId() == kPatchClassCid) {
+          owner = PatchClass::RawCast(owner)->ptr()->patched_class_;
+        }
+        if (owner == thread->isolate()->object_store()->closure_class()) {
+          // Closure call.
+          call_function = Closure::RawCast(receiver)->ptr()->function_;
+        }
+      }
+      if (call_function == Function::null()) {
+        // Invoke field getter on receiver.
+        call_top[1] = argdesc_;                // Save argdesc_.
+        call_top[2] = 0;                       // Result of runtime call.
+        call_top[3] = receiver;                // Receiver.
+        call_top[4] = function->ptr()->name_;  // Field name.
+        Exit(thread, *FP, call_top + 5, *pc);
+        NativeArguments native_args(thread, 2, call_top + 3, call_top + 2);
+        if (!InvokeRuntime(thread, this, DRT_GetFieldForDispatch,
+                           native_args)) {
+          return false;
+        }
+        argdesc_ = Array::RawCast(call_top[1]);
+
+        // Replace receiver with field value, keep all other arguments, and
+        // invoke 'call' function, or if not found, invoke noSuchMethod.
+        receiver = call_top[2];
+        call_base[receiver_idx] = receiver;
+
+        // If the field value is a closure, no need to resolve 'call' function.
+        // Otherwise, call runtime to resolve 'call' function.
+        if (InterpreterHelpers::GetClassId(receiver) == kClosureCid) {
+          // Closure call.
+          call_function = Closure::RawCast(receiver)->ptr()->function_;
+        } else {
+          // Resolve and invoke the 'call' function.
+          call_top[3] = argdesc_;
+          call_top[4] = 0;  // Result of runtime call.
+          Exit(thread, *FP, call_top + 5, *pc);
+          NativeArguments native_args(thread, 2, call_top + 2, call_top + 4);
+          if (!InvokeRuntime(thread, this, DRT_ResolveCallFunction,
+                             native_args)) {
+            return false;
+          }
+          argdesc_ = Array::RawCast(call_top[1]);
+          call_function = Function::RawCast(call_top[4]);
+          if (call_function == Function::null()) {
+            // Function 'call' could not be resolved for argdesc_.
+            // Invoke noSuchMethod.
+            const intptr_t argc =
+                receiver_idx + InterpreterHelpers::ArgDescArgCount(argdesc_);
+            RawObject* null_value = Object::null();
+            call_top[1] = null_value;
+            call_top[2] = call_base[receiver_idx];
+            call_top[3] = argdesc_;
+            call_top[4] = null_value;  // Array of arguments (will be filled).
+
+            // Allocate array of arguments.
+            {
+              call_top[5] = Smi::New(argc);  // length
+              call_top[6] = null_value;      // type
+              Exit(thread, *FP, call_top + 7, *pc);
+              NativeArguments native_args(thread, 2, call_top + 5,
+                                          call_top + 4);
+              if (!InvokeRuntime(thread, this, DRT_AllocateArray,
+                                 native_args)) {
+                return false;
+              }
+
+              // Copy arguments into the newly allocated array.
+              RawArray* array = static_cast<RawArray*>(call_top[4]);
+              ASSERT(array->GetClassId() == kArrayCid);
+              for (intptr_t i = 0; i < argc; i++) {
+                array->ptr()->data()[i] = call_base[i];
+              }
+            }
+
+            // We failed to resolve 'call' function.
+            call_top[5] = Symbols::Call().raw();
+
+            // Invoke noSuchMethod passing down receiver, argument descriptor,
+            // array of arguments, and target name.
+            {
+              Exit(thread, *FP, call_top + 6, *pc);
+              NativeArguments native_args(thread, 4, call_top + 2,
+                                          call_top + 1);
+              if (!InvokeRuntime(thread, this, DRT_InvokeNoSuchMethod,
+                                 native_args)) {
+                return false;
+              }
+            }
+            *SP = call_base;
+            **SP = call_top[1];
+            *invoked = true;
+            return true;
+          }
+        }
+      }
+      ASSERT(call_function != Function::null());
+      // Patch field dispatcher in callee frame with call function.
+      callee_fp[kKBCFunctionSlotFromFp] = call_function;
+      // Do not compile function if it has code or bytecode.
+      if (Function::HasCode(call_function)) {
+        *invoked = true;
+        return InvokeCompiled(thread, call_function, call_base, call_top, pc,
+                              FP, SP);
+      }
+      if (Function::HasBytecode(call_function)) {
+        *invoked = false;
+        return true;
+      }
+      function = call_function;
+      break;  // Compile and invoke the function.
+    }
+    case RawFunction::kNoSuchMethodDispatcher:
+      // TODO(regis): Implement. For now, use jitted version.
+      break;
+    case RawFunction::kDynamicInvocationForwarder:
+      // TODO(regis): Implement. For now, use jitted version.
+      break;
+    default:
+      break;
+  }
+  // Compile the function to either generate code or load bytecode.
+  call_top[1] = 0;  // Code result.
+  call_top[2] = function;
+  Exit(thread, *FP, call_top + 3, *pc);
+  NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
+  if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
+    return false;
+  }
+  // Reload objects after the call which may trigger GC.
+  function = Function::RawCast(call_top[2]);
+  if (Function::HasCode(function)) {
+    *invoked = true;
+    return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
+  }
   ASSERT(Function::HasBytecode(function));
+  // Bytecode was loaded in the above compilation step.
+  // The caller will dispatch to the function's bytecode.
+  *invoked = false;
+  ASSERT(thread->vm_tag() == VMTag::kDartInterpretedTagId);
+  ASSERT(thread->top_exit_frame_info() == 0);
+  return true;
+}
+
+DART_FORCE_INLINE bool Interpreter::Invoke(Thread* thread,
+                                           RawObject** call_base,
+                                           RawObject** call_top,
+                                           uint32_t** pc,
+                                           RawObject*** FP,
+                                           RawObject*** SP) {
+  RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
+
+  RawFunction* function = FrameFunction(callee_fp);
+  if (Function::HasCode(function)) {
+    return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
+  }
+  if (!Function::HasBytecode(function)) {
+    bool invoked = false;
+    bool result = ProcessInvocation(&invoked, thread, function, call_base,
+                                    call_top, pc, FP, SP);
+    if (invoked || !result) {
+      return result;
+    }
+    function = FrameFunction(callee_fp);  // Function may have been patched.
+    ASSERT(Function::HasBytecode(function));
+  }
 #if defined(DEBUG)
   if (IsTracingExecution()) {
     THR_Print("%" Pu64 " ", icount_);
@@ -630,8 +810,6 @@
               Function::Handle(function).ToFullyQualifiedCString());
   }
 #endif
-  RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
-  ASSERT(function == FrameFunction(callee_fp));
   RawBytecode* bytecode = function->ptr()->bytecode_;
   callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
   callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
@@ -645,38 +823,6 @@
   return true;
 }
 
-DART_FORCE_INLINE bool Interpreter::Invoke(Thread* thread,
-                                           RawObject** call_base,
-                                           RawObject** call_top,
-                                           uint32_t** pc,
-                                           RawObject*** FP,
-                                           RawObject*** SP) {
-  RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
-  RawFunction* function = FrameFunction(callee_fp);
-
-  for (;;) {
-    if (Function::HasCode(function)) {
-      return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
-    }
-    if (Function::HasBytecode(function)) {
-      return InvokeBytecode(thread, function, call_base, call_top, pc, FP, SP);
-    }
-
-    // Compile the function to either generate code or load bytecode.
-    call_top[1] = 0;  // Code result.
-    call_top[2] = function;
-    Exit(thread, *FP, call_top + 3, *pc);
-    NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
-    if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
-      return false;
-    }
-    // Reload objects after the call which may trigger GC.
-    function = Function::RawCast(call_top[2]);
-
-    ASSERT(Function::HasCode(function) || Function::HasBytecode(function));
-  }
-}
-
 void Interpreter::InlineCacheMiss(int checked_args,
                                   Thread* thread,
                                   RawICData* icdata,
@@ -3227,9 +3373,6 @@
   {
     BYTECODE(VMInternal_InvokeClosure, 0);
 
-    ASSERT(Function::kind(FrameFunction(FP)) ==
-           RawFunction::kInvokeFieldDispatcher);
-
     const intptr_t type_args_len =
         InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
     const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
@@ -3240,185 +3383,14 @@
         Closure::RawCast(FrameArguments(FP, argc)[receiver_idx]);
     RawFunction* function = receiver->ptr()->function_;
 
-    SP[1] = function;
-    goto TailCallSP1;
-  }
+    if (LIKELY(Function::HasBytecode(function))) {
+      goto TailCallBytecode;
+    }
+    if (LIKELY(Function::HasCode(function))) {
+      goto CallMachineCode;
+    }
 
-  {
-    BYTECODE(VMInternal_InvokeField, 0);
-
-    RawFunction* function = FrameFunction(FP);
-    ASSERT(Function::kind(function) == RawFunction::kInvokeFieldDispatcher);
-
-    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;
-
-    RawObject* receiver = FrameArguments(FP, argc)[receiver_idx];
-
-    // Invoke field getter on receiver.
     {
-      SP[1] = argdesc_;                // Save argdesc_.
-      SP[2] = 0;                       // Result of runtime call.
-      SP[3] = receiver;                // Receiver.
-      SP[4] = function->ptr()->name_;  // Field name.
-      Exit(thread, FP, SP + 5, pc);
-      NativeArguments native_args(thread, 2, SP + 3, SP + 2);
-      if (!InvokeRuntime(thread, this, DRT_GetFieldForDispatch, native_args)) {
-        HANDLE_EXCEPTION;
-      }
-      argdesc_ = Array::RawCast(SP[1]);
-    }
-
-    // Replace receiver with field value, keep all other arguments, and
-    // invoke 'call' function, or if not found, invoke noSuchMethod.
-    FrameArguments(FP, argc)[receiver_idx] = receiver = SP[2];
-
-    // If the field value is a closure, no need to resolve 'call' function.
-    if (InterpreterHelpers::GetClassId(receiver) == kClosureCid) {
-      SP[1] = Closure::RawCast(receiver)->ptr()->function_;
-      goto TailCallSP1;
-    }
-
-    // Otherwise, call runtime to resolve 'call' function.
-    {
-      SP[1] = 0;  // Result slot.
-      SP[2] = receiver;
-      SP[3] = argdesc_;
-      Exit(thread, FP, SP + 4, pc);
-      NativeArguments native_args(thread, 2, SP + 2, SP + 1);
-      if (!InvokeRuntime(thread, this, DRT_ResolveCallFunction, native_args)) {
-        HANDLE_EXCEPTION;
-      }
-      argdesc_ = Array::RawCast(SP[3]);
-      function = Function::RawCast(SP[1]);
-      receiver = SP[2];
-    }
-
-    if (function != Function::null()) {
-      SP[1] = function;
-      goto TailCallSP1;
-    }
-
-    // Function 'call' could not be resolved for argdesc_.
-    // Invoke noSuchMethod.
-    RawObject* null_value = Object::null();
-    SP[1] = null_value;
-    SP[2] = receiver;
-    SP[3] = argdesc_;
-    SP[4] = null_value;  // Array of arguments (will be filled).
-
-    // Allocate array of arguments.
-    {
-      SP[5] = Smi::New(argc);  // length
-      SP[6] = null_value;      // type
-      Exit(thread, FP, SP + 7, pc);
-      NativeArguments native_args(thread, 2, SP + 5, SP + 4);
-      if (!InvokeRuntime(thread, this, DRT_AllocateArray, native_args)) {
-        HANDLE_EXCEPTION;
-      }
-    }
-
-    // Copy arguments into the newly allocated array.
-    RawObject** argv = FrameArguments(FP, argc);
-    RawArray* array = static_cast<RawArray*>(SP[4]);
-    ASSERT(array->GetClassId() == kArrayCid);
-    for (intptr_t i = 0; i < argc; i++) {
-      array->ptr()->data()[i] = argv[i];
-    }
-
-    // We failed to resolve 'call' function.
-    SP[5] = Symbols::Call().raw();
-
-    // Invoke noSuchMethod passing down receiver, argument descriptor,
-    // array of arguments, and target name.
-    {
-      Exit(thread, FP, SP + 6, pc);
-      NativeArguments native_args(thread, 4, SP + 2, SP + 1);
-      if (!InvokeRuntime(thread, this, DRT_InvokeNoSuchMethod, native_args)) {
-        HANDLE_EXCEPTION;
-      }
-
-      ++SP;  // Result at SP[0]
-    }
-    DISPATCH();
-  }
-
-  {
-    BYTECODE(VMInternal_ForwardDynamicInvocation, 0);
-    RawFunction* function = FrameFunction(FP);
-    ASSERT(Function::kind(function) ==
-           RawFunction::kDynamicInvocationForwarder);
-    UNIMPLEMENTED();
-    DISPATCH();
-  }
-
-  {
-    BYTECODE(VMInternal_DispatchNoSuchMethod, 0);
-    RawFunction* function = FrameFunction(FP);
-    ASSERT(Function::kind(function) == RawFunction::kNoSuchMethodDispatcher);
-    UNIMPLEMENTED();
-    DISPATCH();
-  }
-
-  {
-    BYTECODE(VMInternal_ImplicitStaticClosure, 0);
-    RawFunction* function = FrameFunction(FP);
-    ASSERT(Function::kind(function) == RawFunction::kImplicitClosureFunction);
-    UNIMPLEMENTED();
-    DISPATCH();
-  }
-
-  {
-    BYTECODE(VMInternal_ImplicitInstanceClosure, 0);
-    RawFunction* function = FrameFunction(FP);
-    ASSERT(Function::kind(function) == RawFunction::kImplicitClosureFunction);
-    UNIMPLEMENTED();
-    DISPATCH();
-  }
-
-  {
-  TailCallSP1:
-    RawFunction* function = Function::RawCast(SP[1]);
-
-    for (;;) {
-      if (Function::HasBytecode(function)) {
-        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();
-      }
-
-      if (Function::HasCode(function)) {
-        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;
-        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();
-      }
-
       // Compile the function to either generate code or load bytecode.
       SP[1] = argdesc_;
       SP[2] = 0;  // Code result.
@@ -3431,8 +3403,47 @@
       }
       function = Function::RawCast(SP[3]);
       argdesc_ = Array::RawCast(SP[1]);
+    }
 
-      ASSERT(Function::HasCode(function) || Function::HasBytecode(function));
+    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();
     }
   }
 
diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h
index db8d6e7..e8fe9db 100644
--- a/runtime/vm/interpreter.h
+++ b/runtime/vm/interpreter.h
@@ -153,15 +153,16 @@
               RawObject*** FP,
               RawObject*** SP);
 
-  bool InvokeCompiled(Thread* thread,
-                      RawFunction* function,
-                      RawObject** call_base,
-                      RawObject** call_top,
-                      uint32_t** pc,
-                      RawObject*** FP,
-                      RawObject*** SP);
+  bool ProcessInvocation(bool* invoked,
+                         Thread* thread,
+                         RawFunction* function,
+                         RawObject** call_base,
+                         RawObject** call_top,
+                         uint32_t** pc,
+                         RawObject*** FP,
+                         RawObject*** SP);
 
-  bool InvokeBytecode(Thread* thread,
+  bool InvokeCompiled(Thread* thread,
                       RawFunction* function,
                       RawObject** call_base,
                       RawObject** call_top,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index cf590d2..0a8d677 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -936,17 +936,6 @@
   invoke_closure_bytecode_->set_exception_handlers(
       Object::empty_exception_handlers());
 
-  static const KBCInstr invoke_field_instr[2] = {
-      KernelBytecode::Encode(KernelBytecode::kVMInternal_InvokeField),
-      KernelBytecode::Encode(KernelBytecode::kReturnTOS),
-  };
-  *invoke_field_bytecode_ = Bytecode::New(
-      reinterpret_cast<uword>(invoke_field_instr), sizeof(invoke_field_instr),
-      -1, Object::empty_object_pool());
-  invoke_field_bytecode_->set_pc_descriptors(Object::empty_descriptors());
-  invoke_field_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();
@@ -1014,8 +1003,6 @@
   ASSERT(method_extractor_bytecode_->IsBytecode());
   ASSERT(!invoke_closure_bytecode_->IsSmi());
   ASSERT(invoke_closure_bytecode_->IsBytecode());
-  ASSERT(!invoke_field_bytecode_->IsSmi());
-  ASSERT(invoke_field_bytecode_->IsBytecode());
 }
 
 void Object::FinishInit(Isolate* isolate) {
@@ -5738,6 +5725,8 @@
     case RawFunction::kIrregexpFunction:
     case RawFunction::kFfiTrampoline:
       return false;
+    case RawFunction::kInvokeFieldDispatcher:
+      return Class::Handle(zone, Owner()).id() == kClosureCid;
     default:
       return true;
   }
@@ -5761,6 +5750,14 @@
     SetInstructions(StubCode::InterpretCall());
   }
 }
+
+bool Function::HasBytecode() const {
+  return raw_ptr()->bytecode_ != Bytecode::null();
+}
+
+bool Function::HasBytecode(RawFunction* function) {
+  return function->ptr()->bytecode_ != Bytecode::null();
+}
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 bool Function::HasCode(RawFunction* function) {
@@ -15187,8 +15184,6 @@
     return "[Bytecode Stub] VMInternal_MethodExtractor";
   } else if (bytecode.raw() == Object::invoke_closure_bytecode().raw()) {
     return "[Bytecode Stub] VMInternal_InvokeClosure";
-  } else if (bytecode.raw() == Object::invoke_field_bytecode().raw()) {
-    return "[Bytecode Stub] VMInternal_InvokeField";
   }
   return "[unknown stub]";
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3e32cd5..b912fcb 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -401,7 +401,6 @@
   V(Bytecode, implicit_static_getter_bytecode)                                 \
   V(Bytecode, method_extractor_bytecode)                                       \
   V(Bytecode, invoke_closure_bytecode)                                         \
-  V(Bytecode, invoke_field_bytecode)                                           \
   V(Instance, sentinel)                                                        \
   V(Instance, transition_sentinel)                                             \
   V(Instance, unknown_constant)                                                \
@@ -2151,7 +2150,7 @@
   bool HasCode() const;
   static bool HasCode(RawFunction* function);
 #if !defined(DART_PRECOMPILED_RUNTIME)
-  static inline bool HasBytecode(RawFunction* function);
+  static bool HasBytecode(RawFunction* function);
 #endif
 
   static intptr_t code_offset() { return OFFSET_OF(RawFunction, code_); }
@@ -2172,7 +2171,7 @@
   bool IsBytecodeAllowed(Zone* zone) const;
   void AttachBytecode(const Bytecode& bytecode) const;
   RawBytecode* bytecode() const { return raw_ptr()->bytecode_; }
-  inline bool HasBytecode() const;
+  bool HasBytecode() const;
 #endif
 
   virtual intptr_t Hash() const;
@@ -9384,14 +9383,6 @@
 #endif
 }
 
-bool Function::HasBytecode() const {
-  return raw_ptr()->bytecode_ != Bytecode::null();
-}
-
-bool Function::HasBytecode(RawFunction* function) {
-  return function->ptr()->bytecode_ != Bytecode::null();
-}
-
 intptr_t Field::Offset() const {
   ASSERT(is_instance());  // Valid only for dart instance fields.
   intptr_t value = Smi::Value(raw_ptr()->value_.offset_);