[dart2js] Avoid checks on non-tear-off static methods

Change-Id: I279481775d35a17844d912f563b8696435a787cd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106422
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index b3a666b..257c019 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -45,6 +45,7 @@
 import '../tracer.dart';
 import '../universe/call_structure.dart';
 import '../universe/feature.dart';
+import '../universe/member_usage.dart' show MemberAccess;
 import '../universe/selector.dart';
 import '../universe/side_effects.dart' show SideEffects;
 import '../universe/target_checks.dart' show TargetChecks;
@@ -1197,7 +1198,7 @@
   /// that no corresponding ir.Node actually exists for it. We just use the
   /// targetElement.
   void _buildMethodSignature(ir.FunctionNode originalClosureNode) {
-    _openFunction(targetElement);
+    _openFunction(targetElement, checks: TargetChecks.none);
     List<HInstruction> typeArguments = <HInstruction>[];
 
     // Add function type variables.
@@ -1243,13 +1244,10 @@
       return;
     }
 
-    // TODO(sra): Static methods with no tear-off can be generated with no
-    // checks.
-    // TODO(sra): Instance methods can be generated with reduced checks if
-    // called only from non-dynamic call-sites.
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     // If [functionNode] is `operator==` we explicitly add a null check at the
     // beginning of the method. This is to avoid having call sites do the null
@@ -1308,7 +1306,8 @@
   void _buildGenerator(FunctionEntity function, ir.FunctionNode functionNode) {
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     // Prepare to tail-call the body.
 
@@ -1474,7 +1473,8 @@
     assert(functionNode.body == null);
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     if (closedWorld.nativeData.isNativeMember(targetElement)) {
       registry.registerNativeMethod(targetElement);
@@ -1572,12 +1572,24 @@
     }
   }
 
+  TargetChecks _checksForFunction(FunctionEntity function) {
+    if (!function.isInstanceMember) {
+      // Static methods with no tear-off can be generated with no checks.
+      MemberAccess access = closedWorld.getMemberAccess(function);
+      if (access != null && access.reads.isEmpty) {
+        return TargetChecks.none;
+      }
+    }
+    // TODO(sra): Instance methods can be generated with reduced checks if
+    // called only from non-dynamic call-sites.
+    return TargetChecks.dynamicChecks;
+  }
+
   void _openFunction(MemberEntity member,
       {ir.FunctionNode functionNode,
       ParameterStructure parameterStructure,
       TargetChecks checks}) {
-    // TODO(sra): Pass from all sites.
-    checks ??= TargetChecks.dynamicChecks;
+    assert(checks != null);
 
     Map<Local, AbstractValue> parameterMap = {};
     List<ir.VariableDeclaration> elidedParameters = [];
diff --git a/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart b/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
index 0b22968..61cf1c8 100644
--- a/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
+++ b/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
@@ -26,4 +26,7 @@
 }
 
 @pragma('dart2js:noInline')
-void test(C<A> c) {}
+void test(Object o) => test1(o);
+
+@pragma('dart2js:noInline')
+void test1(C<A> c) {}
diff --git a/tests/compiler/dart2js/rti/emission/list.dart b/tests/compiler/dart2js/rti/emission/list.dart
index fbe0937..14d539bf 100644
--- a/tests/compiler/dart2js/rti/emission/list.dart
+++ b/tests/compiler/dart2js/rti/emission/list.dart
@@ -2,16 +2,16 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*strong.class: global#JSArray:checkedInstance,checks=[$isIterable,$isList],instance*/
+/*strong.class: global#JSArray:checkedInstance,checks=[$isIterable],instance*/
 /*omit.class: global#JSArray:checkedInstance,checks=[$isIterable],instance*/
 
 /*class: global#Iterable:checkedInstance*/
 
-/*strong.class: A:checkedInstance,checkedTypeArgument,checks=[],typeArgument*/
+/*strong.class: A:checkedTypeArgument,checks=[],typeArgument*/
 /*omit.class: A:checkedTypeArgument,checks=[],typeArgument*/
 class A {}
 
-/*strong.class: B:checkedInstance,checks=[],typeArgument*/
+/*strong.class: B:checks=[],typeArgument*/
 /*omit.class: B:checks=[],typeArgument*/
 class B {}
 
diff --git a/tests/compiler/dart2js/rti/emission/map_literal.dart b/tests/compiler/dart2js/rti/emission/map_literal.dart
index acb2eb3..66a1309 100644
--- a/tests/compiler/dart2js/rti/emission/map_literal.dart
+++ b/tests/compiler/dart2js/rti/emission/map_literal.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*strong.class: global#Map:checkedInstance,instance*/
+/*strong.class: global#Map:instance*/
 
 /*class: global#LinkedHashMap:*/
 /*class: global#JsLinkedHashMap:checks=[],instance*/
diff --git a/tests/compiler/dart2js/rti/emission/static_argument.dart b/tests/compiler/dart2js/rti/emission/static_argument.dart
index 0e738ce..d840003 100644
--- a/tests/compiler/dart2js/rti/emission/static_argument.dart
+++ b/tests/compiler/dart2js/rti/emission/static_argument.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*strong.class: I1:checkedInstance*/
+/*strong.class: I1:*/
 /*omit.class: I1:*/
 class I1 {}
 
@@ -10,13 +10,11 @@
 /*omit.class: I2:checkedTypeArgument,checks=[],typeArgument*/
 class I2 {}
 
-// TODO(32954): Exclude $isI1 because foo is only called directly.
-/*strong.class: A:checks=[$isI1,$isI2],instance*/
+/*strong.class: A:checks=[$isI2],instance*/
 /*omit.class: A:checks=[$isI2],instance*/
 class A implements I1, I2 {}
 
-// TODO(32954): Exclude $isI1 because foo is only called directly.
-/*strong.class: B:checks=[$isI1,$isI2],instance*/
+/*strong.class: B:checks=[$isI2],instance*/
 /*omit.class: B:checks=[$isI2],instance*/
 class B implements I1, I2 {}