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

Unified Diff: lib/compiler/implementation/ssa/variable_allocator.dart

Issue 10440087: Compute liveness of instructions to get better and fewer variable names. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 7 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 | « lib/compiler/implementation/ssa/ssa.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/compiler/implementation/ssa/variable_allocator.dart
===================================================================
--- lib/compiler/implementation/ssa/variable_allocator.dart (revision 0)
+++ lib/compiler/implementation/ssa/variable_allocator.dart (revision 0)
@@ -0,0 +1,572 @@
+// 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.
+
+/**
+ * The [LiveRange] class covers a range where an instruction is live.
+ */
+class LiveRange {
+ final int start;
+ // [end] is not final because it can be updated due to loops.
+ int end;
+ LiveRange(this.start, this.end) {
+ assert(start <= end);
+ }
+
+ String toString() => '[$start $end[';
+}
+
+/**
+ * The [LiveInterval] class contains the list of ranges where an
+ * instruction is live.
+ */
+class LiveInterval {
+ /**
+ * The id where there instruction is defined.
+ */
+ int start;
+ final List<LiveRange> ranges;
+ LiveInterval() : ranges = <LiveRange>[];
+
+ /**
+ * Update all ranges that are contained in [start, end[ to
+ * die at [end].
+ */
+ void loopUpdate(int start, int end) {
+ for (LiveRange range in ranges) {
+ if (start <= range.start && range.end < end) {
+ range.end = end;
+ }
+ }
+ }
+
+ /**
+ * Add a new range to this interval.
+ */
+ void add(LiveRange interval) {
+ ranges.add(interval);
+ }
+
+ /**
+ * Returns true if one of the ranges of this interval dies at [at].
+ */
+ bool diesAt(int at) {
+ for (LiveRange range in ranges) {
+ if (range.end == at) return true;
+ }
+ return false;
+ }
+
+ String toString() {
+ List<String> res = new List<String>();
+ for (final interval in ranges) res.add(interval.toString());
+ return '(${Strings.join(res, ', ')})';
+ }
+}
+
+/**
+ * The [LiveEnvironment] class contains the liveIn set of a basic
+ * block. A liveIn set of a block contains the instructions that are
+ * live when entering that block.
+ */
+class LiveEnvironment {
+ /**
+ * The instruction id where the basic block starts. See
+ * [SsaLiveIntervalBuilder.instructionId].
+ */
+ int startId;
+
+ /**
+ * The instruction id where the basic block ends.
+ */
+ final int endId;
+
+ /**
+ * Loop markers that will be updated once the loop header is
+ * visited. The liveIn set of the loop header will be merged into this
+ * environment. [loopMarkers] is a mapping from block header to the
+ * end instruction id of the loop exit block.
+ */
+ final Map<HBasicBlock, int> loopMarkers;
+
+ /**
+ * The instructions that are live in this basic block. The values of
+ * the map contain the instruction ids where the instructions die.
+ * It will be used when adding a range to the live interval of an
+ * instruction.
+ */
+ final Map<HInstruction, int> liveInstructions;
+
+ /**
+ * Map containing the live intervals of instructions.
+ */
+ final Map<HInstruction, LiveInterval> liveIntervals;
+
+ LiveEnvironment(this.liveIntervals, this.endId)
+ : liveInstructions = new Map<HInstruction, int>(),
+ loopMarkers = new Map<HBasicBlock, int>();
+
+ /**
+ * Remove an instruction from the liveIn set. This method also
+ * updates the live interval of [instruction] to contain the new
+ * range: [id, / id contained in [liveInstructions] /].
+ */
+ void remove(HInstruction instruction, int id) {
+ // Special case the HCheck instruction to have the same live
+ // interval as the instruction it is checking.
+ if (instruction is HCheck) {
+ HInstruction input = instruction.checkedInput;
+ while (input is HCheck) input = input.checkedInput;
+ liveIntervals.putIfAbsent(input, () => new LiveInterval());
+ liveIntervals.putIfAbsent(instruction, () => liveIntervals[input]);
+ return;
+ }
+ LiveInterval range = liveIntervals.putIfAbsent(
+ instruction, () => new LiveInterval());
+ int lastId = liveInstructions[instruction];
+ // If [lastId] is null, then this instruction is not being used.
+ range.add(new LiveRange(id, lastId == null ? id : lastId));
+ // The instruction is defined at [id].
+ range.start = id;
+ liveInstructions.remove(instruction);
+ }
+
+ /**
+ * Add [instruction] to the liveIn set. If the instruction is not
+ * already in the set, we save the id where it dies.
+ */
+ void add(HInstruction instruction, int userId) {
+ // Special case the HCheck instruction to use the actual checked
+ // instruction.
+ while (instruction is HCheck) instruction = instruction.checkedInput;
+ // Note that we are visiting the grap in post-dominator order, so
+ // the first time we see a variable is when it dies.
+ liveInstructions.putIfAbsent(instruction, () => userId);
+ }
+
+ /**
+ * Merge this environment with [other]. Update the end id of
+ * instructions in case they are different between this and [other].
+ */
+ void mergeWith(LiveEnvironment other) {
+ other.liveInstructions.forEach((HInstruction instruction, int existingId) {
+ // If both environments have the same instruction id of where
+ // [instruction] dies, there is no need to update the live
+ // interval of [instruction]. For example the if block and the
+ // else block have the same end id for an instruction that is
+ // being used in the join block and defined before the if/else.
+ if (existingId == endId) return;
+ LiveInterval range = liveIntervals.putIfAbsent(
+ instruction, () => new LiveInterval());
+ range.add(new LiveRange(other.startId, existingId));
+ liveInstructions[instruction] = endId;
+ });
+ other.loopMarkers.forEach((k, v) { loopMarkers[k] = v; });
+ }
+
+ void addLoopMarker(HBasicBlock header, int id) {
+ assert(!loopMarkers.containsKey(header));
+ loopMarkers[header] = id;
+ }
+
+ void removeLoopMarker(HBasicBlock header) {
+ assert(loopMarkers.containsKey(header));
+ loopMarkers.remove(header);
+ }
+
+ bool isEmpty() => liveInstructions.isEmpty() && loopMarkers.isEmpty();
+ bool contains(HInstruction instruction) => liveInstructions.containsKey(instruction);
+ String toString() => liveInstructions.toString();
+}
+
+/**
+ * Builds the live intervals of each instruction. The algorithm visits
+ * the graph post-dominator tree to find the last uses of an
+ * instruction, and computes the liveIns of each basic block.
+ */
+class SsaLiveIntervalBuilder extends HBaseVisitor {
+ final Compiler compiler;
+
+ /**
+ * A counter to assign start and end ids to live ranges. The initial
+ * value is not relevant. Note that instructionId goes downward to ease
+ * reasoning about live ranges (the first instruction of a graph has
+ * the lowest id).
+ */
+ int instructionId = 0;
+
+ /**
+ * The liveIns of basic blocks.
+ */
+ final Map<HBasicBlock, LiveEnvironment> liveInstructions;
+
+ /**
+ * The live intervals of instructions.
+ */
+ final Map<HInstruction, LiveInterval> liveIntervals;
+
+ SsaLiveIntervalBuilder(this.compiler)
+ : liveInstructions = new Map<HBasicBlock, LiveEnvironment>(),
+ liveIntervals = new Map<HInstruction, LiveInterval>();
+
+ void visitGraph(HGraph graph) {
+ visitPostDominatorTree(graph);
+ if (!liveInstructions[graph.entry].isEmpty()) {
+ compiler.internalError('LiveIntervalBuilder',
+ node: compiler.currentElement.parseNode(compiler));
+ }
+ }
+
+ void visitBasicBlock(HBasicBlock block) {
+ LiveEnvironment environment = new LiveEnvironment(liveIntervals, instructionId);
+
+ // Add to the environment the liveIn of its successor, as well as
+ // the inputs of the phis of the successor that flow from this block.
+ for (int i = 0; i < block.successors.length; i++) {
+ HBasicBlock successor = block.successors[i];
+ LiveEnvironment successorEnv = liveInstructions[successor];
+ if (successorEnv !== null) {
+ environment.mergeWith(successorEnv);
+ } else {
+ environment.addLoopMarker(successor, instructionId);
+ }
+
+ int index = successor.predecessors.indexOf(block);
+ for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) {
+ environment.add(phi.inputs[index], instructionId);
+ }
+ }
+
+ // Iterate over all instructions to remove an instruction from the
+ // environment and add its inputs.
+ HInstruction instruction = block.last;
+ while (instruction != null) {
+ environment.remove(instruction, instructionId);
+ for (int i = 0, len = instruction.inputs.length; i < len; i++) {
+ environment.add(instruction.inputs[i], instructionId);
+ }
+ instruction = instruction.previous;
+ instructionId--;
+ }
+
+ // We just remove the phis from the environment. The inputs of the
+ // phis will be put in the environment of the predecessors.
+ for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
+ environment.remove(phi, instructionId);
+ }
+
+ // Save the liveInstructions of that block.
+ environment.startId = instructionId + 1;
+ liveInstructions[block] = environment;
+
+ // If the block is a loop header, we can remove the loop marker,
+ // because it will just recompute the loop phis.
+ if (block.isLoopHeader()) {
+ updateLoopMarker(block);
+ }
+ }
+
+ void updateLoopMarker(HBasicBlock header) {
+ LiveEnvironment env = liveInstructions[header];
+ int lastId = env.loopMarkers[header];
+ // Update all instructions that are liveIns in [header] to have a
+ // range that covers the loop.
+ env.liveInstructions.forEach((HInstruction instruction, int id) {
+ LiveInterval range = env.liveIntervals.putIfAbsent(
+ instruction, () => new LiveInterval());
+ range.loopUpdate(env.startId, lastId);
+ env.liveInstructions[instruction] = lastId;
+ });
+
+ env.removeLoopMarker(header);
+
+ // Update all liveIns set to contain the liveIns of [header].
+ liveInstructions.forEach((HBasicBlock block, LiveEnvironment other) {
+ if (other.loopMarkers.containsKey(header)) {
+ env.liveInstructions.forEach((HInstruction instruction, int id) {
+ other.liveInstructions[instruction] = id;
+ });
+ other.removeLoopMarker(header);
+ env.loopMarkers.forEach((k, v) { other.loopMarkers[k] = v; });
+ }
+ });
+ }
+}
+
+/**
+ * Represents a copy from one instruction to another. The codegen
+ * also uses this class to represent a copy from one variable to
+ * another.
+ */
+class Copy {
+ final source;
+ final destination;
+ Copy(this.source, this.destination);
+ String toString() => '$destination <- $source';
+}
+
+/**
+ * A copy handler contains the copies that a basic block needs to do
+ * after executing all its instructions.
+ */
+class CopyHandler {
+ /**
+ * The copies from an instruction to a phi of the successor.
+ */
+ final List<Copy> copies;
+
+ /**
+ * Assignments from an instruction that does not need a name (e.g. a
+ * constant) to the phi of a successor.
+ */
+ final List<Copy> assignments;
+
+ CopyHandler()
+ : copies = new List<Copy>(),
+ assignments = new List<Copy>();
+
+ void addCopy(HInstruction source, HInstruction destination) {
+ copies.add(new Copy(source, destination));
+ }
+
+ void addAssignment(HInstruction source, HInstruction destination) {
+ assignments.add(new Copy(source, destination));
+ }
+
+ String toString() => 'Copies: $copies, assignments: $assignments';
+}
+
+/**
+ * Contains the mapping between instructions and their names for code
+ * generation, as well as the [CopyHandler] for each basic block.
+ */
+class VariableNames {
+ final Map<HInstruction, String> ownName;
+ final Map<HBasicBlock, CopyHandler> copyHandlers;
+ /**
+ * Name that is being used as a temporary to break cycles in
+ * parallel copies. We make sure this name is not being used
+ * anywhere by reserving it when we allocate names for instructions.
+ * TODO(ngeoffray): This isn't true for parameters, and we should
+ * rename the parameter name, to keep it simple.
+ */
+ static final String SWAP_TEMP = 't0';
+
+ VariableNames()
+ : ownName = new Map<HInstruction, String>(),
+ copyHandlers = new Map<HBasicBlock, CopyHandler>();
+
+ String getName(HInstruction instruction) {
+ return ownName[instruction];
+ }
+
+ CopyHandler getCopyHandler(HBasicBlock block) {
+ return copyHandlers[block];
+ }
+
+ bool hasName(HInstruction instruction) => ownName.containsKey(instruction);
+
+ void addCopy(HBasicBlock block, HInstruction source, HPhi destination) {
+ CopyHandler handler =
+ copyHandlers.putIfAbsent(block, () => new CopyHandler());
+ handler.addCopy(source, destination);
+ }
+
+ void addAssignment(HBasicBlock block, HInstruction source, HPhi destination) {
+ CopyHandler handler =
+ copyHandlers.putIfAbsent(block, () => new CopyHandler());
+ handler.addAssignment(source, destination);
+ }
+}
+
+/**
+ * Allocates variable names for instructions, making sure they don't collide.
+ */
+class VariableNamer {
+ final VariableNames names;
+ final Set<String> usedNames;
+
+ VariableNamer(LiveEnvironment environment, this.names)
+ : usedNames = new Set<String>() {
+ // [VariableNames.SWAP_TEMP] is being used when there is a cycle
+ // in a copy handler. Therefore we make sure no one will use it.
+ usedNames.add(VariableNames.SWAP_TEMP);
+
+ // All liveIns instructions must have a name at this point, so we
+ // add them to the list of used names.
+ environment.liveInstructions.forEach((HInstruction instruction, int index) {
+ String name = names.getName(instruction);
+ if (name !== null) {
+ usedNames.add(name);
+ }
+ });
+ }
+
+ String allocateWithHint(String originalName) {
+ int i = 0;
+ String name = JsNames.getValid(originalName);
+ while (usedNames.contains(name)) {
+ name = JsNames.getValid('$originalName${i++}');
+ }
+ return name;
+ }
+
+ String allocateTemporary() {
+ // Don't start at 0 because t0 is being used for
+ // [VariableNames.SWAP_TEMP].
+ int i = 1;
+ String name = 't${i++}';
+ while (usedNames.contains(name)) name = 't${i++}';
+ return name;
+ }
+
+ HPhi firstPhiUserWithElement(HInstruction instruction) {
+ for (HInstruction user in instruction.usedBy) {
+ if (user is HPhi && user.sourceElement !== null) {
+ return user;
+ }
+ }
+ return null;
+ }
+
+ String allocateName(HInstruction instruction) {
+ String name;
+ if (instruction is HCheck) {
+ // Special case the check instruction to use the name of its
+ // checked instruction.
+ HCheck check = instruction;
+ name = names.ownName[check.checkedInput];
+ // If the name is null, then the checked input is being
+ // generated at use site, and we don't need a name for the check
+ // instruction.
+ if (name == null) return;
+ } else if (instruction is HParameterValue) {
+ HParameterValue parameter = instruction;
+ name = allocateWithHint(parameter.element.name.slowToString());
+ } else if (instruction.sourceElement !== null) {
+ name = allocateWithHint(instruction.sourceElement.name.slowToString());
+ } else {
+ // We could not find an element for the instruction. If the
+ // instruction is used by a phi, try to use the name of the phi.
+ // Otherwise, just allocate a temporary name.
+ HPhi phi = firstPhiUserWithElement(instruction);
+ if (phi !== null) {
+ name = allocateWithHint(phi.sourceElement.name.slowToString());
+ } else {
+ name = allocateTemporary();
+ }
+ }
+ usedNames.add(name);
+ names.ownName[instruction] = name;
+ return name;
+ }
+
+ /**
+ * Frees [instruction]'s name so it can be used for other instructions.
+ */
+ void freeName(HInstruction instruction) {
+ String ownName = names.ownName[instruction];
+ if (ownName != null) {
+ usedNames.remove(ownName);
+ }
+ }
+}
+
+/**
+ * Visits all blocks in the graph, sets names to instructions, and
+ * creates the [CopyHandler] for each block. This class needs to have
+ * the liveIns set as well as all the live intervals of instructions.
+ * It visits the graph in dominator order, so that at each entry of a
+ * block, the instructions in its liveIns set have names.
+ *
+ * When visiting a block, it goes through all instructions. For each
+ * instruction, it frees the names of the inputs that die at that
+ * instruction, and allocates a name to the instruction. For each phi,
+ * it adds a copy to the CopyHandler of the corresponding predecessor.
+ */
+class SsaVariableAllocator extends HBaseVisitor {
+
+ final Compiler compiler;
+ final Map<HBasicBlock, LiveEnvironment> liveInstructions;
+ final Map<HInstruction, LiveInterval> liveIntervals;
+ final Set<HInstruction> generateAtUseSite;
+
+ final VariableNames names;
+
+ SsaVariableAllocator(this.compiler,
+ this.liveInstructions,
+ this.liveIntervals,
+ this.generateAtUseSite)
+ : names = new VariableNames();
+
+ void visitGraph(HGraph graph) {
+ visitDominatorTree(graph);
+ }
+
+ void visitBasicBlock(HBasicBlock block) {
+ VariableNamer namer = new VariableNamer(liveInstructions[block], names);
+
+ block.forEachPhi((HPhi phi) {
+ handlePhi(phi, namer);
+ });
+
+ block.forEachInstruction((HInstruction instruction) {
+ handleInstruction(instruction, namer);
+ });
+ }
+
+ /**
+ * Returns whether [instruction] needs a name. Instructions that
+ * have no users or that are generated at use site does not need a name.
+ */
+ bool needsName(HInstruction instruction) {
+ if (instruction.usedBy.isEmpty()) return false;
+ // TODO(ngeoffray): parameters are being generated at use site,
+ // but we need a name for parameters. We should probably not make
+ // them generate at use site to make things simpler.
+ if (instruction is HParameterValue && instruction is !HThis) return true;
+ if (generateAtUseSite.contains(instruction)) return false;
+ return true;
+ }
+
+ /**
+ * Returns whether [instruction] dies at the instruction [at].
+ */
+ bool diesAt(HInstruction instruction, HInstruction at) {
+ LiveInterval atInterval = liveIntervals[at];
+ LiveInterval instructionInterval = liveIntervals[instruction];
+ int start = atInterval.start;
+ return instructionInterval.diesAt(start);
+ }
+
+ void handleInstruction(HInstruction instruction, VariableNamer namer) {
+ for (int i = 0, len = instruction.inputs.length; i < len; i++) {
+ HInstruction input = instruction.inputs[i];
+ // If [input] has a name, and its use here is the last use, free
+ // its name.
+ if (needsName(input) && diesAt(input, instruction)) {
+ namer.freeName(input);
+ }
+ }
+
+ if (needsName(instruction)) {
+ namer.allocateName(instruction);
+ }
+ }
+
+ void handlePhi(HPhi phi, VariableNamer namer) {
+ if (!needsName(phi)) return;
+
+ for (int i = 0; i < phi.inputs.length; i++) {
+ HInstruction input = phi.inputs[i];
+ HBasicBlock predecessor = phi.block.predecessors[i];
+ if (!needsName(input)) {
+ names.addAssignment(predecessor, input, phi);
+ } else {
+ names.addCopy(predecessor, input, phi);
+ }
+ }
+
+ namer.allocateName(phi);
+ }
+}
« no previous file with comments | « lib/compiler/implementation/ssa/ssa.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698