Files
sdk/pkg/vm/lib/transformations/devirtualization.dart
T
Alexander Markov 8c1f86e5dd [vm/aot] Devirtualization of function calls
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>
2024-03-13 16:48:34 +00:00

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);
}
}