8c1f86e5dd
TFA now produces direct call metadata for function calls based on the results of the analysis. VM reads direct call metadata for function calls, but only uses it if target is a tear-off, as it cannot handle inlining of an arbitrary closure function yet (due to scope building dependencies between compilation of a closure and its parent function). TEST=pkg/vm/testcases/transformations/type_flow/transformer/closures.dart Issue: https://github.com/dart-lang/sdk/issues/39692 Change-Id: Idf0b6988534bfca1a64c6661a82df8d453272abf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/357180 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
224 lines
7.2 KiB
Dart
224 lines
7.2 KiB
Dart
// Copyright (c) 2017, 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 vm.transformations.cha_devirtualization;
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'package:kernel/class_hierarchy.dart'
|
|
show ClassHierarchy, ClassHierarchySubtypes, ClosedWorldClassHierarchy;
|
|
|
|
import '../metadata/direct_call.dart';
|
|
|
|
/// Devirtualization of method invocations based on the class hierarchy
|
|
/// analysis. Assumes strong mode and closed world.
|
|
Component transformComponent(CoreTypes coreTypes, Component component) {
|
|
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
|
|
ClosedWorldClassHierarchy hierarchy = new ClassHierarchy(component, coreTypes,
|
|
onAmbiguousSupertypes: ignoreAmbiguousSupertypes)
|
|
as ClosedWorldClassHierarchy;
|
|
final hierarchySubtypes = hierarchy.computeSubtypesInformation();
|
|
new CHADevirtualization(coreTypes, component, hierarchy, hierarchySubtypes)
|
|
.visitComponent(component);
|
|
return component;
|
|
}
|
|
|
|
/// Base class for implementing devirtualization of method invocations.
|
|
/// Subclasses should implement particular devirtualization strategy in
|
|
/// [getDirectCall] method. Once direct target is determined, the invocation
|
|
/// node is annotated with direct call metadata.
|
|
abstract class Devirtualization extends RecursiveVisitor {
|
|
/// Toggles tracing (useful for debugging).
|
|
static const _trace = const bool.fromEnvironment('trace.devirtualization');
|
|
|
|
final DirectCallMetadataRepository _metadata;
|
|
final Set<Name> _objectMemberNames;
|
|
|
|
Devirtualization(
|
|
CoreTypes coreTypes, Component component, ClassHierarchy hierarchy)
|
|
: _metadata = new DirectCallMetadataRepository(),
|
|
_objectMemberNames = new Set<Name>.from(hierarchy
|
|
.getInterfaceMembers(coreTypes.objectClass)
|
|
.map((Member m) => m.name)) {
|
|
component.addMetadataRepository(_metadata);
|
|
}
|
|
|
|
bool isMethod(Member member) => (member is Procedure) && !member.isGetter;
|
|
|
|
bool isFieldOrGetter(Member member) =>
|
|
(member is Field) || ((member is Procedure) && member.isGetter);
|
|
|
|
bool isLegalTargetForMethodInvocation(Member target, Arguments arguments) {
|
|
final FunctionNode func = target.function!;
|
|
|
|
final positionalArgs = arguments.positional.length;
|
|
if ((positionalArgs < func.requiredParameterCount) ||
|
|
(positionalArgs > func.positionalParameters.length)) {
|
|
return false;
|
|
}
|
|
|
|
if (arguments.named.isNotEmpty || func.namedParameters.isNotEmpty) {
|
|
final names = arguments.named.map((v) => v.name).toSet();
|
|
for (var param in func.namedParameters) {
|
|
final passed = names.remove(param.name);
|
|
if (param.isRequired && !passed) {
|
|
return false;
|
|
}
|
|
}
|
|
if (names.isNotEmpty) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (arguments.types.isNotEmpty &&
|
|
arguments.types.length != func.typeParameters.length) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool hasExtraTargetForNull(DirectCallMetadata directCall) =>
|
|
directCall.checkReceiverForNull &&
|
|
_objectMemberNames.contains(directCall.targetMember!.name);
|
|
|
|
DirectCallMetadata? getDirectCall(TreeNode node, Member? interfaceTarget,
|
|
{bool setter = false});
|
|
|
|
makeDirectCall(TreeNode node, Member? target, DirectCallMetadata directCall) {
|
|
if (_trace) {
|
|
print("[devirt] Resolving ${target} to ${directCall.targetMember}"
|
|
" at ${node.location}");
|
|
}
|
|
_metadata.mapping[node] = directCall;
|
|
}
|
|
|
|
@override
|
|
visitLibrary(Library node) {
|
|
if (_trace) {
|
|
print("[devirt] Processing library ${node.name}");
|
|
}
|
|
super.visitLibrary(node);
|
|
}
|
|
|
|
void _handleMethodInvocation(
|
|
TreeNode node, Member? target, Arguments arguments) {
|
|
if (target != null && !isMethod(target)) {
|
|
return;
|
|
}
|
|
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
|
|
// TODO(alexmarkov): Convert _isLegalTargetForMethodInvocation()
|
|
// check into an assertion once front-end implements all override checks.
|
|
if ((directCall != null) &&
|
|
isMethod(directCall.targetMember!) &&
|
|
isLegalTargetForMethodInvocation(directCall.targetMember!, arguments) &&
|
|
!hasExtraTargetForNull(directCall)) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceInvocation(InstanceInvocation node) {
|
|
super.visitInstanceInvocation(node);
|
|
_handleMethodInvocation(node, node.interfaceTarget, node.arguments);
|
|
}
|
|
|
|
@override
|
|
visitDynamicInvocation(DynamicInvocation node) {
|
|
super.visitDynamicInvocation(node);
|
|
_handleMethodInvocation(node, null, node.arguments);
|
|
}
|
|
|
|
@override
|
|
visitEqualsCall(EqualsCall node) {
|
|
super.visitEqualsCall(node);
|
|
|
|
final target = node.interfaceTarget;
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
if (directCall != null && !directCall.checkReceiverForNull) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
void _handlePropertyGet(TreeNode node, Member? target) {
|
|
if (target != null && !isFieldOrGetter(target)) {
|
|
return;
|
|
}
|
|
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
|
|
if ((directCall != null) &&
|
|
isFieldOrGetter(directCall.targetMember!) &&
|
|
!hasExtraTargetForNull(directCall)) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceGet(InstanceGet node) {
|
|
super.visitInstanceGet(node);
|
|
_handlePropertyGet(node, node.interfaceTarget);
|
|
}
|
|
|
|
@override
|
|
visitDynamicGet(DynamicGet node) {
|
|
super.visitDynamicGet(node);
|
|
_handlePropertyGet(node, null);
|
|
}
|
|
|
|
void _handlePropertySet(TreeNode node, Member? target) {
|
|
final DirectCallMetadata? directCall =
|
|
getDirectCall(node, target, setter: true);
|
|
if (directCall != null) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceSet(InstanceSet node) {
|
|
super.visitInstanceSet(node);
|
|
_handlePropertySet(node, node.interfaceTarget);
|
|
}
|
|
|
|
@override
|
|
visitDynamicSet(DynamicSet node) {
|
|
super.visitDynamicSet(node);
|
|
_handlePropertySet(node, null);
|
|
}
|
|
|
|
@override
|
|
visitFunctionInvocation(FunctionInvocation node) {
|
|
super.visitFunctionInvocation(node);
|
|
final DirectCallMetadata? directCall = getDirectCall(node, null);
|
|
if (directCall != null) {
|
|
makeDirectCall(node, null, directCall);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Devirtualization based on the closed-world class hierarchy analysis.
|
|
class CHADevirtualization extends Devirtualization {
|
|
final ClassHierarchySubtypes _hierarchySubtype;
|
|
|
|
CHADevirtualization(CoreTypes coreTypes, Component component,
|
|
ClosedWorldClassHierarchy hierarchy, this._hierarchySubtype)
|
|
: super(coreTypes, component, hierarchy);
|
|
|
|
@override
|
|
DirectCallMetadata? getDirectCall(TreeNode node, Member? interfaceTarget,
|
|
{bool setter = false}) {
|
|
if (interfaceTarget == null) {
|
|
return null;
|
|
}
|
|
Member? singleTarget = _hierarchySubtype
|
|
.getSingleTargetForInterfaceInvocation(interfaceTarget, setter: setter);
|
|
if (singleTarget == null) {
|
|
return null;
|
|
}
|
|
return DirectCallMetadata.targetMember(singleTarget, true);
|
|
}
|
|
}
|