[dart2js] new-rti: Simplification and branch-strengthening for 'is'

Change-Id: I49d712b303400fe2f2109b6c9f8fb684917df0ef
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112701
Reviewed-by: Mayank Patke <fishythefish@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams
2019-08-12 20:40:06 +00:00
committed by commit-bot@chromium.org
parent f444374bb7
commit bd47ec2f37
9 changed files with 214 additions and 21 deletions
@@ -207,7 +207,7 @@ abstract class AbstractValueDomain {
/// reasoning, for example, that a dominating check uses the same type
/// expression.
AbstractValueWithPrecision createFromStaticType(DartType type,
[ClassRelation classRelation = ClassRelation.subtype]);
{ClassRelation classRelation = ClassRelation.subtype, bool nullable});
/// Creates an [AbstractValue] for a non-null exact instance of [cls].
AbstractValue createNonNullExact(ClassEntity cls);
+5 -2
View File
@@ -360,8 +360,11 @@ class TrivialAbstractValueDomain implements AbstractValueDomain {
@override
AbstractValueWithPrecision createFromStaticType(DartType type,
[ClassRelation classRelation = ClassRelation.subtype]) =>
const AbstractValueWithPrecision(const TrivialAbstractValue(), false);
{ClassRelation classRelation = ClassRelation.subtype, bool nullable}) {
assert(nullable != null);
return const AbstractValueWithPrecision(
const TrivialAbstractValue(), false);
}
@override
AbstractValue get asyncStarStreamType => const TrivialAbstractValue();
@@ -265,7 +265,13 @@ class CommonMasks implements AbstractValueDomain {
@override
AbstractValueWithPrecision createFromStaticType(DartType type,
[ClassRelation classRelation = ClassRelation.subtype]) {
{ClassRelation classRelation = ClassRelation.subtype, bool nullable}) {
assert(nullable != null);
AbstractValueWithPrecision finish(TypeMask value, bool isPrecise) {
return AbstractValueWithPrecision(
nullable ? value : value.nonNullable(), isPrecise);
}
bool isPrecise = true;
while (type is TypeVariableType) {
TypeVariableType typeVariable = type;
@@ -274,6 +280,7 @@ class CommonMasks implements AbstractValueDomain {
classRelation = ClassRelation.subtype;
isPrecise = false;
}
if (type is InterfaceType) {
if (isPrecise) {
// TODO(sra): Could be precise if instantiated-to-bounds.
@@ -284,24 +291,22 @@ class CommonMasks implements AbstractValueDomain {
}
switch (classRelation) {
case ClassRelation.exact:
return AbstractValueWithPrecision(
TypeMask.exact(type.element, _closedWorld), isPrecise);
return finish(TypeMask.exact(type.element, _closedWorld), isPrecise);
case ClassRelation.thisExpression:
if (!_closedWorld.isUsedAsMixin(type.element)) {
return AbstractValueWithPrecision(
return finish(
TypeMask.subclass(type.element, _closedWorld), isPrecise);
}
break;
case ClassRelation.subtype:
break;
}
return AbstractValueWithPrecision(
TypeMask.subtype(type.element, _closedWorld), isPrecise);
return finish(TypeMask.subtype(type.element, _closedWorld), isPrecise);
} else if (type is FunctionType) {
return AbstractValueWithPrecision(
return finish(
TypeMask.subtype(commonElements.functionClass, _closedWorld), false);
} else {
return AbstractValueWithPrecision(dynamicType, false);
return finish(dynamicType, false);
}
}
+9 -5
View File
@@ -4055,8 +4055,8 @@ class KernelSsaGraphBuilder extends ir.Visitor {
StaticType receiverStaticType =
_getStaticType(invocation.arguments.positional[1]);
AbstractValue receiverType = _abstractValueDomain
.createFromStaticType(
receiverStaticType.type, receiverStaticType.relation)
.createFromStaticType(receiverStaticType.type,
classRelation: receiverStaticType.relation, nullable: true)
.abstractValue;
push(new HInvokeClosure(selector, receiverType, inputs,
_abstractValueDomain.dynamicType, typeArguments));
@@ -4811,8 +4811,8 @@ class KernelSsaGraphBuilder extends ir.Visitor {
List<DartType> typeArguments,
SourceInformation sourceInformation) {
AbstractValue typeBound = _abstractValueDomain
.createFromStaticType(
staticReceiverType.type, staticReceiverType.relation)
.createFromStaticType(staticReceiverType.type,
classRelation: staticReceiverType.relation, nullable: true)
.abstractValue;
receiverType = receiverType == null
? typeBound
@@ -5404,7 +5404,11 @@ class KernelSsaGraphBuilder extends ir.Visitor {
if (options.experimentNewRti) {
HInstruction rti =
_typeBuilder.analyzeTypeArgumentNewRti(typeValue, sourceElement);
push(HIsTest(typeValue, expression, rti, _abstractValueDomain.boolType));
AbstractValueWithPrecision checkedType =
_abstractValueDomain.createFromStaticType(typeValue, nullable: false);
push(HIsTest(typeValue, checkedType, expression, rti,
_abstractValueDomain.boolType));
return;
}
+3 -2
View File
@@ -4352,10 +4352,11 @@ class HTypeInfoExpression extends HInstruction {
/// lowered to other instructions, so this instruction remains for types that
/// depend on type variables and complex types.
class HIsTest extends HInstruction {
final AbstractValueWithPrecision checkedAbstractValue;
final DartType dartType;
HIsTest(
this.dartType, HInstruction checked, HInstruction rti, AbstractValue type)
HIsTest(this.dartType, this.checkedAbstractValue, HInstruction checked,
HInstruction rti, AbstractValue type)
: super([rti, checked], type) {
setUseGvn();
}
+46 -2
View File
@@ -761,7 +761,9 @@ class SsaInstructionSimplifier extends HBaseVisitor
_closedWorld.elementEnvironment.getFieldType(field);
HInstruction closureCall = new HInvokeClosure(
callSelector,
_abstractValueDomain.createFromStaticType(fieldType).abstractValue,
_abstractValueDomain
.createFromStaticType(fieldType, nullable: true)
.abstractValue,
inputs,
node.instructionType,
node.typeArguments)
@@ -1877,7 +1879,7 @@ class SsaInstructionSimplifier extends HBaseVisitor
dartType, node.isTypeError, _closedWorld.commonElements);
if (specializedCheck != null) {
AbstractValueWithPrecision checkedType =
_abstractValueDomain.createFromStaticType(dartType);
_abstractValueDomain.createFromStaticType(dartType, nullable: true);
return HAsCheckSimple(node.checkedInput, dartType, checkedType,
node.isTypeError, specializedCheck, node.instructionType);
}
@@ -1891,6 +1893,27 @@ class SsaInstructionSimplifier extends HBaseVisitor
return node;
}
@override
HInstruction visitIsTest(HIsTest node) {
AbstractValueWithPrecision checkedAbstractValue = node.checkedAbstractValue;
HInstruction checkedInput = node.checkedInput;
AbstractValue inputType = checkedInput.instructionType;
AbstractBool isIn = _abstractValueDomain.isIn(
inputType, checkedAbstractValue.abstractValue);
if (isIn.isDefinitelyFalse) {
return _graph.addConstantBool(false, _closedWorld);
}
if (!checkedAbstractValue.isPrecise) return node;
if (isIn.isDefinitelyTrue) {
return _graph.addConstantBool(true, _closedWorld);
}
return node;
}
@override
HInstruction visitInstanceEnvironment(HInstanceEnvironment node) {
HInstruction instance = node.inputs.single;
@@ -2954,6 +2977,27 @@ class SsaTypeConversionInserter extends HBaseVisitor
// false. Avoid strengthening to `null`.
}
@override
void visitIsTest(HIsTest instruction) {
List<HBasicBlock> trueTargets = <HBasicBlock>[];
List<HBasicBlock> falseTargets = <HBasicBlock>[];
collectTargets(instruction, trueTargets, falseTargets);
if (trueTargets.isEmpty && falseTargets.isEmpty) return;
AbstractValue convertedType =
instruction.checkedAbstractValue.abstractValue;
HInstruction input = instruction.checkedInput;
for (HBasicBlock block in trueTargets) {
insertTypePropagationForDominatedUsers(block, input, convertedType);
}
// TODO(sra): Also strengthen uses for when the condition is precise and
// known false (e.g. int? x; ... if (x is! int) use(x)). Avoid strengthening
// to `null`.
}
@override
void visitIdentity(HIdentity instruction) {
// At HIf(HIdentity(x, null)) strengthens x to non-null on else branch.
+1 -1
View File
@@ -480,7 +480,7 @@ abstract class TypeBuilder {
type, builder.sourceElement,
sourceInformation: sourceInformation);
AbstractValueWithPrecision checkedType =
_abstractValueDomain.createFromStaticType(type);
_abstractValueDomain.createFromStaticType(type, nullable: true);
AbstractValue instructionType = _abstractValueDomain.intersection(
original.instructionType, checkedType.abstractValue);
return HAsCheck(
@@ -0,0 +1,132 @@
// Copyright (c) 2019, 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 new_rti_is_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import '../helpers/compiler_helper.dart';
// 'N' tests all have a nullable input so should not reduce is-test.
// TODO(NNBD): Add tests with non-nullable input types.
const TEST1N = r"""
foo(int a) {
return a is double;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST2N = r"""
foo(int a) {
return a is num;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST3N = r"""
foo(double a) {
return a is int;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST4N = r"""
foo(double a) {
return a is num;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST5N = r"""
foo(num a) {
return a is int;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST6N = r"""
foo(num a) {
return a is double;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST1I = r"""
foo(a) {
if (a is int) return a is double;
// present: 'return true'
}
""";
const TEST2I = r"""
foo(a) {
if (a is int) return a is num;
// present: 'return true'
}
""";
const TEST3I = r"""
foo(a) {
if (a is double) return a is int;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST4I = r"""
foo(a) {
if (a is double) return a is num;
// present: 'return true'
}
""";
const TEST5I = r"""
foo(a) {
if (a is num) return a is int;
// absent: 'return true'
// absent: 'return false'
}
""";
const TEST6I = r"""
foo(a) {
if (a is num) return a is double;
// present: 'return true'
}
""";
main() {
runTests() async {
Future check(String test) {
return compile(test,
entry: 'foo', check: checkerForAbsentPresent(test), newRti: true);
}
await check(TEST1N);
await check(TEST2N);
await check(TEST3N);
await check(TEST4N);
await check(TEST5N);
await check(TEST6N);
await check(TEST1I);
await check(TEST2I);
await check(TEST3I);
await check(TEST4I);
await check(TEST5I);
await check(TEST6I);
}
asyncTest(() async {
print('--test from kernel------------------------------------------------');
await runTests();
});
}
@@ -37,6 +37,7 @@ Future<String> compile(String code,
bool trustJSInteropTypeAnnotations: false,
bool disableTypeInference: true,
bool omitImplicitChecks: true,
bool newRti: false,
void check(String generatedEntry),
bool returnAll: false}) async {
OutputCollector outputCollector = returnAll ? new OutputCollector() : null;
@@ -59,6 +60,9 @@ Future<String> compile(String code,
if (disableInlining) {
options.add(Flags.disableInlining);
}
if (newRti) {
options.add(Flags.experimentNewRti);
}
// Pretend this is a dart2js_native test to allow use of 'native' keyword
// and import of private libraries.