Skip to content

Commit

Permalink
[vm/aot/tfa] Whole-program constant propagation
Browse files Browse the repository at this point in the history
Size:
flutter_gallery total size -2.48% (arm), -2.3% (arm64)
flutter_gallery instructions size -2.91% (arm), -2.77% (arm64)
velocity_tracker_bench total size -7.5% (arm), -7.1% (arm64)

Performance:
SkeletalAnimation +46.02% (Intel Core i5), +37.75% (Intel Xeon), +24.86% (arm), +39.75% (arm64).
FfiMemory.Pointer* +44-64% (x64)
FfiMemory.PointerPointer +436-465% (x64), +443% (arm64).

Issue: #37710

Change-Id: I6221bfa02b165ccc17d4ee8b857bb89212febaff
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125936
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
  • Loading branch information
alexmarkov authored and commit-bot@chromium.org committed Dec 4, 2019
1 parent 9929b53 commit f56b0f6
Show file tree
Hide file tree
Showing 54 changed files with 594 additions and 174 deletions.
2 changes: 2 additions & 0 deletions pkg/kernel/lib/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7104,6 +7104,7 @@ abstract class BinarySink {
void writeStringReference(String str);
void writeName(Name node);
void writeDartType(DartType type);
void writeConstantReference(Constant constant);
void writeNode(Node node);

void enterScope(
Expand Down Expand Up @@ -7132,6 +7133,7 @@ abstract class BinarySource {
String readStringReference();
Name readName();
DartType readDartType();
Constant readConstantReference();
FunctionNode readFunctionNode();

void enterScope({List<TypeParameter> typeParameters});
Expand Down
1 change: 1 addition & 0 deletions pkg/kernel/lib/target/targets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ abstract class Target {
Class concreteConstMapLiteralClass(CoreTypes coreTypes) => null;

Class concreteIntLiteralClass(CoreTypes coreTypes, int value) => null;
Class concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
Class concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;

ConstantsBackend constantsBackend(CoreTypes coreTypes);
Expand Down
90 changes: 76 additions & 14 deletions pkg/vm/lib/bytecode/gen_bytecode.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1285,8 +1285,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
throw 'Expected constant, got ${expr.runtimeType}';
}

void _genPushConstExpr(Expression expr) {
final constant = _getConstant(expr);
void _genPushConstant(Constant constant) {
if (constant is NullConstant) {
asm.emitPushNull();
} else if (constant is BoolConstant) {
Expand All @@ -1298,6 +1297,20 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
}
}

void _genPushConstExpr(Expression expr) {
if (expr is ConstantExpression) {
_genPushConstant(expr.constant);
} else if (expr is NullLiteral) {
asm.emitPushNull();
} else if (expr is BoolLiteral) {
_genPushBool(expr.value);
} else if (expr is IntLiteral) {
_genPushInt(expr.value);
} else {
_genPushConstant(_getConstant(expr));
}
}

void _genReturnTOS([int yieldSourcePosition = null]) {
if (options.causalAsyncStacks &&
parentFunction != null &&
Expand Down Expand Up @@ -1340,6 +1353,9 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
} else {
asm.emitDirectCall(cpIndex, totalArgCount);
}
if (inferredTypeMetadata != null && node != null) {
_replaceWithConstantValue(node);
}
}

void _genDirectCallWithArgs(Member target, Arguments args,
Expand Down Expand Up @@ -1794,7 +1810,20 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
} else {
inferredTypesAttribute.add(NullConstant());
}
inferredTypesAttribute.add(IntConstant(md.flags));
// Inferred constant values are handled in bytecode generator
// (_replaceWithConstantValue, _initConstantParameters) and
// not propagated to VM.
final flags = md.flags & ~InferredType.flagConstant;
inferredTypesAttribute.add(IntConstant(flags));
}

void _replaceWithConstantValue(TreeNode node) {
final InferredType md = inferredTypeMetadata[node];
if (md == null || md.constantValue == null || asm.isUnreachable) {
return;
}
asm.emitDrop1();
_genPushConstant(md.constantValue);
}

// Generate additional code for 'operator ==' to handle nulls.
Expand Down Expand Up @@ -2030,6 +2059,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
asm.emitPopLocal(locals.functionTypeArgsVarIndexInFrame);
}
}

if (inferredTypeMetadata != null && function != null) {
_initConstantParameters(function);
}
}

void _handleDelayedTypeArguments(Label doneCheckingTypeArguments) {
Expand Down Expand Up @@ -2073,6 +2106,21 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
asm.emitPopLocal(locals.functionTypeArgsVarIndexInFrame);
}

void _initConstantParameters(FunctionNode function) {
function.positionalParameters.forEach(_initParameterIfConstant);
locals.sortedNamedParameters.forEach(_initParameterIfConstant);
}

void _initParameterIfConstant(VariableDeclaration variable) {
final md = inferredTypeMetadata[variable];
if (md != null && md.constantValue != null) {
_genPushConstant(md.constantValue);
asm.emitPopLocal(locals.isCaptured(variable)
? locals.getOriginalParamSlotIndex(variable)
: locals.getVarIndexInFrame(variable));
}
}

void _setupInitialContext(FunctionNode function) {
_allocateContextIfNeeded();

Expand Down Expand Up @@ -3279,25 +3327,32 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
_appendInferredType(node, asm.offset);
}

bool generated = false;
if (invocationKind != InvocationKind.getter && !isDynamic && !isUnchecked) {
final staticReceiverType = getStaticType(receiver, staticTypeContext);
if (isInstantiatedInterfaceCall(interfaceTarget, staticReceiverType)) {
final callCpIndex = cp.addInstantiatedInterfaceCall(
invocationKind, interfaceTarget, argDesc, staticReceiverType);
asm.emitInstantiatedInterfaceCall(callCpIndex, totalArgCount);
return;
generated = true;
}
}

final callCpIndex = cp.addInstanceCall(
invocationKind, interfaceTarget, targetName, argDesc);
if (isDynamic) {
assert(!isUnchecked);
asm.emitDynamicCall(callCpIndex, totalArgCount);
} else if (isUnchecked) {
asm.emitUncheckedInterfaceCall(callCpIndex, totalArgCount);
} else {
asm.emitInterfaceCall(callCpIndex, totalArgCount);
if (!generated) {
final callCpIndex = cp.addInstanceCall(
invocationKind, interfaceTarget, targetName, argDesc);
if (isDynamic) {
assert(!isUnchecked);
asm.emitDynamicCall(callCpIndex, totalArgCount);
} else if (isUnchecked) {
asm.emitUncheckedInterfaceCall(callCpIndex, totalArgCount);
} else {
asm.emitInterfaceCall(callCpIndex, totalArgCount);
}
}

if (inferredTypeMetadata != null && node != null) {
_replaceWithConstantValue(node);
}
}

Expand Down Expand Up @@ -3619,6 +3674,13 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
if (target.isConst) {
_genPushConstExpr(target.initializer);
} else if (!_needsGetter(target)) {
if (inferredTypeMetadata != null) {
final InferredType md = inferredTypeMetadata[node];
if (md != null && md.constantValue != null) {
_genPushConstant(md.constantValue);
return;
}
}
asm.emitLoadStatic(cp.addStaticField(target));
} else {
_genDirectCall(target, objectTable.getArgDescHandle(0), 0,
Expand Down Expand Up @@ -4676,7 +4738,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {

@override
visitConstantExpression(ConstantExpression node) {
_genPushConstExpr(node);
_genPushConstant(node.constant);
}
}

Expand Down
57 changes: 43 additions & 14 deletions pkg/vm/lib/metadata/inferred_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:kernel/ast.dart';
/// Metadata for annotating nodes with an inferred type information.
class InferredType {
final Reference _concreteClassReference;
final Constant _constantValue;
final int _flags;

static const int flagNullable = 1 << 0;
Expand All @@ -17,6 +18,8 @@ class InferredType {
// For invocations: whether to use the unchecked entry-point.
static const int flagSkipCheck = 1 << 2;

static const int flagConstant = 1 << 3;

// Entire list may be null if no type arguments were inferred.
// Will always be null if `concreteClass` is null.
//
Expand All @@ -27,22 +30,28 @@ class InferredType {
// argument (in the runtime type) is always exactly a particular `DartType`.
final List<DartType> exactTypeArguments;

InferredType(Class concreteClass, bool nullable, bool isInt,
InferredType(
Class concreteClass, bool nullable, bool isInt, Constant constantValue,
{List<DartType> exactTypeArguments, bool skipCheck: false})
: this._byReference(
getClassReference(concreteClass),
constantValue,
(nullable ? flagNullable : 0) |
(isInt ? flagInt : 0) |
(skipCheck ? flagSkipCheck : 0),
(skipCheck ? flagSkipCheck : 0) |
(constantValue != null ? flagConstant : 0),
exactTypeArguments);

InferredType._byReference(
this._concreteClassReference, this._flags, this.exactTypeArguments) {
InferredType._byReference(this._concreteClassReference, this._constantValue,
this._flags, this.exactTypeArguments) {
assert(exactTypeArguments == null || _concreteClassReference != null);
assert(_constantValue == null || _concreteClassReference != null);
}

Class get concreteClass => _concreteClassReference?.asClass;

Constant get constantValue => _constantValue;

bool get nullable => (_flags & flagNullable) != 0;
bool get isInt => (_flags & flagInt) != 0;
bool get skipCheck => (_flags & flagSkipCheck) != 0;
Expand All @@ -51,17 +60,30 @@ class InferredType {

@override
String toString() {
final base =
"${concreteClass != null ? concreteClass : (isInt ? 'int' : '!')}";
final suffix = "${nullable ? '?' : ''}";
String typeArgs = "";
final StringBuffer buf = new StringBuffer();
if (concreteClass != null) {
buf.write(concreteClass);
} else if (isInt) {
buf.write('int');
} else {
buf.write('!');
}
if (nullable) {
buf.write('?');
}
if (exactTypeArguments != null) {
typeArgs =
exactTypeArguments.map((t) => t != null ? "$t" : "?").join(", ");
typeArgs = "<" + typeArgs + ">";
buf.write('<');
buf.write(
exactTypeArguments.map((t) => t != null ? "$t" : "?").join(", "));
buf.write('>');
}
final skip = skipCheck ? " (skip check)" : "";
return base + suffix + typeArgs + skip;
if (skipCheck) {
buf.write(' (skip check)');
}
if (_constantValue != null) {
buf.write(' (value: $_constantValue)');
}
return buf.toString();
}
}

Expand All @@ -82,6 +104,9 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
sink.writeNullAllowedCanonicalNameReference(
getCanonicalNameOfClass(metadata.concreteClass));
sink.writeByte(metadata._flags);
if (metadata.constantValue != null) {
sink.writeConstantReference(metadata.constantValue);
}
}

@override
Expand All @@ -91,6 +116,10 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
final concreteClassReference =
source.readCanonicalNameReference()?.getReference();
final flags = source.readByte();
return new InferredType._byReference(concreteClassReference, flags, null);
final constantValue = (flags & InferredType.flagConstant) != 0
? source.readConstantReference()
: null;
return new InferredType._byReference(
concreteClassReference, constantValue, flags, null);
}
}
6 changes: 6 additions & 0 deletions pkg/vm/lib/target/vm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class VmTarget extends Target {
Class _oneByteString;
Class _twoByteString;
Class _smi;
Class _double; // _Double, not double.

VmTarget(this.flags);

Expand Down Expand Up @@ -423,6 +424,11 @@ class VmTarget extends Target {
return null;
}

@override
Class concreteDoubleLiteralClass(CoreTypes coreTypes, double value) {
return _double ??= coreTypes.index.getClass('dart:core', '_Double');
}

@override
Class concreteStringLiteralClass(CoreTypes coreTypes, String value) {
const int maxLatin1 = 0xff;
Expand Down
Loading

0 comments on commit f56b0f6

Please sign in to comment.