[dart2js] Remove more entry checks
Remove checks on generator bodies, constructor bodies and constructor
factories, as these are all called from statically resolved contexts.
Add a TargetChecks type to convey which checks are required.
Change-Id: Ib354956d039078a5d1428de4706b7427401b08d9
Reviewed-on: https://dart-review.googlesource.com/58681
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index fcb0672..079f4c0 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -37,6 +37,7 @@
import '../universe/call_structure.dart';
import '../universe/selector.dart';
import '../universe/side_effects.dart' show SideEffects;
+import '../universe/target_checks.dart' show TargetChecks;
import '../universe/use.dart'
show ConstantUse, ConstrainedDynamicUse, StaticUse;
import '../universe/world_builder.dart' show CodegenWorldBuilder;
@@ -323,7 +324,7 @@
void buildField(ir.Field node) {
_inLazyInitializerExpression = node.isStatic;
FieldEntity field = _elementMap.getMember(node);
- openFunction(field);
+ openFunction(field, checks: TargetChecks.none);
if (node.isInstanceMember && options.enableTypeAssertions) {
HInstruction thisInstruction = localsHandler.readThis(
sourceInformation: _sourceInformationBuilder.buildGet(node));
@@ -471,7 +472,8 @@
ClassEntity cls = constructor.enclosingClass;
if (_inliningStack.isEmpty) {
- openFunction(constructor, node.function);
+ openFunction(constructor,
+ functionNode: node.function, checks: TargetChecks.none);
}
// [fieldValues] accumulates the field initializer values, which may be
@@ -961,8 +963,8 @@
/// Builds generative constructor body.
void buildConstructorBody(ir.Constructor constructor) {
- openFunction(
- _elementMap.getConstructorBody(constructor), constructor.function);
+ openFunction(_elementMap.getConstructorBody(constructor),
+ functionNode: constructor.function, checks: TargetChecks.none);
constructor.function.body.accept(this);
closeFunction();
}
@@ -976,7 +978,11 @@
return;
}
- openFunction(function, functionNode);
+ // 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);
// 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
@@ -1033,7 +1039,7 @@
/// per-invocation checks and the body, which is later transformed, contains
/// the re-entrant 'state machine' code.
void buildGenerator(FunctionEntity function, ir.FunctionNode functionNode) {
- openFunction(function, functionNode);
+ openFunction(function, functionNode: functionNode);
// Prepare to tail-call the body.
@@ -1097,7 +1103,7 @@
void buildGeneratorBody(
JGeneratorBody function, ir.FunctionNode functionNode) {
FunctionEntity entry = function.function;
- openFunction(entry, functionNode);
+ openFunction(entry, functionNode: functionNode, checks: TargetChecks.none);
graph.needsAsyncRewrite = true;
if (!function.elementType.containsFreeTypeVariables) {
// We can generate the element type in place
@@ -1118,13 +1124,16 @@
return true;
}
- void _potentiallyAddFunctionParameterTypeChecks(ir.FunctionNode function) {
+ void _potentiallyAddFunctionParameterTypeChecks(
+ ir.FunctionNode function, TargetChecks targetChecks) {
// Put the type checks in the first successor of the entry,
// because that is where the type guards will also be inserted.
// This way we ensure that a type guard will dominate the type
// check.
- checkTypeVariableBounds(targetElement);
+ if (targetChecks.checkTypeParameters) {
+ checkTypeVariableBounds(targetElement);
+ }
MemberDefinition definition =
_elementMap.getMemberDefinition(targetElement);
@@ -1140,9 +1149,22 @@
return;
}
HInstruction newParameter = localsHandler.directLocals[local];
+ DartType type = _getDartTypeIfValid(variable.type);
- newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
- newParameter, _getDartTypeIfValid(variable.type));
+ if (options.strongMode) {
+ if (targetChecks.checkAllParameters ||
+ (targetChecks.checkCovariantParameters &&
+ (variable.isGenericCovariantImpl || variable.isCovariant))) {
+ newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
+ newParameter, type);
+ } else {
+ newParameter = typeBuilder.trustTypeOfParameter(newParameter, type);
+ }
+ } else {
+ newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
+ newParameter, type);
+ }
+
localsHandler.directLocals[local] = newParameter;
}
@@ -1179,7 +1201,7 @@
// TODO(johnniwinther): Non-js-interop external functions should
// throw a runtime error.
assert(functionNode.body == null);
- openFunction(function, functionNode);
+ openFunction(function, functionNode: functionNode);
if (closedWorld.nativeData.isNativeMember(targetElement)) {
nativeEmitter.nativeMethods.add(targetElement);
@@ -1259,7 +1281,11 @@
}
}
- void openFunction(MemberEntity member, [ir.FunctionNode functionNode]) {
+ void openFunction(MemberEntity member,
+ {ir.FunctionNode functionNode, TargetChecks checks}) {
+ // TODO(sra): Pass from all sites.
+ checks ??= TargetChecks.dynamicChecks;
+
Map<Local, AbstractValue> parameterMap = <Local, AbstractValue>{};
if (functionNode != null) {
void handleParameter(ir.VariableDeclaration node) {
@@ -1293,7 +1319,7 @@
_addFunctionTypeVariablesIfNeeded(member);
if (functionNode != null) {
- _potentiallyAddFunctionParameterTypeChecks(functionNode);
+ _potentiallyAddFunctionParameterTypeChecks(functionNode, checks);
}
_insertTraceCall(member);
_insertCoverageCall(member);
diff --git a/pkg/compiler/lib/src/universe/target_checks.dart b/pkg/compiler/lib/src/universe/target_checks.dart
new file mode 100644
index 0000000..6d4ac79
--- /dev/null
+++ b/pkg/compiler/lib/src/universe/target_checks.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+library dart2js.target_checks;
+
+/// A summary of the checks required when entering a target method.
+///
+/// The target checks are used to annotate call sites with the checking required
+/// from that call site.
+///
+/// The target can either perform worst-case checks over all call sites, or can
+/// be generated as multiple entry points for different TargetChecks.
+///
+/// The TargetChecks at a call site can be refined by analysis. For example,
+/// the generic covariant check for writing into a List might not be required
+/// when copying values from a List allocated with the same type variable value.
+///
+/// The TargetChecks at a call site can be used to inform optimizations, for
+/// example, only lowering to simpler instructions when generic covariant check
+/// is required.
+///
+/// Unsound modes can be implemented in a scoped manner by using a TargetChecks
+/// that has fewer checks than required (or no checks) in the unsound region.
+class TargetChecks {
+ // Typical of direct static call sites.
+ // Typical of static method targets with no tear-offs.
+ static final TargetChecks none = const TargetChecks._(false, false, false);
+
+ // Typical of closure calls and dynamic calls.
+ static final TargetChecks dynamicChecks =
+ const TargetChecks._(true, true, true);
+
+ // Typical of method calls.
+ static final TargetChecks covariantChecks =
+ const TargetChecks._(false, true, false);
+
+ // TODO(sra): This can be more fine-grained, talking about individual
+ // parameters.
+ final bool _checkOtherParameters;
+ final bool _checkCovariantParameters;
+ final bool _checkTypeParameters;
+ const TargetChecks._(
+ this._checkOtherParameters,
+ this._checkCovariantParameters,
+ this._checkTypeParameters,
+ );
+
+ bool get checkTypeParameters => _checkTypeParameters;
+ bool get checkCovariantParameters => _checkCovariantParameters;
+ bool get checkAllParameters => _checkOtherParameters;
+}
diff --git a/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart b/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart
new file mode 100644
index 0000000..37dfefe
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+import 'package:meta/dart2js.dart';
+
+/*class: A1:checks=[],instance*/
+class A1 {}
+
+// Constructor calls are always statically invoked, so there is no checks at the
+// entry and the `Test1` constructor does not cause any checks.
+/*class: B1:checks=[],instance*/
+class B1 implements A1 {}
+
+/*class: Test1:checks=[],instance*/
+class Test1 {
+ A1 x;
+ @noInline
+ Test1(this.x);
+}
+
+/*class: A2:checks=[],instance*/
+class A2 {}
+
+// Constructor bodies are always statically resolved, so there is no checks at
+// the entry and the `Test2` constructor body does not cause any checks.
+/*class: B2:checks=[],instance*/
+class B2 implements A2 {}
+
+/*class: Test2:checks=[]*/
+abstract class Test2 {
+ @noInline
+ Test2(A2 x) {
+ print(x);
+ }
+}
+
+/*class: Use:checks=[],instance*/
+class Use extends Test2 {
+ Use.A2() : super(new A2());
+ Use.B2() : super(new B2());
+}
+
+main() {
+ new Test1(new A1());
+ new Test1(new B1());
+ new Test1(null);
+
+ new Use.A2();
+ new Use.B2();
+}
diff --git a/tests/compiler/dart2js/rti/emission/static_argument_strong.dart b/tests/compiler/dart2js/rti/emission/static_argument_strong.dart
new file mode 100644
index 0000000..7bdc190
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/static_argument_strong.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+import 'package:meta/dart2js.dart';
+
+/*class: I1:checkedInstance*/
+class I1 {}
+
+/*class: I2:checkedInstance,checkedTypeArgument,checks=[],typeArgument*/
+class I2 {}
+
+// TODO(32954): Exclude $isI1 because foo is only called directly.
+/*class: A:checks=[$isI1,$isI2],instance*/
+class A implements I1, I2 {}
+
+// TODO(32954): Exclude $isI1 because foo is only called directly.
+/*class: B:checks=[$isI1,$isI2],instance*/
+class B implements I1, I2 {}
+
+@noInline
+void foo(I1 x) {}
+
+@noInline
+void bar(I2 x) {}
+
+main() {
+ dynamic f = bar;
+
+ foo(new A());
+ foo(new B());
+ f(new A());
+ f(new B());
+}