blob: 4a73e5dcac3a035b550bc552cccecd572956e670 [file] [log] [blame]
library dart2js.cps_ir.loop_effects;
import 'cps_ir_nodes.dart';
import 'loop_hierarchy.dart';
import '../world.dart';
import 'effects.dart';
/// Determines the side effects that may occur in each loop.
class LoopSideEffects extends TrampolineRecursiveVisitor {
LoopHierarchy loopHierarchy;
final World world;
final Map<Continuation, List<Continuation>> exitContinuations = {};
final Map<Continuation, int> loopSideEffects = {};
Continuation currentLoopHeader;
LoopSideEffects(FunctionDefinition node, this.world, {this.loopHierarchy}) {
if (loopHierarchy == null) {
loopHierarchy = new LoopHierarchy(node);
}
visit(node);
}
/// Returns the accumulated effects and dependencies on all paths from the
/// loop entry to any recursive invocation of the loop.
int getSideEffectsInLoop(Continuation loop) {
return loopSideEffects[loop];
}
/// True if the length of an indexable object may change between the loop
/// entry and a recursive invocation of the loop.
bool changesIndexableLength(Continuation loop) {
return loopSideEffects[loop] & Effects.changesIndexableLength != 0;
}
@override
Expression traverseContinuation(Continuation cont) {
if (cont.isRecursive) {
loopSideEffects[cont] = Effects.none;
exitContinuations[cont] = <Continuation>[];
pushAction(() {
if (currentLoopHeader != null) {
loopSideEffects[currentLoopHeader] |= loopSideEffects[cont];
}
exitContinuations[cont].forEach(push);
});
}
Continuation oldLoopHeader = currentLoopHeader;
currentLoopHeader = loopHierarchy.getLoopHeader(cont);
pushAction(() {
currentLoopHeader = oldLoopHeader;
});
return cont.body;
}
@override
Expression traverseLetHandler(LetHandler node) {
enqueueContinuation(node.handler);
return node.body;
}
@override
Expression traverseLetCont(LetCont node) {
node.continuations.forEach(enqueueContinuation);
return node.body;
}
@override
Expression traverseLetPrim(LetPrim node) {
if (currentLoopHeader != null) {
loopSideEffects[currentLoopHeader] |= node.primitive.effects;
}
return node.body;
}
void enqueueContinuation(Continuation cont) {
Continuation loop = loopHierarchy.getEnclosingLoop(cont);
if (loop == currentLoopHeader) {
push(cont);
} else {
// Multiple loops can be exited at once.
// Register as an exit from the outermost loop being exited.
Continuation inner = currentLoopHeader;
Continuation outer = loopHierarchy.getEnclosingLoop(currentLoopHeader);
while (outer != loop) {
if (inner == null) {
// The shrinking reductions pass must run before any pass that relies
// on computing loop side effects.
world.compiler.reporter.internalError(null,
'Unreachable continuations must be removed before computing '
'loop side effects.');
}
inner = outer;
outer = loopHierarchy.getEnclosingLoop(outer);
}
exitContinuations[inner].add(cont);
}
}
}