blob: 85bb7524c7e5205db6b2b124bcdf2cf74b64a1ae [file] [log] [blame]
// Copyright (c) 2012, 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.
class BailoutInfo {
int instructionId;
int bailoutId;
BailoutInfo(this.instructionId, this.bailoutId);
}
/**
* Keeps track of the execution environment for instructions. An
* execution environment contains the SSA instructions that are live.
*/
class Environment {
final Set<HInstruction> lives;
final Set<HBasicBlock> loopMarkers;
Environment() : lives = new Set<HInstruction>(),
loopMarkers = new Set<HBasicBlock>();
Environment.from(Environment other)
: lives = new Set<HInstruction>.from(other.lives),
loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers);
Environment.forLoopBody(Environment other)
: lives = new Set<HInstruction>(),
loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers);
void remove(HInstruction instruction) {
lives.remove(instruction);
}
void add(HInstruction instruction) {
if (!instruction.isCodeMotionInvariant()) {
lives.add(instruction);
} else {
for (int i = 0, len = instruction.inputs.length; i < len; i++) {
add(instruction.inputs[i]);
}
}
}
void addLoopMarker(HBasicBlock block) {
loopMarkers.add(block);
}
void removeLoopMarker(HBasicBlock block) {
loopMarkers.remove(block);
}
void addAll(Environment other) {
lives.addAll(other.lives);
loopMarkers.addAll(other.loopMarkers);
}
List<HInstruction> buildAndSetLast(HInstruction instruction) {
remove(instruction);
List<HInstruction> result = new List<HInstruction>.from(lives);
result.addLast(instruction);
add(instruction);
return result;
}
bool isEmpty() => lives.isEmpty();
bool contains(HInstruction instruction) => lives.contains(instruction);
bool containsLoopMarker(HBasicBlock block) => loopMarkers.contains(block);
void clear() => lives.clear();
}
/**
* Computes the environment for each SSA instruction: visits the graph
* in post-dominator order. Removes an instruction from the environment
* and adds its inputs to the environment at the instruction's
* definition.
*
* At the end of the computation, insert type guards in the graph.
*/
class SsaTypeGuardBuilder extends HBaseVisitor implements OptimizationPhase {
final Compiler compiler;
final WorkItem work;
final String name = 'SsaTypeGuardBuilder';
Environment environment;
SubGraph subGraph;
final Map<HInstruction, Environment> capturedEnvironments;
SsaTypeGuardBuilder(Compiler this.compiler, WorkItem this.work)
: capturedEnvironments = new Map<HInstruction, Environment>();
void visitGraph(HGraph graph) {
subGraph = new SubGraph(graph.entry, graph.exit);
environment = new Environment();
visitBasicBlock(graph.entry);
if (!environment.isEmpty()) {
compiler.internalError('Bailout environment computation',
node: compiler.currentElement.parseNode(compiler));
}
insertCapturedEnvironments();
}
void maybeCaptureEnvironment(HInstruction instruction) {
if (shouldCaptureEnvironment(instruction)) {
capturedEnvironments[instruction] = new Environment.from(environment);
}
}
void visitSubGraph(SubGraph newSubGraph) {
SubGraph oldSubGraph = subGraph;
subGraph = newSubGraph;
visitBasicBlock(subGraph.start);
subGraph = oldSubGraph;
}
void visitBasicBlock(HBasicBlock block) {
if (!subGraph.contains(block)) return;
block.last.accept(this);
HInstruction instruction = block.last.previous;
while (instruction != null) {
HInstruction previous = instruction.previous;
instruction.accept(this);
instruction = previous;
}
for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
phi.accept(this);
}
if (block.isLoopHeader()) {
// If the block is a loop header, we need to change every uses
// of its loop marker to the current set of live instructions.
// For example with the following loop (read the example in
// reverse):
//
// while (true) { <-- (4) update the marker with the environment
// use(x); <-- (3) environment = {x}
// bailout; <-- (2) has the marker when computed
// } <-- (1) create a loop marker
//
// The bailout instruction first captures the marker, but it
// will be replaced by the live environment at the loop entry,
// in this case {x}.
environment.removeLoopMarker(block);
capturedEnvironments.forEach((instruction, env) {
if (env.containsLoopMarker(block)) {
env.removeLoopMarker(block);
env.addAll(environment);
}
});
}
}
void visitPhi(HPhi phi) {
maybeCaptureEnvironment(phi);
environment.remove(phi);
// If the block is a loop header, we insert the incoming values of
// the phis, and remove the loop values.
// If the block is not a loop header, the phi will be handled by
// the control flow instruction.
if (phi.block.isLoopHeader()) {
environment.add(phi.inputs[0]);
for (int i = 1, len = phi.inputs.length; i < len; i++) {
environment.remove(phi.inputs[i]);
}
}
}
void visitInstruction(HInstruction instruction) {
maybeCaptureEnvironment(instruction);
environment.remove(instruction);
for (int i = 0, len = instruction.inputs.length; i < len; i++) {
environment.add(instruction.inputs[i]);
}
}
void visitIf(HIf instruction) {
HIfBlockInformation info = instruction.blockInformation;
HBasicBlock joinBlock = info.joinBlock;
if (joinBlock != null) {
visitBasicBlock(joinBlock);
}
Environment thenEnvironment = new Environment.from(environment);
Environment elseEnvironment = environment;
if (joinBlock != null) {
for (HPhi phi = joinBlock.phis.first; phi != null; phi = phi.next) {
if (joinBlock.predecessors[0] == instruction.block) {
// We're dealing with an 'if' without an else branch.
thenEnvironment.add(phi.inputs[1]);
elseEnvironment.add(phi.inputs[0]);
} else {
thenEnvironment.add(phi.inputs[0]);
elseEnvironment.add(phi.inputs[1]);
}
}
}
if (instruction.hasElse) {
environment = elseEnvironment;
visitSubGraph(info.elseGraph);
elseEnvironment = environment;
}
environment = thenEnvironment;
visitSubGraph(info.thenGraph);
environment.addAll(elseEnvironment);
}
void visitGoto(HGoto goto) {
HBasicBlock block = goto.block;
if (block.successors[0].dominator != block) return;
visitBasicBlock(block.successors[0]);
}
void visitBreak(HBreak breakInstruction) {
compiler.unimplemented("SsaEnvironmentBuilder.visitBreak");
}
void visitLoopBranch(HLoopBranch branch) {
HBasicBlock block = branch.block;
// Visit the code after the loop.
visitBasicBlock(block.successors[1]);
Environment joinEnvironment = environment;
// When visiting the loop body, we don't require the live
// instructions after the loop body to be in the environment. They
// will be either recomputed in the loop header, or inserted
// with the loop marker. We still need to transfer existing loop
// markers from the current environment, because they must be live
// for this loop body.
environment = new Environment.forLoopBody(environment);
// Put the loop phis in the environment.
HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
for (HPhi phi = header.phis.first; phi != null; phi = phi.next) {
for (int i = 1, len = phi.inputs.length; i < len; i++) {
environment.add(phi.inputs[i]);
}
}
// Add the loop marker
environment.addLoopMarker(header);
if (!branch.isDoWhile()) {
assert(block.successors[0] == block.dominatedBlocks[0]);
visitBasicBlock(block.successors[0]);
}
// We merge the environment required by the code after the loop,
// and the code inside the loop.
environment.addAll(joinEnvironment);
}
// Deal with all kinds of control flow instructions. In case we add
// a new one, we will hit an internal error.
void visitExit(HExit exit) {}
void visitReturn(HReturn instruction) {
environment.clear();
visitInstruction(instruction);
}
void visitThrow(HThrow instruction) {
environment.clear();
visitInstruction(instruction);
}
void visitControlFlow(HControlFlow instruction) {
compiler.internalError('Control flow instructions already dealt with.',
instruction: instruction);
}
bool shouldCaptureEnvironment(HInstruction instruction) {
return instruction.type.isKnown() && !instruction.hasExpectedType();
}
void insertCapturedEnvironments() {
work.guards = <HTypeGuard>[];
int state = 1;
capturedEnvironments.forEach((HInstruction instruction, Environment env) {
List<HInstruction> inputs = env.buildAndSetLast(instruction);
HTypeGuard guard = new HTypeGuard(state++, inputs);
work.guards.add(guard);
instruction.block.rewrite(instruction, guard);
HInstruction insertionPoint = (instruction is HPhi)
? instruction.block.first
: instruction.next;
insertionPoint.block.addBefore(insertionPoint, guard);
});
}
}
/**
* Propagates bailout information to blocks that need it. This visitor
* is run before codegen, to know which blocks have to deal with
* bailouts.
*/
class SsaBailoutPropagator extends HBaseVisitor {
final Compiler compiler;
final List<HBasicBlock> blocks;
SsaBailoutPropagator(Compiler this.compiler) : blocks = <HBasicBlock>[];
void visitGraph(HGraph graph) {
blocks.addLast(graph.entry);
visitBasicBlock(graph.entry);
}
void visitBasicBlock(HBasicBlock block) {
if (block.isLoopHeader()) blocks.addLast(block);
HInstruction instruction = block.first;
while (instruction != null) {
instruction.accept(this);
instruction = instruction.next;
}
}
void enterBlock(HBasicBlock block) {
if (block == null) return;
blocks.addLast(block);
visitBasicBlock(block);
blocks.removeLast();
}
void visitIf(HIf instruction) {
enterBlock(instruction.thenBlock);
enterBlock(instruction.elseBlock);
enterBlock(instruction.joinBlock);
}
void visitGoto(HGoto goto) {
HBasicBlock block = goto.block;
if (block.successors[0].dominator != block) return;
visitBasicBlock(block.successors[0]);
}
void visitLoopBranch(HLoopBranch branch) {
HBasicBlock branchBlock = branch.block;
if (!branch.isDoWhile()) {
// Not a do while loop. We visit the body of the loop.
visitBasicBlock(branchBlock.dominatedBlocks[0]);
}
blocks.removeLast();
visitBasicBlock(branchBlock.successors[1]);
}
// Deal with all kinds of control flow instructions. In case we add
// a new one, we will hit an internal error.
void visitExit(HExit exit) {}
void visitReturn(HReturn instruction) {}
void visitThrow(HThrow instruction) {}
void visitControlFlow(HControlFlow instruction) {
compiler.internalError('Control flow instructions already dealt with.',
instruction: instruction);
}
visitTypeGuard(HTypeGuard guard) {
blocks.forEach((HBasicBlock block) {
block.guards.add(guard);
});
}
}