Files
sdk/pkg/kernel/lib/transformations/closure/rewriter.dart
T
Samir Jindel f0941f7c7d Improve the performance of closure-converted code.
Summary:

Previously, we would create a wrapper function in the flowgraph around converted
closures, which would forward all the closure's arguments and unpack the context
argument before calling the real closure function.

Now, we perform the unpacking at the top of the real function to avoid having
any wrapper function.

Previously, captured parameters would still be appear live to the GC even if
they're updated, because after they are copied into the context, all updates to
them are done there.

Now, as in regular closures, we zero-out the parameter variable after copying
it's value into the context, avoiding potential memory leaks.

Test Plan:

Ran the closure conversion test suite.
Ran benchmarks on Golem -- all statistically significant regressions are gone.

BUG=
R=dmitryas@google.com, regis@google.com

Review-Url: https://codereview.chromium.org/3008923002 .
2017-09-01 14:59:40 +02:00

133 lines
4.5 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.closure.rewriter;
import '../../ast.dart';
import 'converter.dart' show ClosureConverter;
/// Used by the [Context] to initialize and update the context variable
/// used to capture the variables closed over by functions.
abstract class AstRewriter {
/// The declared variable that holds the context.
VariableDeclaration contextDeclaration;
/// Expression that is used to initialize the vector representing the context.
/// It's [length] field is modified by the [extend] operation
VectorCreation vectorCreation;
/// Creates a new [AstRewriter] for a (nested) [Block].
BlockRewriter forNestedBlock(Block block);
/// Inserts an allocation of a context and initializes [contextDeclaration]
/// and [vectorCreation].
void insertContextDeclaration(Expression accessParent);
/// Inserts an expression or statement that extends the context.
void insertExtendContext(VectorSet extender);
/// Inserts an expression that sets a parameter to NULL, so we don't have
/// unnecessary references to it.
void insertZeroOutParameter(VariableDeclaration parameter);
void _createDeclaration() {
assert(contextDeclaration == null && vectorCreation == null);
// Context size is set to 2 initially, because the 0-th element of it holds
// the vector of type arguments that the VM creates, and the 1-st element
// works as a link to the parent context.
vectorCreation = new VectorCreation(2);
contextDeclaration = new VariableDeclaration.forValue(vectorCreation,
type: const DynamicType());
contextDeclaration.name = "#context";
}
}
/// Adds a local variable for the context and adds update [Statement]s to the
/// current block.
class BlockRewriter extends AstRewriter {
Block _currentBlock;
int _insertionIndex;
BlockRewriter(this._currentBlock) : _insertionIndex = 0;
BlockRewriter forNestedBlock(Block block) {
return _currentBlock != block ? new BlockRewriter(block) : this;
}
void transformStatements(ClosureConverter converter) {
while (_insertionIndex < _currentBlock.statements.length) {
var original = _currentBlock.statements[_insertionIndex];
var transformed = original.accept(converter);
assert(_currentBlock.statements[_insertionIndex] == original);
if (transformed == null) {
_currentBlock.statements.removeAt(_insertionIndex);
} else {
_currentBlock.statements[_insertionIndex++] = transformed;
transformed.parent = _currentBlock;
}
}
}
void _insertStatement(Statement statement) {
_currentBlock.statements.insert(_insertionIndex++, statement);
statement.parent = _currentBlock;
}
void insertContextDeclaration(Expression accessParent) {
_createDeclaration();
_insertStatement(contextDeclaration);
if (accessParent is! NullLiteral) {
// Index 1 of a context always points to the parent.
_insertStatement(new ExpressionStatement(
new VectorSet(new VariableGet(contextDeclaration), 1, accessParent)));
}
}
void insertExtendContext(VectorSet extender) {
_insertStatement(new ExpressionStatement(extender));
}
void insertZeroOutParameter(VariableDeclaration parameter) {
_insertStatement(
new ExpressionStatement(new VariableSet(parameter, new NullLiteral())));
}
}
class InitializerListRewriter extends AstRewriter {
final Constructor parentConstructor;
final List<Initializer> prefix = [];
InitializerListRewriter(this.parentConstructor);
@override
BlockRewriter forNestedBlock(Block block) {
return new BlockRewriter(block);
}
@override
void insertContextDeclaration(Expression accessParent) {
_createDeclaration();
var init = new LocalInitializer(contextDeclaration);
init.parent = parentConstructor;
prefix.add(init);
}
@override
void insertExtendContext(VectorSet extender) {
var init = new LocalInitializer(
new VariableDeclaration(null, initializer: extender));
init.parent = parentConstructor;
prefix.add(init);
}
@override
void insertZeroOutParameter(VariableDeclaration parameter) {
var init = new LocalInitializer(new VariableDeclaration(null,
initializer: new VariableSet(parameter, new NullLiteral())));
init.parent = parentConstructor;
prefix.add(init);
}
}