[dart2js] Do more native method inlining

Check if the call is typesafe instead of inlining only when type
checks are disabled.

Change-Id: Ie391bd7013941ffd3852fb8e2421e8827f41b6d0
Reviewed-on: https://dart-review.googlesource.com/c/89926
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Stephen Adams 2019-01-17 20:21:36 +00:00 committed by commit-bot@chromium.org
parent f353719b08
commit 1020a22290
4 changed files with 58 additions and 33 deletions

View file

@ -7,6 +7,7 @@ library dart2js.abstract_value_domain;
import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../elements/entities.dart';
import '../elements/names.dart';
import '../elements/types.dart' show DartType;
import '../serialization/serialization.dart';
import '../universe/selector.dart';
import '../universe/world_builder.dart';
@ -477,6 +478,11 @@ abstract class AbstractValueDomain {
AbstractBool needsNoSuchMethodHandling(
AbstractValue receiver, Selector selector);
/// Returns the [AbstractValue] for the [parameterType] of a native
/// method. May return `null`, for example, if [parameterType] is not modelled
/// precisely by an [AbstractValue].
AbstractValue getAbstractValueForNativeMethodParameterType(DartType type);
/// Returns an [AbstractBool] that describes if the set of runtime values of
/// [subset] are known to all be in the set of runtime values of [superset].
AbstractBool contains(AbstractValue superset, AbstractValue subset);

View file

@ -5,6 +5,7 @@
import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../elements/entities.dart';
import '../elements/names.dart';
import '../elements/types.dart' show DartType;
import '../serialization/serialization.dart';
import '../universe/selector.dart';
import '../universe/world_builder.dart';
@ -169,6 +170,10 @@ class TrivialAbstractValueDomain implements AbstractValueDomain {
AbstractValue computeAbstractValueForConstant(ConstantValue value) =>
const TrivialAbstractValue();
@override
AbstractValue getAbstractValueForNativeMethodParameterType(DartType type) =>
null;
@override
AbstractBool containsAll(AbstractValue a) => AbstractBool.Maybe;

View file

@ -11,6 +11,7 @@ import '../../common_elements.dart' show CommonElements;
import '../../constants/values.dart';
import '../../elements/entities.dart';
import '../../elements/names.dart';
import '../../elements/types.dart' show DartType, InterfaceType, DynamicType;
import '../../serialization/serialization.dart';
import '../../universe/class_hierarchy.dart';
import '../../universe/selector.dart' show Selector;
@ -774,6 +775,20 @@ class CommonMasks implements AbstractValueDomain {
return null;
}
@override
AbstractValue getAbstractValueForNativeMethodParameterType(DartType type) {
if (type is InterfaceType) {
if (type.typeArguments.isNotEmpty) return null;
// TODO(sra): Consider using a strengthened type check to avoid passing
// `null` to primitive types since the native methods usually have
// non-nullable primitive parameter types.
return createNullableSubtype(type.element);
}
if (type is DynamicType) return dynamicType;
// TODO(sra): Convert other [DartType]s to [AbstractValue]s
return null;
}
@override
String getCompactText(AbstractValue value) {
return formatType(value);

View file

@ -14,7 +14,6 @@ import '../elements/entities.dart';
import '../elements/types.dart';
import '../inferrer/abstract_value_domain.dart';
import '../inferrer/types.dart';
//import '../js/js.dart' as js;
import '../js_backend/allocator_analysis.dart' show JAllocatorAnalysis;
import '../js_backend/backend.dart';
import '../js_backend/native_data.dart' show NativeData;
@ -755,50 +754,50 @@ class SsaInstructionSimplifier extends HBaseVisitor
HInstruction tryInlineNativeMethod(
HInvokeDynamicMethod node, FunctionEntity method) {
// Enable direct calls to a native method only if we don't run in checked
// mode, where the Dart version may have type annotations on parameters and
// return type that it should check.
// Also check that the parameters are not functions: it's the callee that
// will translate them to JS functions.
//
// TODO(ngeoffray): There are some cases where we could still inline in
// checked mode if we know the arguments have the right type. And we could
// do the closure conversion as well as the return type annotation check.
// We can replace the call to the native class interceptor method (target)
// if the target does no conversions or useful type checks.
if (_options.disableInlining) return null;
if (!node.isInterceptedCall) return null;
FunctionType type = _closedWorld.elementEnvironment.getFunctionType(method);
if (type.namedParameters.isNotEmpty) return null;
// Return types on native methods don't need to be checked, since the
// declaration has to be truthful.
// The call site might omit optional arguments. The inlined code must
// preserve the number of arguments, so check only the actual arguments.
List<HInstruction> inputs = node.inputs.sublist(1);
bool canInline = true;
if (_options.parameterCheckPolicy.isEmitted && inputs.length > 1) {
// TODO(sra): Check if [input] is guaranteed to pass the parameter
// type check. Consider using a strengthened type check to avoid
// passing `null` to primitive types since the native methods usually
// have non-nullable primitive parameter types.
canInline = false;
} else {
int inputPosition = 1; // Skip receiver.
void checkParameterType(DartType type) {
if (inputPosition++ < inputs.length && canInline) {
if (type.unaliased.isFunctionType) {
canInline = false;
}
}
List<HInstruction> inputs = node.inputs;
int inputPosition = 2; // Skip interceptor and receiver.
void checkParameterType(DartType parameterType) {
if (!canInline) return;
if (inputPosition >= inputs.length) return;
HInstruction input = inputs[inputPosition++];
if (parameterType.unaliased.isFunctionType) {
// Must call the target since it contains a function conversion.
canInline = false;
return;
}
type.parameterTypes.forEach(checkParameterType);
type.optionalParameterTypes.forEach(checkParameterType);
type.namedParameterTypes.forEach(checkParameterType);
// If the target has no checks don't let a bad type stop us inlining.
if (!_options.parameterCheckPolicy.isEmitted) return;
AbstractValue parameterAbstractValue = _abstractValueDomain
.getAbstractValueForNativeMethodParameterType(parameterType);
if (parameterAbstractValue == null ||
_abstractValueDomain
.isIn(input.instructionType, parameterAbstractValue)
.isPotentiallyFalse) {
canInline = false;
return;
}
}
type.parameterTypes.forEach(checkParameterType);
type.optionalParameterTypes.forEach(checkParameterType);
assert(type.namedParameterTypes.isEmpty);
if (!canInline) return null;
// Strengthen instruction type from annotations to help optimize
@ -809,7 +808,7 @@ class SsaInstructionSimplifier extends HBaseVisitor
HInvokeDynamicMethod result = new HInvokeDynamicMethod(
node.selector,
node.mask,
inputs,
inputs.sublist(1), // Drop interceptor.
returnType,
node.typeArguments,
node.sourceInformation);