blob: 877f4ac9a56cd4671f79a1338df2216887f1a53f [file] [log] [blame]
// Copyright (c) 2015, 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 dart2js.cps_ir.loop_invariant_code_motion;
import 'optimizers.dart';
import 'cps_ir_nodes.dart';
import 'loop_hierarchy.dart';
/// Lifts primitives out of loops when it is safe and profitable.
///
/// Currently we only do this for interceptors.
class LoopInvariantCodeMotion extends RecursiveVisitor implements Pass {
String get passName => 'Loop-invariant code motion';
final Map<Primitive, Continuation> primitiveLoop =
<Primitive, Continuation>{};
LoopHierarchy loopHierarchy;
Continuation currentLoopHeader;
/// When processing the dependencies of a primitive, [referencedLoop]
/// refers to the innermost loop that contains one of the dependencies
/// seen so far (or `null` if none of the dependencies are bound in a loop).
///
/// This is used to determine how far the primitive can be lifted without
/// falling out of scope of its dependencies.
Continuation referencedLoop;
void rewrite(FunctionDefinition node) {
loopHierarchy = new LoopHierarchy(node);
visit(node.body);
}
@override
Expression traverseContinuation(Continuation cont) {
Continuation oldLoopHeader = currentLoopHeader;
pushAction(() {
currentLoopHeader = oldLoopHeader;
});
currentLoopHeader = loopHierarchy.getLoopHeader(cont);
for (Parameter param in cont.parameters) {
primitiveLoop[param] = currentLoopHeader;
}
return cont.body;
}
@override
Expression traverseLetPrim(LetPrim node) {
primitiveLoop[node.primitive] = currentLoopHeader;
if (!shouldLift(node.primitive)) {
return node.body;
}
referencedLoop = null;
visit(node.primitive); // Sets referencedLoop.
Expression next = node.body;
if (referencedLoop != currentLoopHeader) {
// Bind the primitive inside [referencedLoop] (possibly null),
// immediately before the binding of the inner loop.
// Find the inner loop.
Continuation loop = currentLoopHeader;
Continuation enclosing = loopHierarchy.getEnclosingLoop(loop);
while (enclosing != referencedLoop) {
assert(loop != null);
loop = enclosing;
enclosing = loopHierarchy.getEnclosingLoop(loop);
}
assert(loop != null);
// Remove LetPrim from its current position.
node.parent.body = node.body;
node.body.parent = node.parent;
// Insert the LetPrim immediately before the loop.
LetCont loopBinding = loop.parent;
InteriorNode newParent = loopBinding.parent;
newParent.body = node;
node.body = loopBinding;
loopBinding.parent = node;
node.parent = newParent;
// A different loop now contains the primitive.
primitiveLoop[node.primitive] = enclosing;
}
return next;
}
/// Returns the the innermost loop that effectively encloses both
/// c1 and c2 (or `null` if there is no such loop).
Continuation leastCommonAncestor(Continuation c1, Continuation c2) {
int d1 = getDepth(c1), d2 = getDepth(c2);
while (c1 != c2) {
if (d1 <= d2) {
c2 = loopHierarchy.getEnclosingLoop(c2);
d2 = getDepth(c2);
} else {
c1 = loopHierarchy.getEnclosingLoop(c1);
d1 = getDepth(c1);
}
}
return c1;
}
@override
void processReference(Reference ref) {
Continuation loop =
leastCommonAncestor(currentLoopHeader, primitiveLoop[ref.definition]);
referencedLoop = getInnerLoop(loop, referencedLoop);
}
int getDepth(Continuation loop) {
if (loop == null) return -1;
return loopHierarchy.loopDepth[loop];
}
Continuation getInnerLoop(Continuation loop1, Continuation loop2) {
if (loop1 == null) return loop2;
if (loop2 == null) return loop1;
if (loopHierarchy.loopDepth[loop1] > loopHierarchy.loopDepth[loop2]) {
return loop1;
} else {
return loop2;
}
}
bool shouldLift(Primitive prim) {
// Interceptors are generally safe and almost always profitable for lifting
// out of loops. Several other primitive could be lifted too, but it's not
// always profitable to do so.
// Note that the type of the interceptor is technically not sound after
// lifting because its current type might have been computed based on a
// refinement node (which has since been removed). As a whole, it still
// works out because all uses of the interceptor must occur in a context
// where it indeed has the type it claims to have.
return prim is Interceptor;
}
}