[vm/jit] Introduce dynamic invocation forwarders that perform type checking

This forwarders are used at dynamic call-sites and perform type checking
for all non-generic-covariant arguments. This allows to skip the same
type checks in the actual method body.

This yield on average 10% improvement in performance across the body of
benchmarks including dart2js compilation times.

Bug: https://github.com/dart-lang/sdk/issues/33257
Change-Id: If3fc94a2e0a6f496ec0633f0b379d053a54a40ca
Reviewed-on: https://dart-review.googlesource.com/61244
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/observatory/lib/src/elements/function_view.dart b/runtime/observatory/lib/src/elements/function_view.dart
index 93cc57c..c3edda8 100644
--- a/runtime/observatory/lib/src/elements/function_view.dart
+++ b/runtime/observatory/lib/src/elements/function_view.dart
@@ -433,6 +433,8 @@
         return 'tag';
       case M.FunctionKind.signatureFunction:
         return 'signature function';
+      case M.FunctionKind.dynamicInvocationForwarder:
+        return 'dynamic invocation forwarder';
     }
     throw new Exception('Unknown Functionkind ($kind)');
   }
diff --git a/runtime/observatory/lib/src/models/objects/function.dart b/runtime/observatory/lib/src/models/objects/function.dart
index 904038d..e619f5e 100644
--- a/runtime/observatory/lib/src/models/objects/function.dart
+++ b/runtime/observatory/lib/src/models/objects/function.dart
@@ -23,7 +23,8 @@
   native,
   stub,
   tag,
-  signatureFunction
+  signatureFunction,
+  dynamicInvocationForwarder
 }
 
 bool isSyntheticFunction(FunctionKind kind) {
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index ace655c..768262c 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -3112,6 +3112,8 @@
       return M.FunctionKind.tag;
     case 'SignatureFunction':
       return M.FunctionKind.signatureFunction;
+    case 'DynamicInvocationForwarder':
+      return M.FunctionKind.dynamicInvocationForwarder;
   }
   Logger.root.severe('Unrecognized function kind: $value');
   throw new FallThroughError();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index c329ac8..01c88c8 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -38,6 +38,31 @@
              .StartsWith(Symbols::InitPrefix());
 }
 
+// Returns true if the given method can skip type checks for all arguments
+// that are not covariant or generic covariant in its implementation.
+static bool MethodCanSkipTypeChecksForNonCovariantArguments(
+    const Function& method,
+    const ProcedureAttributesMetadata& attrs) {
+  // Dart 2 type system at non-dynamic call sites statically guarantees that
+  // argument values match declarated parameter types for all non-covariant
+  // and non-generic-covariant parameters. The same applies to type parameters
+  // bounds for type parameters of generic functions.
+  // In JIT mode we dynamically generate trampolines (dynamic invocation
+  // forwarders) that perform type checks when arriving to a method from a
+  // dynamic call-site.
+  // In AOT mode we don't dynamically generate such trampolines but
+  // instead rely on a static analysis to discover which methods can
+  // be invoked dynamically.
+  if (method.name() == Symbols::Call().raw()) {
+    // Currently we consider all call methods to be invoked dynamically and
+    // don't mangle their names.
+    // TODO(vegorov) remove this once we also introduce special type checking
+    // entry point for closures.
+    return false;
+  }
+  return !FLAG_precompiled_mode || !attrs.has_dynamic_invocations;
+}
+
 void FunctionNodeHelper::ReadUntilExcluding(Field field) {
   if (field <= next_read_) return;
 
@@ -1690,19 +1715,22 @@
       if (function.IsNonImplicitClosureFunction()) {
         type_check_mode = kTypeCheckAllParameters;
       } else if (function.IsImplicitClosureFunction()) {
-        if (!attrs.has_dynamic_invocations) {
+        if (MethodCanSkipTypeChecksForNonCovariantArguments(
+                Function::Handle(Z, function.parent_function()), attrs)) {
           // This is a tear-off of an instance method that can not be reached
           // from any dynamic invocation. The method would not check any
           // parameters except covariant ones and those annotated with
           // generic-covariant-impl. Which means that we have to check
-          // the rest in the tear-off itself..
-          type_check_mode = kTypeCheckForTearOffOfNonDynamicallyInvokedMethod;
+          // the rest in the tear-off itself.
+          type_check_mode =
+              kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod;
         }
       } else {
         if (function.is_static()) {
           // In static functions we don't check anything.
           type_check_mode = kTypeCheckForStaticFunction;
-        } else if (!attrs.has_dynamic_invocations) {
+        } else if (MethodCanSkipTypeChecksForNonCovariantArguments(function,
+                                                                   attrs)) {
           // If the current function is never a target of a dynamic invocation
           // and this parameter is not marked with generic-covariant-impl
           // (which means that among all super-interfaces no type parameters
@@ -1769,7 +1797,8 @@
             AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)));
         scope_->InsertParameterAt(pos++, result_->setter_value);
 
-        if (is_method && !attrs.has_dynamic_invocations) {
+        if (is_method &&
+            MethodCanSkipTypeChecksForNonCovariantArguments(function, attrs)) {
           FieldHelper field_helper(builder_);
           field_helper.ReadUntilIncluding(FieldHelper::kFlags);
 
@@ -1783,6 +1812,43 @@
       }
       break;
     }
+    case RawFunction::kDynamicInvocationForwarder: {
+      if (builder_->PeekTag() == kField) {
+        ASSERT(Field::IsSetterName(String::Handle(function.name())));
+
+        // Create [this] variable.
+        const Class& klass = Class::Handle(Z, function.Owner());
+        result_->this_variable =
+            MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+                         Symbols::This(), H.GetCanonicalType(klass));
+        scope_->InsertParameterAt(0, result_->this_variable);
+
+        // Create setter value variable.
+        result_->setter_value = MakeVariable(
+            TokenPosition::kNoSource, TokenPosition::kNoSource,
+            Symbols::Value(),
+            AbstractType::ZoneHandle(Z, function.ParameterTypeAt(1)));
+        scope_->InsertParameterAt(1, result_->setter_value);
+      } else {
+        builder_->ReadUntilFunctionNode();
+        function_node_helper.ReadUntilExcluding(
+            FunctionNodeHelper::kPositionalParameters);
+
+        // Create [this] variable.
+        intptr_t pos = 0;
+        Class& klass = Class::Handle(Z, function.Owner());
+        result_->this_variable =
+            MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+                         Symbols::This(), H.GetCanonicalType(klass));
+        scope_->InsertParameterAt(pos++, result_->this_variable);
+
+        // Create all positional and named parameters.
+        AddPositionalAndNamedParameters(
+            pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
+            attrs);
+      }
+      break;
+    }
     case RawFunction::kMethodExtractor: {
       // Add a receiver parameter.  Though it is captured, we emit code to
       // explicitly copy it to a fixed offset in a freshly-allocated context
@@ -2863,7 +2929,7 @@
     variable->set_is_forced_stack();
   }
 
-  const bool needs_covariant_checke_in_method =
+  const bool needs_covariant_check_in_method =
       helper.IsCovariant() ||
       (helper.IsGenericCovariantImpl() && attrs.has_non_this_uses);
 
@@ -2871,8 +2937,8 @@
     case kTypeCheckAllParameters:
       variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
       break;
-    case kTypeCheckForTearOffOfNonDynamicallyInvokedMethod:
-      if (needs_covariant_checke_in_method) {
+    case kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod:
+      if (needs_covariant_check_in_method) {
         // Don't type check covariant parameters - they will be checked by
         // a function we forward to. Their types however are not known.
         variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
@@ -2881,7 +2947,7 @@
       }
       break;
     case kTypeCheckForNonDynamicallyInvokedMethod:
-      if (needs_covariant_checke_in_method) {
+      if (needs_covariant_check_in_method) {
         variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
       } else {
         // Types of non-covariant parameters are guaranteed to match by
@@ -5266,8 +5332,13 @@
 
   const Function& function = parsed_function()->function();
 
-  bool is_setter = function.IsImplicitSetterFunction();
-  bool is_method = !function.IsStaticFunction();
+  // Instead of building a dynamic invocation forwarder that checks argument
+  // type and then invokes original setter we simply generate the type check
+  // and inlined field store. Scope builder takes care of setting correct
+  // type check mode in this case.
+  const bool is_setter = function.IsDynamicInvocationForwader() ||
+                         function.IsImplicitSetterFunction();
+  const bool is_method = !function.IsStaticFunction();
   Field& field = Field::ZoneHandle(
       Z, H.LookupFieldByKernelField(field_helper.canonical_name_));
 
@@ -5277,6 +5348,10 @@
 
   Fragment body(normal_entry);
   if (is_setter) {
+    // We only expect to generate a dynamic invocation forwarder if
+    // the value needs type check.
+    ASSERT(!function.IsDynamicInvocationForwader() ||
+           setter_value->needs_type_check());
     if (is_method) {
       body += LoadLocal(scopes()->this_variable);
     }
@@ -5695,18 +5770,17 @@
       // Tearoffs of static methods needs to perform arguments checks since
       // static methods they forward to don't do it themselves.
       AlternativeReadingScope _(&reader_);
-      body += BuildArgumentTypeChecks();
+      body += BuildArgumentTypeChecks(kCheckAllTypeParameterBounds);
     } else {
       // Check if parent function was annotated with no-dynamic-invocations.
       const ProcedureAttributesMetadata attrs =
           procedure_attributes_metadata_helper_.GetProcedureAttributes(
               parent.kernel_offset());
-      if (!attrs.has_dynamic_invocations) {
+      if (MethodCanSkipTypeChecksForNonCovariantArguments(parent, attrs)) {
         // If it was then we might need to build some checks in the
         // tear-off.
         AlternativeReadingScope _(&reader_);
-        body +=
-            BuildArgumentTypeChecks(kTypeChecksForNoDynamicInvocationsTearOff);
+        body += BuildArgumentTypeChecks(kCheckNonCovariantTypeParameterBounds);
       }
     }
   }
@@ -5813,7 +5887,7 @@
 
   if (function.NeedsArgumentTypeChecks(I)) {
     AlternativeReadingScope _(&reader_);
-    body += BuildArgumentTypeChecks();
+    body += BuildArgumentTypeChecks(kCheckAllTypeParameterBounds);
   }
 
   function_node_helper.ReadUntilExcluding(
@@ -6062,8 +6136,84 @@
                            B->last_used_block_id_, prologue_info);
 }
 
+bool StreamingFlowGraphBuilder::NeedsDynamicInvocationForwarder(
+    const Function& function) {
+  // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
+  // e.g. for type translation.
+  const Class& klass = Class::Handle(zone_, function.Owner());
+  Function& outermost_function = Function::Handle(Z);
+  DiscoverEnclosingElements(Z, function, &outermost_function);
+
+  ActiveClassScope active_class_scope(active_class(), &klass);
+  ActiveMemberScope active_member(active_class(), &outermost_function);
+  ActiveTypeParametersScope active_type_params(active_class(), function, Z);
+
+  SetOffset(function.kernel_offset());
+
+  // Handle setters.
+  if (PeekTag() == kField) {
+    ASSERT(function.IsImplicitSetterFunction());
+    FieldHelper field_helper(this);
+    field_helper.ReadUntilIncluding(FieldHelper::kFlags);
+    return !(field_helper.IsCovariant() ||
+             field_helper.IsGenericCovariantImpl());
+  }
+
+  ReadUntilFunctionNode();
+
+  FunctionNodeHelper function_node_helper(this);
+  function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
+  intptr_t num_type_params = ReadListLength();
+
+  for (intptr_t i = 0; i < num_type_params; ++i) {
+    TypeParameterHelper helper(this);
+    helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
+    AbstractType& bound = T.BuildType();  // read bound
+    helper.Finish();
+
+    if (!bound.IsTopType() && !helper.IsGenericCovariantImpl()) {
+      return true;
+    }
+  }
+  function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
+  function_node_helper.ReadUntilExcluding(
+      FunctionNodeHelper::kPositionalParameters);
+
+  // Positional.
+  const intptr_t num_positional_params = ReadListLength();
+  for (intptr_t i = 0; i < num_positional_params; ++i) {
+    VariableDeclarationHelper helper(this);
+    helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
+    AbstractType& type = T.BuildType();  // read type.
+    helper.SetJustRead(VariableDeclarationHelper::kType);
+    helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
+
+    if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
+        !helper.IsCovariant()) {
+      return true;
+    }
+  }
+
+  // Named.
+  const intptr_t num_named_params = ReadListLength();
+  for (intptr_t i = 0; i < num_named_params; ++i) {
+    VariableDeclarationHelper helper(this);
+    helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
+    AbstractType& type = T.BuildType();  // read type.
+    helper.SetJustRead(VariableDeclarationHelper::kType);
+    helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
+
+    if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
+        !helper.IsCovariant()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
-    TypeChecksToBuild mode /*= kDefaultTypeChecks*/) {
+    TypeChecksToBuild mode) {
   FunctionNodeHelper function_node_helper(this);
   function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
   const Function& dart_function = parsed_function()->function();
@@ -6081,45 +6231,65 @@
     ASSERT(!forwarding_target->IsNull());
   }
 
-  // Type parameters
-  if (mode == kDefaultTypeChecks) {
-    intptr_t num_type_params = ReadListLength();
-    TypeArguments& forwarding_params = TypeArguments::Handle(Z);
-    if (forwarding_target != NULL) {
-      forwarding_params = forwarding_target->type_parameters();
-      ASSERT(forwarding_params.Length() == num_type_params);
-    }
-    TypeParameter& forwarding_param = TypeParameter::Handle(Z);
-    for (intptr_t i = 0; i < num_type_params; ++i) {
-      TypeParameterHelper helper(this);
-      helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
-      String& name = H.DartSymbolObfuscate(helper.name_index_);
-      AbstractType& bound = T.BuildType();                 // read bound
-      helper.Finish();
-
-      if (forwarding_target != NULL) {
-        forwarding_param ^= forwarding_params.TypeAt(i);
-        bound = forwarding_param.bound();
-      }
-
-      if (I->strong() && !bound.IsObjectType() && !bound.IsDynamicType() &&
-          (I->reify_generic_functions() || dart_function.IsFactory())) {
-        TypeParameter& param = TypeParameter::Handle(Z);
-        if (dart_function.IsFactory()) {
-          param ^= TypeArguments::Handle(
-                       Class::Handle(dart_function.Owner()).type_parameters())
-                       .TypeAt(i);
-        } else {
-          param ^=
-              TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
-        }
-        ASSERT(param.IsFinalized());
-        body += CheckTypeArgumentBound(param, bound, name);
-      }
-    }
-    function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
+  intptr_t num_type_params = ReadListLength();
+  TypeArguments& forwarding_params = TypeArguments::Handle(Z);
+  if (forwarding_target != NULL) {
+    forwarding_params = forwarding_target->type_parameters();
+    ASSERT(forwarding_params.Length() == num_type_params);
   }
 
+  const bool has_reified_type_arguments =
+      I->strong() && I->reify_generic_functions();
+
+  TypeParameter& forwarding_param = TypeParameter::Handle(Z);
+  for (intptr_t i = 0; i < num_type_params; ++i) {
+    TypeParameterHelper helper(this);
+    helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
+    String& name = H.DartSymbolObfuscate(helper.name_index_);
+    AbstractType& bound = T.BuildType();  // read bound
+    helper.Finish();
+
+    if (!has_reified_type_arguments) {
+      continue;
+    }
+
+    if (forwarding_target != NULL) {
+      forwarding_param ^= forwarding_params.TypeAt(i);
+      bound = forwarding_param.bound();
+    }
+
+    if (bound.IsTopType()) {
+      continue;
+    }
+
+    switch (mode) {
+      case kCheckAllTypeParameterBounds:
+        break;
+      case kCheckCovariantTypeParameterBounds:
+        if (!helper.IsGenericCovariantImpl()) {
+          continue;
+        }
+        break;
+      case kCheckNonCovariantTypeParameterBounds:
+        if (helper.IsGenericCovariantImpl()) {
+          continue;
+        }
+        break;
+    }
+
+    TypeParameter& param = TypeParameter::Handle(Z);
+    if (dart_function.IsFactory()) {
+      param ^= TypeArguments::Handle(
+                   Class::Handle(dart_function.Owner()).type_parameters())
+                   .TypeAt(i);
+    } else {
+      param ^= TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
+    }
+    ASSERT(param.IsFinalized());
+    body += CheckTypeArgumentBound(param, bound, name);
+  }
+
+  function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
   function_node_helper.ReadUntilExcluding(
       FunctionNodeHelper::kPositionalParameters);
 
@@ -6173,6 +6343,163 @@
   return body;
 }
 
+Fragment StreamingFlowGraphBuilder::PushAllArguments(PushedArguments* pushed) {
+  ASSERT(I->strong());
+
+  FunctionNodeHelper function_node_helper(this);
+  function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
+
+  Fragment body;
+
+  const intptr_t num_type_params = ReadListLength();
+  if (num_type_params > 0) {
+    // Skip type arguments.
+    for (intptr_t i = 0; i < num_type_params; ++i) {
+      TypeParameterHelper helper(this);
+      helper.Finish();
+    }
+
+    if (I->reify_generic_functions()) {
+      body += LoadLocal(parsed_function()->function_type_arguments());
+      body += PushArgument();
+      pushed->type_args_len = num_type_params;
+    }
+  }
+  function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
+  function_node_helper.ReadUntilExcluding(
+      FunctionNodeHelper::kPositionalParameters);
+
+  // Push receiver.
+  body += LoadLocal(scopes()->this_variable);
+  body += PushArgument();
+
+  // Push positional parameters.
+  const intptr_t num_positional_params = ReadListLength();
+  for (intptr_t i = 0; i < num_positional_params; ++i) {
+    // ith variable offset.
+    const intptr_t offset = ReaderOffset();
+    SkipVariableDeclaration();
+
+    LocalVariable* param = LookupVariable(offset + data_program_offset_);
+    body += LoadLocal(param);
+    body += PushArgument();
+  }
+
+  // Push named parameters.
+  const intptr_t num_named_params = ReadListLength();
+  pushed->argument_names = Array::New(num_named_params, Heap::kOld);
+  for (intptr_t i = 0; i < num_named_params; ++i) {
+    // ith variable offset.
+    const intptr_t offset = ReaderOffset();
+    SkipVariableDeclaration();
+
+    LocalVariable* param = LookupVariable(offset + data_program_offset_);
+    pushed->argument_names.SetAt(i, param->name());
+    body += LoadLocal(param);
+    body += PushArgument();
+  }
+
+  pushed->argument_count = num_positional_params + num_named_params + 1;
+
+  return body;
+}
+
+FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfDynamicInvocationForwarder() {
+  // The prologue builder needs the default parameter values.
+  SetupDefaultParameterValues();
+
+  const Function& dart_function = parsed_function()->function();
+  TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
+  PrologueInfo prologue_info(-1, -1);
+  BlockEntryInstr* instruction_cursor =
+      flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info);
+
+  flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
+      *parsed_function(), normal_entry, flow_graph_builder_->osr_id_);
+
+  Fragment body;
+  if (!dart_function.is_native()) {
+    body += flow_graph_builder_->CheckStackOverflowInPrologue(
+        dart_function.token_pos());
+  }
+
+  ASSERT(parsed_function()->node_sequence()->scope()->num_context_variables() ==
+         0);
+
+  FunctionNodeHelper function_node_helper(this);
+  function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
+  const intptr_t type_parameters_offset = ReaderOffset();
+  function_node_helper.ReadUntilExcluding(
+      FunctionNodeHelper::kPositionalParameters);
+  intptr_t first_parameter_offset = -1;
+  {
+    AlternativeReadingScope alt(&reader_);
+    intptr_t list_length = ReadListLength();  // read number of positionals.
+    if (list_length > 0) {
+      first_parameter_offset = ReaderOffset() + data_program_offset_;
+    }
+  }
+  // Current position: About to read list of positionals.
+
+  // Should never build a dynamic invocation forwarder for equality
+  // operator.
+  ASSERT(dart_function.name() != Symbols::EqualOperator().raw());
+
+  // Even if the caller did not pass argument vector we would still
+  // call the target with instantiate-to-bounds type arguments.
+  body += BuildDefaultTypeHandling(dart_function, type_parameters_offset);
+
+  String& name = String::Handle(Z, dart_function.name());
+  name = Function::DemangleDynamicInvocationForwarderName(name);
+  const Class& owner = Class::Handle(Z, dart_function.Owner());
+  const Function& target =
+      Function::ZoneHandle(Z, owner.LookupDynamicFunction(name));
+  ASSERT(!target.IsNull());
+
+  // Build argument type checks that complement those that are emitted in the
+  // target.
+  {
+    AlternativeReadingScope alt(&reader_);
+    SetOffset(type_parameters_offset);
+    body += BuildArgumentTypeChecks(kCheckNonCovariantTypeParameterBounds);
+  }
+
+  // Push all arguments and invoke the original method.
+  PushedArguments pushed = {0, 0, Array::ZoneHandle(Z)};
+  {
+    AlternativeReadingScope alt(&reader_);
+    SetOffset(type_parameters_offset);
+    body += PushAllArguments(&pushed);
+  }
+  body += StaticCall(TokenPosition::kNoSource, target, pushed.argument_count,
+                     pushed.argument_names, ICData::kNoRebind, nullptr,
+                     pushed.type_args_len);
+
+  // Some IL optimization passes assume that result of operator []= invocation
+  // is never used, so we drop it and replace with an explicit null constant.
+  if (name.raw() == Symbols::AssignIndexToken().raw()) {
+    body += Drop();
+    body += NullConstant();
+  }
+
+  body += Return(TokenPosition::kNoSource);
+
+  instruction_cursor->LinkTo(body.entry);
+
+  GraphEntryInstr* graph_entry = flow_graph_builder_->graph_entry_;
+  // When compiling for OSR, use a depth first search to find the OSR
+  // entry and make graph entry jump to it instead of normal entry.
+  // Catch entries are always considered reachable, even if they
+  // become unreachable after OSR.
+  if (flow_graph_builder_->osr_id_ != Compiler::kNoOSRDeoptId) {
+    graph_entry->RelinkToOsrEntry(Z,
+                                  flow_graph_builder_->last_used_block_id_ + 1);
+  }
+  return new (Z)
+      FlowGraph(*parsed_function(), graph_entry,
+                flow_graph_builder_->last_used_block_id_, prologue_info);
+}
+
 FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) {
   // The prologue builder needs the default parameter values.
   SetupDefaultParameterValues();
@@ -6288,17 +6615,25 @@
     body = Fragment(body.entry, non_null_entry);
   }
 
-  // If we run in checked mode or strong mode, we have to check the type of the
-  // passed arguments.
+  // If we run in checked mode or strong mode, we have to check the type of
+  // the passed arguments.
   if (dart_function.NeedsArgumentTypeChecks(I)) {
+    // Check if parent function was annotated with no-dynamic-invocations.
+    const ProcedureAttributesMetadata attrs =
+        procedure_attributes_metadata_helper_.GetProcedureAttributes(
+            dart_function.kernel_offset());
+
     AlternativeReadingScope _(&reader_);
     SetOffset(type_parameters_offset);
-    body += BuildArgumentTypeChecks();
+    body += BuildArgumentTypeChecks(
+        MethodCanSkipTypeChecksForNonCovariantArguments(dart_function, attrs)
+            ? kCheckCovariantTypeParameterBounds
+            : kCheckAllTypeParameterBounds);
   }
 
   function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
 
-  bool has_body = ReadTag() == kSomething;  // read first part of body.
+  const bool has_body = ReadTag() == kSomething;  // read first part of body.
 
   if (dart_function.is_native()) {
     body += flow_graph_builder_->NativeFunctionBody(first_parameter_offset,
@@ -6511,9 +6846,10 @@
                 flow_graph_builder_->last_used_block_id_, prologue_info);
 }
 
-FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
+FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
   ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
   const Function& function = parsed_function()->function();
+  const intptr_t kernel_offset = function.kernel_offset();
 
   // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
   // e.g. for type translation.
@@ -6585,6 +6921,13 @@
                  ? BuildGraphOfFieldInitializer()
                  : BuildGraphOfFieldAccessor(scopes()->setter_value);
     }
+    case RawFunction::kDynamicInvocationForwarder:
+      if (PeekTag() == kField) {
+        return BuildGraphOfFieldAccessor(scopes()->setter_value);
+      } else {
+        ReadUntilFunctionNode(parsed_function());
+        return BuildGraphOfDynamicInvocationForwarder();
+      }
     case RawFunction::kMethodExtractor:
       return flow_graph_builder_->BuildGraphOfMethodExtractor(function);
     case RawFunction::kNoSuchMethodDispatcher:
@@ -8262,8 +8605,15 @@
   } else {
     const intptr_t kTypeArgsLen = 0;
     const intptr_t kNumArgsChecked = 1;
+
+    const String* mangled_name = &setter_name;
+    if (!FLAG_precompiled_mode && I->strong() && H.IsRoot(itarget_name)) {
+      mangled_name = &String::ZoneHandle(
+          Z, Function::CreateDynamicInvocationForwarderName(setter_name));
+    }
+
     instructions +=
-        InstanceCall(position, setter_name, Token::kSET, kTypeArgsLen, 2,
+        InstanceCall(position, *mangled_name, Token::kSET, kTypeArgsLen, 2,
                      Array::null_array(), kNumArgsChecked, *interface_target,
                      /* result_type = */ NULL);
   }
@@ -8820,10 +9170,22 @@
                                argument_names, ICData::kNoRebind, &result_type,
                                type_args_len);
   } else {
+    const String* mangled_name = &name;
+    // Do not mangle == or call:
+    //   * operator == takes an Object so its either not checked or checked
+    //     at the entry because the parameter is marked covariant, neither of
+    //     those cases require a dynamic invocation forwarder;
+    //   * we assume that all closures are entered in a checked way.
+    if (!FLAG_precompiled_mode && I->strong() &&
+        (name.raw() != Symbols::EqualOperator().raw()) &&
+        (name.raw() != Symbols::Call().raw()) && H.IsRoot(itarget_name)) {
+      mangled_name = &String::ZoneHandle(
+          Z, Function::CreateDynamicInvocationForwarderName(name));
+    }
     instructions +=
-        InstanceCall(position, name, token_kind, type_args_len, argument_count,
-                     argument_names, checked_argument_count, *interface_target,
-                     &result_type);
+        InstanceCall(position, *mangled_name, token_kind, type_args_len,
+                     argument_count, argument_names, checked_argument_count,
+                     *interface_target, &result_type);
   }
 
   // Drop temporaries preserving result on the top of the stack.
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index a28476b..5b1c7dd 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -115,6 +115,10 @@
 
   void Finish() { ReadUntilExcluding(kEnd); }
 
+  bool IsGenericCovariantImpl() {
+    return (flags_ & kIsGenericCovariantImpl) != 0;
+  }
+
   TokenPosition position_;
   uint8_t flags_;
   StringIndex name_index_;
@@ -814,8 +818,8 @@
 
     // Only parameters *not* marked as covariant or generic-covariant-impl will
     // be checked. The rest would be checked in the method itself.
-    // Inverse of kTypeCheckOnlyGenericCovariantImplParameters.
-    kTypeCheckForTearOffOfNonDynamicallyInvokedMethod,
+    // Inverse of kTypeCheckForNonDynamicallyInvokedMethod.
+    kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
 
     // No parameters will be checked.
     kTypeCheckForStaticFunction,
@@ -1273,7 +1277,9 @@
 
   virtual ~StreamingFlowGraphBuilder() {}
 
-  FlowGraph* BuildGraph(intptr_t kernel_offset);
+  bool NeedsDynamicInvocationForwarder(const Function& function);
+
+  FlowGraph* BuildGraph();
 
   void ReportUnexpectedTag(const char* variant, Tag tag) override;
 
@@ -1296,8 +1302,6 @@
   // it crosses a procedure node for a concrete forwarding stub.
   void ReadUntilFunctionNode(ParsedFunction* set_forwarding_stub = NULL);
 
-  enum DispatchCategory { Interface, ViaThis, Closure, DynamicDispatch };
-
  private:
   void LoadAndSetupTypeParameters(ActiveClass* active_class,
                                   const Object& set_on,
@@ -1320,6 +1324,7 @@
   Fragment BuildInitializers(const Class& parent_class);
   FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
   FlowGraph* BuildGraphOfFunction(bool constructor);
+  FlowGraph* BuildGraphOfDynamicInvocationForwarder();
   FlowGraph* BuildGraphOfNoSuchMethodForwarder(
       const Function& function,
       bool is_implicit_closure_function,
@@ -1418,15 +1423,23 @@
                         const InferredTypeMetadata* result_type = NULL);
 
   enum TypeChecksToBuild {
-    kDefaultTypeChecks,
-    kTypeChecksForNoDynamicInvocationsTearOff
+    kCheckAllTypeParameterBounds,
+    kCheckNonCovariantTypeParameterBounds,
+    kCheckCovariantTypeParameterBounds,
   };
 
   // Does not move the cursor.
   Fragment BuildDefaultTypeHandling(const Function& function,
                                     intptr_t type_parameters_offset);
 
-  Fragment BuildArgumentTypeChecks(TypeChecksToBuild mode = kDefaultTypeChecks);
+  struct PushedArguments {
+    intptr_t type_args_len;
+    intptr_t argument_count;
+    Array& argument_names;
+  };
+  Fragment PushAllArguments(PushedArguments* pushed);
+
+  Fragment BuildArgumentTypeChecks(TypeChecksToBuild mode);
 
   Fragment ThrowException(TokenPosition position);
   Fragment BooleanNegate();
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index f019342..1bb584e 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -2061,6 +2061,28 @@
   }
 }
 
+bool FlowGraphBuilder::NeedsDynamicInvocationForwarder(
+    const Function& function) {
+  ASSERT(Isolate::Current()->strong());
+
+  Thread* thread = Thread::Current();
+  Zone* zone_ = thread->zone();
+
+  TranslationHelper helper(thread);
+  Script& script = Script::Handle(Z, function.script());
+  helper.InitFromScript(script);
+
+  const Class& owner_class = Class::Handle(Z, function.Owner());
+  ActiveClass active_class;
+  ActiveClassScope active_class_scope(&active_class, &owner_class);
+
+  StreamingFlowGraphBuilder streaming_flow_graph_builder(
+      &helper, script, Z, ExternalTypedData::Handle(Z, function.KernelData()),
+      function.KernelDataProgramOffset(), &active_class);
+
+  return streaming_flow_graph_builder.NeedsDynamicInvocationForwarder(function);
+}
+
 FlowGraph* FlowGraphBuilder::BuildGraph() {
   const Function& function = parsed_function_->function();
 
@@ -2080,8 +2102,7 @@
       this, ExternalTypedData::Handle(Z, function.KernelData()),
       function.KernelDataProgramOffset());
   streaming_flow_graph_builder_ = &streaming_flow_graph_builder;
-  FlowGraph* result =
-      streaming_flow_graph_builder_->BuildGraph(function.kernel_offset());
+  FlowGraph* result = streaming_flow_graph_builder_->BuildGraph();
   streaming_flow_graph_builder_ = NULL;
   return result;
 }
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index e0671a2..9bb30e5 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -217,6 +217,7 @@
            function_kind == RawFunction::kGetterFunction ||
            function_kind == RawFunction::kSetterFunction ||
            function_kind == RawFunction::kMethodExtractor ||
+           function_kind == RawFunction::kDynamicInvocationForwarder ||
            member->IsFactory();
   }
 
@@ -705,6 +706,12 @@
 
   FlowGraph* BuildGraph();
 
+  // Returns true if the given function needs dynamic invocation forwarder:
+  // that is if any of the arguments require checking on the dynamic
+  // call-site: if function has no parameters or has only covariant parameters
+  // as such function already checks all of its parameters.
+  static bool NeedsDynamicInvocationForwarder(const Function& function);
+
  private:
   BlockEntryInstr* BuildPrologue(TargetEntryInstr* normal_entry,
                                  PrologueInfo* prologue_info);
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 7097f5a..974c447 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -687,6 +687,13 @@
     isolate_flags_ = IsServiceIsolateBit::update(value, isolate_flags_);
   }
 
+  bool is_kernel_isolate() const {
+    return IsKernelIsolateBit::decode(isolate_flags_);
+  }
+  void set_is_kernel_isolate(bool value) {
+    isolate_flags_ = IsKernelIsolateBit::update(value, isolate_flags_);
+  }
+
   bool should_load_vmservice() const {
     return ShouldLoadVmServiceBit::decode(isolate_flags_);
   }
@@ -844,6 +851,7 @@
   V(ErrorsFatal)                                                               \
   V(IsRunnable)                                                                \
   V(IsServiceIsolate)                                                          \
+  V(IsKernelIsolate)                                                           \
   V(CompilationAllowed)                                                        \
   V(AllClassesFinalized)                                                       \
   V(RemappingCids)                                                             \
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index b5efb44..4f9a2af 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -265,6 +265,9 @@
 
 void KernelIsolate::SetKernelIsolate(Isolate* isolate) {
   MonitorLocker ml(monitor_);
+  if (isolate != nullptr) {
+    isolate->set_is_kernel_isolate(true);
+  }
   isolate_ = isolate;
 }
 
diff --git a/runtime/vm/log.cc b/runtime/vm/log.cc
index 06a4263..83b99b2 100644
--- a/runtime/vm/log.cc
+++ b/runtime/vm/log.cc
@@ -115,7 +115,7 @@
 
 bool Log::ShouldLogForIsolate(const Isolate* isolate) {
   if (FLAG_isolate_log_filter == NULL) {
-    if (isolate->is_service_isolate()) {
+    if (isolate->is_service_isolate() || isolate->is_kernel_isolate()) {
       // By default, do not log for the service isolate.
       return false;
     }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1aa4b52..b5dc110 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2714,27 +2714,54 @@
   set_next_field_offset(offset);
 }
 
+struct InvocationDispatcherCacheLayout {
+  enum { kNameIndex = 0, kArgsDescIndex, kFunctionIndex, kEntrySize };
+};
+
+void Class::AddInvocationDispatcher(const String& target_name,
+                                    const Array& args_desc,
+                                    const Function& dispatcher) const {
+  // Search for a free entry.
+  Array& cache = Array::Handle(invocation_dispatcher_cache());
+  intptr_t i = 0;
+  while (i < cache.Length() && cache.At(i) != Object::null()) {
+    i += InvocationDispatcherCacheLayout::kEntrySize;
+  }
+
+  if (i == cache.Length()) {
+    // Allocate new larger cache.
+    intptr_t new_len =
+        (cache.Length() == 0)
+            ? static_cast<intptr_t>(InvocationDispatcherCacheLayout::kEntrySize)
+            : cache.Length() * 2;
+    cache ^= Array::Grow(cache, new_len);
+    set_invocation_dispatcher_cache(cache);
+  }
+  cache.SetAt(i + InvocationDispatcherCacheLayout::kNameIndex, target_name);
+  cache.SetAt(i + InvocationDispatcherCacheLayout::kArgsDescIndex, args_desc);
+  cache.SetAt(i + InvocationDispatcherCacheLayout::kFunctionIndex, dispatcher);
+}
+
 RawFunction* Class::GetInvocationDispatcher(const String& target_name,
                                             const Array& args_desc,
                                             RawFunction::Kind kind,
                                             bool create_if_absent) const {
-  enum { kNameIndex = 0, kArgsDescIndex, kFunctionIndex, kEntrySize };
-
   ASSERT(kind == RawFunction::kNoSuchMethodDispatcher ||
-         kind == RawFunction::kInvokeFieldDispatcher);
+         kind == RawFunction::kInvokeFieldDispatcher ||
+         kind == RawFunction::kDynamicInvocationForwarder);
   Function& dispatcher = Function::Handle();
   Array& cache = Array::Handle(invocation_dispatcher_cache());
   ASSERT(!cache.IsNull());
   String& name = String::Handle();
   Array& desc = Array::Handle();
   intptr_t i = 0;
-  for (; i < cache.Length(); i += kEntrySize) {
-    name ^= cache.At(i + kNameIndex);
+  for (; i < cache.Length(); i += InvocationDispatcherCacheLayout::kEntrySize) {
+    name ^= cache.At(i + InvocationDispatcherCacheLayout::kNameIndex);
     if (name.IsNull()) break;  // Reached last entry.
     if (!name.Equals(target_name)) continue;
-    desc ^= cache.At(i + kArgsDescIndex);
+    desc ^= cache.At(i + InvocationDispatcherCacheLayout::kArgsDescIndex);
     if (desc.raw() != args_desc.raw()) continue;
-    dispatcher ^= cache.At(i + kFunctionIndex);
+    dispatcher ^= cache.At(i + InvocationDispatcherCacheLayout::kFunctionIndex);
     if (dispatcher.kind() == kind) {
       // Found match.
       ASSERT(dispatcher.IsFunction());
@@ -2743,18 +2770,8 @@
   }
 
   if (dispatcher.IsNull() && create_if_absent) {
-    if (i == cache.Length()) {
-      // Allocate new larger cache.
-      intptr_t new_len = (cache.Length() == 0)
-                             ? static_cast<intptr_t>(kEntrySize)
-                             : cache.Length() * 2;
-      cache ^= Array::Grow(cache, new_len);
-      set_invocation_dispatcher_cache(cache);
-    }
     dispatcher ^= CreateInvocationDispatcher(target_name, args_desc, kind);
-    cache.SetAt(i + kNameIndex, target_name);
-    cache.SetAt(i + kArgsDescIndex, args_desc);
-    cache.SetAt(i + kFunctionIndex, dispatcher);
+    AddInvocationDispatcher(target_name, args_desc, dispatcher);
   }
   return dispatcher.raw();
 }
@@ -2874,6 +2891,81 @@
   return result.raw();
 }
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+RawFunction* Function::CreateDynamicInvocationForwarder(
+    const String& mangled_name) const {
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+
+  Function& forwarder = Function::Handle(zone);
+  forwarder ^= Object::Clone(*this, Heap::kOld);
+
+  forwarder.set_name(mangled_name);
+  forwarder.set_kind(RawFunction::kDynamicInvocationForwarder);
+  forwarder.set_is_debuggable(false);
+
+  // TODO(vegorov) for error reporting reasons it is better to make this
+  // function visible and instead use a TailCall to invoke the target.
+  // Our TailCall instruction is not ready for such usage though it
+  // blocks inlining and can't take Function-s only Code objects.
+  forwarder.set_is_visible(false);
+
+  forwarder.ClearICDataArray();
+  forwarder.ClearCode();
+  forwarder.set_usage_counter(0);
+  forwarder.set_deoptimization_counter(0);
+  forwarder.set_optimized_instruction_count(0);
+  forwarder.set_inlining_depth(0);
+  forwarder.set_optimized_call_site_count(0);
+  forwarder.set_kernel_offset(kernel_offset());
+
+  return forwarder.raw();
+}
+
+bool Function::IsDynamicInvocationForwaderName(const String& name) {
+  return name.StartsWith(Symbols::DynamicPrefix());
+}
+
+RawString* Function::CreateDynamicInvocationForwarderName(const String& name) {
+  return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
+}
+
+RawString* Function::DemangleDynamicInvocationForwarderName(
+    const String& name) {
+  const intptr_t kDynamicPrefixLength = 4;  // "dyn:"
+  ASSERT(Symbols::DynamicPrefix().Length() == kDynamicPrefixLength);
+  return Symbols::New(Thread::Current(), name, kDynamicPrefixLength,
+                      name.Length() - kDynamicPrefixLength);
+}
+
+RawFunction* Function::GetDynamicInvocationForwarder(
+    const String& mangled_name,
+    bool allow_add /* = true */) const {
+  ASSERT(IsDynamicInvocationForwaderName(mangled_name));
+  const Class& owner = Class::Handle(Owner());
+  Function& result = Function::Handle(owner.GetInvocationDispatcher(
+      mangled_name, Array::null_array(),
+      RawFunction::kDynamicInvocationForwarder, /*create_if_absent=*/false));
+
+  if (!result.IsNull()) {
+    return result.raw();
+  }
+
+  // Check if function actually needs a dynamic invocation forwarder.
+  if (!kernel::FlowGraphBuilder::NeedsDynamicInvocationForwarder(*this)) {
+    result = raw();
+  } else if (allow_add) {
+    result = CreateDynamicInvocationForwarder(mangled_name);
+  }
+
+  if (allow_add) {
+    owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
+  }
+
+  return result.raw();
+}
+#endif
+
 bool AbstractType::InstantiateAndTestSubtype(
     AbstractType* subtype,
     AbstractType* supertype,
@@ -6042,6 +6134,8 @@
     case RawFunction::kIrregexpFunction:
       return "IrregexpFunction";
       break;
+    case RawFunction::kDynamicInvocationForwarder:
+      return "DynamicInvocationForwarder";
     default:
       UNREACHABLE();
       return NULL;
@@ -7128,7 +7222,6 @@
   clone.set_optimized_instruction_count(0);
   clone.set_inlining_depth(0);
   clone.set_optimized_call_site_count(0);
-  clone.set_kernel_offset(kernel_offset());
 
   if (new_owner.NumTypeParameters() > 0) {
     // Adjust uninstantiated types to refer to type parameters of the new owner.
@@ -7969,6 +8062,9 @@
     case RawFunction::kNoSuchMethodDispatcher:
       kind_str = " no-such-method-dispatcher";
       break;
+    case RawFunction::kDynamicInvocationForwarder:
+      kind_str = " dynamic-invocation-forwader";
+      break;
     case RawFunction::kInvokeFieldDispatcher:
       kind_str = "invoke-field-dispatcher";
       break;
@@ -13694,8 +13790,18 @@
   const String& name = String::Handle(target_name());
   const Class& smi_class = Class::Handle(Smi::Class());
   Zone* zone = Thread::Current()->zone();
-  const Function& smi_op_target =
+  Function& smi_op_target =
       Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  if (smi_op_target.IsNull() &&
+      Function::IsDynamicInvocationForwaderName(name)) {
+    const String& demangled =
+        String::Handle(Function::DemangleDynamicInvocationForwarderName(name));
+    smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
+  }
+#endif
+
   if (NumberOfChecksIs(0)) {
     GrowableArray<intptr_t> class_ids(2);
     class_ids.Add(kSmiCid);
@@ -13753,6 +13859,13 @@
 }
 
 bool ICData::ValidateInterceptor(const Function& target) const {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  const String& name = String::Handle(target_name());
+  if (Function::IsDynamicInvocationForwaderName(name)) {
+    return Function::DemangleDynamicInvocationForwarderName(name) ==
+           target.name();
+  }
+#endif
   ObjectStore* store = Isolate::Current()->object_store();
   ASSERT((target.raw() == store->simple_instance_of_true_function()) ||
          (target.raw() == store->simple_instance_of_false_function()));
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 513d91c..23c4988 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1442,6 +1442,10 @@
   void CheckReload(const Class& replacement,
                    IsolateReloadContext* context) const;
 
+  void AddInvocationDispatcher(const String& target_name,
+                               const Array& args_desc,
+                               const Function& dispatcher) const;
+
  private:
   bool CanReloadFinalized(const Class& replacement,
                           IsolateReloadContext* context) const;
@@ -2302,6 +2306,10 @@
     return kind() == RawFunction::kInvokeFieldDispatcher;
   }
 
+  bool IsDynamicInvocationForwader() const {
+    return kind() == RawFunction::kDynamicInvocationForwarder;
+  }
+
   bool IsImplicitGetterOrSetter() const {
     return kind() == RawFunction::kImplicitGetter ||
            kind() == RawFunction::kImplicitSetter ||
@@ -2367,6 +2375,7 @@
       case RawFunction::kMethodExtractor:
       case RawFunction::kNoSuchMethodDispatcher:
       case RawFunction::kInvokeFieldDispatcher:
+      case RawFunction::kDynamicInvocationForwarder:
         return true;
       case RawFunction::kClosureFunction:
       case RawFunction::kImplicitClosureFunction:
@@ -2400,6 +2409,7 @@
       case RawFunction::kMethodExtractor:
       case RawFunction::kNoSuchMethodDispatcher:
       case RawFunction::kInvokeFieldDispatcher:
+      case RawFunction::kDynamicInvocationForwarder:
         return false;
       default:
         UNREACHABLE();
@@ -2609,6 +2619,7 @@
       case RawFunction::kImplicitSetter:
       case RawFunction::kNoSuchMethodDispatcher:
       case RawFunction::kInvokeFieldDispatcher:
+      case RawFunction::kDynamicInvocationForwarder:
         return true;
       default:
         return false;
@@ -2770,6 +2781,17 @@
   RawFunction* CreateMethodExtractor(const String& getter_name) const;
   RawFunction* GetMethodExtractor(const String& getter_name) const;
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  static bool IsDynamicInvocationForwaderName(const String& name);
+  static RawString* DemangleDynamicInvocationForwarderName(const String& name);
+  static RawString* CreateDynamicInvocationForwarderName(const String& name);
+
+  RawFunction* CreateDynamicInvocationForwarder(
+      const String& mangled_name) const;
+  RawFunction* GetDynamicInvocationForwarder(const String& mangled_name,
+                                             bool allow_add = true) const;
+#endif
+
   // Allocate new function object, clone values from this function. The
   // owner of the clone is new_owner.
   RawFunction* Clone(const Class& new_owner) const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 1fd9696..5d7a7f0 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -867,6 +867,9 @@
     kNoSuchMethodDispatcher,  // invokes noSuchMethod.
     kInvokeFieldDispatcher,   // invokes a field as a closure.
     kIrregexpFunction,  // represents a generated irregexp matcher function.
+    kDynamicInvocationForwarder,  // represents forwarder which performs type
+                                  // checks for arguments of a dynamic
+                                  // invocation.
   };
 
   enum AsyncModifier {
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index b0238ad..abb9130 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -69,16 +69,30 @@
     THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
               String::Handle(zone, cls.Name()).ToCString());
   }
+  Function& function = Function::Handle(zone);
+
+  String& demangled = String::Handle(zone);
 
   const bool is_getter = Field::IsGetterName(function_name);
-  String& field_name = String::Handle(zone);
   if (is_getter) {
-    field_name ^= Field::NameFromGetter(function_name);
+    demangled ^= Field::NameFromGetter(function_name);
   }
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  if (Function::IsDynamicInvocationForwaderName(function_name)) {
+    ASSERT(!FLAG_precompiled_mode);
+    demangled ^=
+        Function::DemangleDynamicInvocationForwarderName(function_name);
+    function =
+        ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
+    return function.IsNull() ? function.raw()
+                             : function.GetDynamicInvocationForwarder(
+                                   function_name, allow_add);
+  }
+#endif
+
   // Now look for an instance function whose name matches function_name
   // in the class.
-  Function& function = Function::Handle(zone);
   while (!cls.IsNull()) {
     function ^= cls.LookupDynamicFunction(function_name);
     if (!function.IsNull()) {
@@ -87,7 +101,7 @@
     // Getter invocation might actually be a method extraction.
     if (FLAG_lazy_dispatchers) {
       if (is_getter && function.IsNull()) {
-        function ^= cls.LookupDynamicFunction(field_name);
+        function ^= cls.LookupDynamicFunction(demangled);
         if (!function.IsNull() && allow_add) {
           // We were looking for the getter but found a method with the same
           // name. Create a method extractor and return it.
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 4ffa330..38d5fa1 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1024,6 +1024,16 @@
                                    const String& target_name) {
   const Class& receiver_class = Class::Handle(receiver.clazz());
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  // Handle noSuchMethod for dyn:methodName by getting a noSuchMethod dispatcher
+  // (or a call-through getter for methodName).
+  if (Function::IsDynamicInvocationForwaderName(target_name)) {
+    const String& demangled = String::Handle(
+        Function::DemangleDynamicInvocationForwarderName(target_name));
+    return InlineCacheMissHelper(receiver, args_descriptor, demangled);
+  }
+#endif
+
   Function& result = Function::Handle();
   if (!ResolveCallThroughGetter(receiver, receiver_class, target_name,
                                 args_descriptor, &result)) {
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 50e5b36..d901830 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -367,6 +367,7 @@
   V(GetterPrefix, "get:")                                                      \
   V(SetterPrefix, "set:")                                                      \
   V(InitPrefix, "init:")                                                       \
+  V(DynamicPrefix, "dyn:")                                                     \
   V(Index, "index")                                                            \
   V(DartScheme, "dart:")                                                       \
   V(DartSchemePrivate, "dart:_")                                               \