d794eb3330
Since 'DartType's overload equality with a structural comparison, we cannot store metadata for them in a 'Map', as is done for 'TreeNode's. Since there is no use-case yet for storing metadata on types, we remove this ability and the restrict the metadata test. Bug: Change-Id: I825f55a5fd48da2c615b3154f909dd666baf5a33 Reviewed-on: https://dart-review.googlesource.com/14640 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Kevin Millikin <kmillikin@google.com>
141 lines
4.3 KiB
Dart
141 lines
4.3 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 kernel.transformations.precompiler;
|
|
|
|
import '../ast.dart';
|
|
|
|
import '../core_types.dart' show CoreTypes;
|
|
|
|
import '../class_hierarchy.dart' show ClosedWorldClassHierarchy;
|
|
|
|
/// Performs whole-program optimizations for Dart VM precompiler.
|
|
/// Assumes strong mode and closed world.
|
|
Program transformProgram(CoreTypes coreTypes, Program program) {
|
|
new _Devirtualization(coreTypes, program).visitProgram(program);
|
|
return program;
|
|
}
|
|
|
|
class DirectCallMetadata {
|
|
final Member target;
|
|
final bool checkReceiverForNull;
|
|
|
|
DirectCallMetadata(this.target, this.checkReceiverForNull);
|
|
}
|
|
|
|
class DirectCallMetadataRepository
|
|
extends MetadataRepository<DirectCallMetadata> {
|
|
@override
|
|
final String tag = 'vm.direct-call.metadata';
|
|
|
|
@override
|
|
final Map<TreeNode, DirectCallMetadata> mapping =
|
|
<TreeNode, DirectCallMetadata>{};
|
|
|
|
@override
|
|
void writeToBinary(DirectCallMetadata metadata, BinarySink sink) {
|
|
sink.writeCanonicalNameReference(getCanonicalNameOfMember(metadata.target));
|
|
sink.writeByte(metadata.checkReceiverForNull ? 1 : 0);
|
|
}
|
|
|
|
@override
|
|
DirectCallMetadata readFromBinary(BinarySource source) {
|
|
var target = source.readCanonicalNameReference()?.getReference()?.asMember;
|
|
if (target == null) {
|
|
throw 'DirectCallMetadata should have a non-null target';
|
|
}
|
|
var checkReceiverForNull = (source.readByte() != 0);
|
|
return new DirectCallMetadata(target, checkReceiverForNull);
|
|
}
|
|
}
|
|
|
|
/// Resolves targets of instance method invocations, property getter
|
|
/// invocations and property setters invocations using strong mode
|
|
/// types / interface targets and closed-world class hierarchy analysis.
|
|
/// If direct target is determined, the invocation node is annotated
|
|
/// with direct call metadata.
|
|
class _Devirtualization extends RecursiveVisitor<Null> {
|
|
/// Toggles tracing (useful for debugging).
|
|
static const _trace = const bool.fromEnvironment('trace.devirtualization');
|
|
|
|
final ClosedWorldClassHierarchy _hierarchy;
|
|
final DirectCallMetadataRepository _metadata;
|
|
Set<Name> _objectMemberNames;
|
|
|
|
_Devirtualization(CoreTypes coreTypes, Program program)
|
|
: _hierarchy = new ClosedWorldClassHierarchy(program),
|
|
_metadata = new DirectCallMetadataRepository() {
|
|
_objectMemberNames = new Set<Name>.from(_hierarchy
|
|
.getInterfaceMembers(coreTypes.objectClass)
|
|
.map((Member m) => m.name));
|
|
program.addMetadataRepository(_metadata);
|
|
}
|
|
|
|
_makeDirectCall(TreeNode node, Member target, Member singleTarget) {
|
|
if (_trace) {
|
|
print("[devirt] Resolving ${target} to ${singleTarget}");
|
|
}
|
|
_metadata.mapping[node] = new DirectCallMetadata(singleTarget, true);
|
|
}
|
|
|
|
@override
|
|
visitLibrary(Library node) {
|
|
if (_trace) {
|
|
String external = node.isExternal ? " (external)" : "";
|
|
print("[devirt] Processing library ${node.name}${external}");
|
|
}
|
|
super.visitLibrary(node);
|
|
}
|
|
|
|
@override
|
|
visitMethodInvocation(MethodInvocation node) {
|
|
super.visitMethodInvocation(node);
|
|
|
|
Member target = node.interfaceTarget;
|
|
if ((target != null) &&
|
|
(target is! Field) &&
|
|
!_objectMemberNames.contains(target.name)) {
|
|
Member singleTarget =
|
|
_hierarchy.getSingleTargetForInterfaceInvocation(target);
|
|
if ((singleTarget is Procedure) && !singleTarget.isGetter) {
|
|
_makeDirectCall(node, target, singleTarget);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
@override
|
|
visitPropertyGet(PropertyGet node) {
|
|
super.visitPropertyGet(node);
|
|
|
|
Member target = node.interfaceTarget;
|
|
if ((target != null) && !_objectMemberNames.contains(target.name)) {
|
|
Member singleTarget =
|
|
_hierarchy.getSingleTargetForInterfaceInvocation(target);
|
|
if (singleTarget != null) {
|
|
_makeDirectCall(node, target, singleTarget);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
@override
|
|
visitPropertySet(PropertySet node) {
|
|
super.visitPropertySet(node);
|
|
|
|
Member target = node.interfaceTarget;
|
|
if (target != null) {
|
|
Member singleTarget = _hierarchy
|
|
.getSingleTargetForInterfaceInvocation(target, setter: true);
|
|
if (singleTarget != null) {
|
|
_makeDirectCall(node, target, singleTarget);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
}
|