[vm/compiler] Inline implicit setters in call specializer in strong mode.

Previously we relegated this to the inliner which interfers with inlining
heuristics.

Additionally avoid emitting assert assignable when specializing assignments
on the receiver.

This improves the following microbenchmark by a factor of 3:

int sumList(List<A> l) {
  var sum = 0;
  for (var obj in l) {  // this would generate AssertAssignable for
    sum += obj.field;   // _ListIterator._current
  }
  return sum;
}

Bug: https://github.com/dart-lang/sdk/issues/33257
Change-Id: Ib2fff8be7c953981bdf5967baf7fcd5f6f648dcf
Reviewed-on: https://dart-review.googlesource.com/64842
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index 6b3556b..e6b1481 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -916,12 +916,6 @@
                                               const ICData& unary_ic_data) {
   ASSERT(!unary_ic_data.NumberOfChecksIs(0) &&
          (unary_ic_data.NumArgsTested() == 1));
-  if (I->argument_type_checks()) {
-    // Checked mode setters are inlined like normal methods by conventional
-    // inlining.
-    return false;
-  }
-
   ASSERT(instr->HasICData());
   if (unary_ic_data.NumberOfChecksIs(0)) {
     // No type feedback collected.
@@ -940,8 +934,11 @@
     return false;
   }
   // Inline implicit instance setter.
-  const String& field_name =
-      String::Handle(Z, Field::NameFromSetter(instr->function_name()));
+  String& field_name = String::Handle(Z, instr->function_name().raw());
+  if (Function::IsDynamicInvocationForwaderName(field_name)) {
+    field_name = Function::DemangleDynamicInvocationForwarderName(field_name);
+  }
+  field_name = Field::NameFromSetter(field_name);
   const Field& field = Field::ZoneHandle(Z, GetField(class_id, field_name));
   ASSERT(!field.IsNull());
 
@@ -974,6 +971,62 @@
     }
   }
 
+  // Build an AssertAssignable if necessary.
+  if (I->argument_type_checks()) {
+    const AbstractType& dst_type =
+        AbstractType::ZoneHandle(zone(), field.type());
+
+    // Compute if we need to type check the value. Always type check if
+    // not in strong mode or if at a dynamic invocation.
+    bool needs_check = true;
+    if (I->strong() && !instr->interface_target().IsNull() &&
+        (field.kernel_offset() >= 0)) {
+      bool is_covariant = false;
+      bool is_generic_covariant = false;
+      field.GetCovarianceAttributes(&is_covariant, &is_generic_covariant);
+
+      if (is_covariant) {
+        // Always type check covariant fields.
+        needs_check = true;
+      } else if (is_generic_covariant) {
+        // If field is generic covariant then we don't need to check it
+        // if we know that actual type arguments match static type arguments
+        // e.g. if this is an invocation on this (an instance we are storing
+        // into is also a receiver of a surrounding method).
+        needs_check = !flow_graph_->IsReceiver(instr->ArgumentAt(0));
+      } else {
+        // The rest of the stores are checked statically (we are not at
+        // a dynamic invocation).
+        needs_check = false;
+      }
+    }
+
+    if (needs_check) {
+      Definition* instantiator_type_args = flow_graph_->constant_null();
+      Definition* function_type_args = flow_graph_->constant_null();
+      if (!dst_type.IsInstantiated()) {
+        const Class& owner = Class::Handle(Z, field.Owner());
+        if (owner.NumTypeArguments() > 0) {
+          instantiator_type_args = new (Z) LoadFieldInstr(
+              new (Z) Value(instr->ArgumentAt(0)),
+              NativeFieldDesc::GetTypeArgumentsFieldFor(zone(), owner),
+              instr->token_pos());
+          InsertBefore(instr, instantiator_type_args, instr->env(),
+                       FlowGraph::kValue);
+        }
+      }
+
+      InsertBefore(
+          instr,
+          new (Z) AssertAssignableInstr(
+              instr->token_pos(), new (Z) Value(instr->ArgumentAt(1)),
+              new (Z) Value(instantiator_type_args),
+              new (Z) Value(function_type_args), dst_type,
+              String::ZoneHandle(zone(), field.name()), instr->deopt_id()),
+          instr->env(), FlowGraph::kEffect);
+    }
+  }
+
   // Field guard was detached.
   ASSERT(instr->FirstArgIndex() == 0);
   StoreInstanceFieldInstr* store = new (Z)
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 0086c71..057eff5 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8316,7 +8316,11 @@
 
 RawExternalTypedData* Field::KernelData() const {
   const Object& obj = Object::Handle(this->raw_ptr()->owner_);
-  if (obj.IsClass()) {
+  // During background JIT compilation field objects are copied
+  // and copy points to the original field via the owner field.
+  if (obj.IsField()) {
+    return Field::Cast(obj).KernelData();
+  } else if (obj.IsClass()) {
     Library& library = Library::Handle(Class::Cast(obj).library());
     return library.kernel_data();
   }
@@ -8326,7 +8330,11 @@
 
 intptr_t Field::KernelDataProgramOffset() const {
   const Object& obj = Object::Handle(raw_ptr()->owner_);
-  if (obj.IsClass()) {
+  // During background JIT compilation field objects are copied
+  // and copy points to the original field via the owner field.
+  if (obj.IsField()) {
+    return Field::Cast(obj).KernelDataProgramOffset();
+  } else if (obj.IsClass()) {
     Library& lib = Library::Handle(Class::Cast(obj).library());
     return lib.kernel_offset();
   }
@@ -8334,6 +8342,27 @@
   return PatchClass::Cast(obj).library_kernel_offset();
 }
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+void Field::GetCovarianceAttributes(bool* is_covariant,
+                                    bool* is_generic_covariant) const {
+  Thread* thread = Thread::Current();
+  Zone* zone = Thread::Current()->zone();
+  auto& script = Script::Handle(zone, Script());
+
+  kernel::TranslationHelper translation_helper(thread);
+  translation_helper.InitFromScript(script);
+
+  kernel::KernelReaderHelper kernel_reader_helper(
+      zone, &translation_helper, script,
+      ExternalTypedData::Handle(zone, KernelData()), KernelDataProgramOffset());
+  kernel_reader_helper.SetOffset(kernel_offset());
+  kernel::FieldHelper field_helper(&kernel_reader_helper);
+  field_helper.ReadUntilIncluding(kernel::FieldHelper::kFlags);
+  *is_covariant = field_helper.IsCovariant();
+  *is_generic_covariant = field_helper.IsGenericCovariantImpl();
+}
+#endif
+
 // Called at finalization time
 void Field::SetFieldType(const AbstractType& value) const {
   ASSERT(Thread::Current()->IsMutatorThread());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index fc1d640a..3a1b5cb 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -3177,6 +3177,11 @@
 
   intptr_t KernelDataProgramOffset() const;
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  void GetCovarianceAttributes(bool* is_covariant,
+                               bool* is_generic_covariant) const;
+#endif
+
   inline intptr_t Offset() const;
   // Called during class finalization.
   inline void SetOffset(intptr_t offset_in_bytes) const;