Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Unified Diff: pkg/compiler/lib/src/cps_ir/type_propagation.dart

Issue 1307303004: dart2js cps: Inline single-use local closures (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
index 5925c5924b3412a361d16fab6c302a64e7dc789a..3212f9406308c66fd34bf3c33220d4125f53e861 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -4,8 +4,10 @@
import 'optimizers.dart';
+import '../closure.dart' show
+ ClosureClassElement, Identifiers;
import '../common/names.dart' show
- Selectors;
+ Selectors, Identifiers;
import '../compiler.dart' as dart2js show
Compiler;
import '../constants/constant_system.dart';
@@ -27,6 +29,7 @@ import '../universe/universe.dart';
import '../world.dart' show World;
import 'cps_fragment.dart';
import 'cps_ir_nodes.dart';
+import 'cps_ir_nodes_sexpr.dart' show SExpressionStringifier;
enum AbstractBool {
True, False, Maybe, Nothing
@@ -1594,6 +1597,70 @@ class TransformingVisitor extends LeafVisitor {
return false;
}
+ /// Inlines a single-use closure if it leaves the closure object with only
+ /// field accesses. This is optimized later by [ScalarReplacer].
+ bool specializeSingleUseClosureCall(InvokeMethod node) {
+ Selector call = node.selector;
+ if (!call.isClosureCall) return false;
+
+ assert(!isInterceptedSelector(call));
+ assert(call.argumentCount == node.arguments.length);
+
+ Primitive receiver = node.receiver.definition;
+ if (receiver is !CreateInstance) return false;
+ CreateInstance createInstance = receiver;
+ if (!createInstance.hasExactlyOneUse) return false;
+
+ // Inline only closures. This avoids inlining the 'call' method of a class
+ // that has many allocation sites.
+ if (createInstance.classElement is !ClosureClassElement) return false;
+
+ ClosureClassElement closureClassElement = createInstance.classElement;
+ Element element = closureClassElement.localLookup(Identifiers.call);
+
+ if (element == null || !element.isFunction) return false;
+ FunctionElement functionElement = element;
+ if (functionElement.asyncMarker != AsyncMarker.SYNC) return false;
+
+ if (!call.signatureApplies(functionElement)) return false;
+ // Inline only for exact match.
+ // TODO(sra): Handle call with defaulted arguments.
+ Selector targetSelector = new Selector.fromElement(functionElement);
+ if (call.callStructure != targetSelector.callStructure) return false;
+
+ FunctionDefinition target =
+ functionCompiler.compileToCpsIR(functionElement);
+
+ // Accesses to closed-over values are field access primitives. We we don't
+ // inline if there are other uses of 'this' since that could be an escape or
+ // a recursive call.
+ for (Reference ref = target.thisParameter.firstRef;
+ ref != null;
+ ref = ref.next) {
+ Node use = ref.parent;
+ if (use is GetField) continue;
+ // Closures do not currently have writable fields, but closure conversion
+ // could esily be changed to allocate some cells in a closure object.
+ if (use is SetField && ref == use.object) continue;
+ return false;
+ }
+
+ // Don't inline if [target] contains try-catch or try-finally. JavaScript
+ // engines typically do poor optimization of the entire function containing
+ // the 'try'.
+ if (ContainsTry.analyze(target)) return false;
+
+ node.receiver.definition.substituteFor(target.thisParameter);
+ for (int i = 0; i < node.arguments.length; ++i) {
+ node.arguments[i].definition.substituteFor(target.parameters[i]);
+ }
+ node.continuation.definition.substituteFor(target.returnContinuation);
+
+ replaceSubtree(node, target.body);
+ push(target.body);
+ return true;
+ }
+
/// Side-effect free expressions with constant results are be replaced by:
///
/// (LetPrim p = constant (InvokeContinuation k p)).
@@ -1618,6 +1685,7 @@ class TransformingVisitor extends LeafVisitor {
if (specializeFieldAccess(node)) return;
if (specializeIndexableAccess(node)) return;
if (specializeArrayAccess(node)) return;
+ if (specializeSingleUseClosureCall(node)) return;
if (specializeClosureCall(node)) return;
AbstractValue receiver = getValue(node.receiver.definition);
@@ -2769,3 +2837,25 @@ class ResetAnalysisInfo extends RecursiveVisitor {
values.remove(node.variable);
}
}
+
+
+class ContainsTry extends RecursiveVisitor {
+ bool _found = false;
+ ContainsTry._();
+
+ /// Scans [root] for evidence of try-catch and try-finally.
+ static bool analyze(Node root) {
+ ContainsTry visitor = new ContainsTry._();
+ visitor.visit(root);
+ return visitor._found;
+ }
+
+ visit(Node node) {
+ if (_found) return; // Early exit if we know the answer.
+ super.visit(node);
+ }
+
+ processLetHandler(LetHandler node) {
+ _found = true;
+ }
+}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698