/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.instrsched;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.depgraph.DepGraph;
import org.jikesrvm.compilers.opt.depgraph.DepGraphNode;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
import org.jikesrvm.compilers.opt.liveness.LiveAnalysis;
import org.jikesrvm.compilers.opt.util.GraphNodeEnumeration;
/**
* Instruction Scheduler
*
* It is a simple list scheduler
*
* This class is declared as "final" which implies that all its methods
* are "final" too.
*
* TODO:
* - Add more priority lists
*
* - When scheduling an instruction, verify that its predecessors have
* already been scheduled.
*
* - Change forward propagation of earliest time to computing it from the
* scheduling time of predecessors + latencies.
*
* - Change bubble sort to insertion sort.
*/
final class Scheduler {
/**
* Debugging level.
*/
private static final int VERBOSE = 0;
/**
* Should we print the length of the critical path for each basic block?
*/
private static final boolean PRINT_CRITICAL_PATH_LENGTH = false;
/**
* A constant signifying pre-pass scheduling phase.
*/
public static final int PREPASS = 1;
/**
* A constant signifying post-pass scheduling phase.
* WARNING: POSTPASS INSTRUCTION SCHEDULING (AFTER REGISTER ALLOCATION)
* Cannot be done safely due to failure to update GCMapping information.
* --dave.
*/
public static final int POSTPASS = 2;
/**
* Names of various scheduling phases.
*/
public static final String[] PhaseName = new String[]{"Invalid Phase!!!!!!!!", "pre-pass", "post-pass"};
/**
* Current phase (prepass/postpass).
*/
private final int phase;
/**
* Current IR.
*/
private IR ir;
/**
* Current basic block.
*/
private BasicBlock bb;
/**
* Dependence graph for current basic block.
*/
private DepGraph dg;
/**
* Mapping from Instruction to DepGraphNode.
*/
private DepGraphNode[] i2gn;
/**
* Should we print the dependence graph?
* @param options the options object
* @return true if we should print depgraph, false otherwise
*/
private boolean printDepgraph(OptOptions options) {
return (phase == PREPASS && options.PRINT_DG_SCHED_PRE) || (phase == POSTPASS && options.PRINT_DG_SCHED_POST);
}
/**
* For each basic block, build the dependence graph and
* perform instruction scheduling.
* This is an MIR to MIR transformation.
*
* @param _ir the IR in question
*/
void perform(IR _ir) {
// Remember the ir to schedule
ir = _ir;
if (VERBOSE >= 1) {
debug("Scheduling " +
ir.method.getDeclaringClass() +
' ' +
ir.method.getName() +
' ' +
ir.method.getDescriptor());
}
// Performing live analysis may reduce dependences between PEIs and stores
if (ir.options.L2M_HANDLER_LIVENESS) {
new LiveAnalysis(false, false, true).perform(ir);
}
// Create mapping for dependence graph
i2gn = new DepGraphNode[ir.numberInstructions()];
// Create scheduling info for each instruction
for (InstructionEnumeration instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
SchedulingInfo.createInfo(instr.next());
}
// iterate over each basic block
for (BasicBlockEnumeration e = ir.getBasicBlocks(); e.hasMoreElements();) {
bb = e.nextElement();
if (bb.isEmpty()) {
continue;
}
// HACK: temporarily disable scheduling of unsafe basic blocks.
// TODO: remove when UNINT_BEGIN/END are working properly.
if (bb.isUnsafeToSchedule()) {
continue;
}
// Build Dependence graph
dg = new DepGraph(ir, bb.firstInstruction(), bb.lastRealInstruction(), bb);
if (printDepgraph(ir.options)) {
// print dependence graph.
System.out.println("**** START OF " + PhaseName[phase].toUpperCase() + " DEPENDENCE GRAPH ****");
dg.printDepGraph();
System.out.println("**** END OF " + PhaseName[phase].toUpperCase() + " DEPENDENCE GRAPH ****");
}
scheduleBasicBlock();
}
// Remove scheduling info for each instruction
for (InstructionEnumeration instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
SchedulingInfo.removeInfo(instr.next());
}
// Remove mapping for dependence graph
i2gn = null;
// Clear the ir to schedule
bb = null;
dg = null;
ir = null;
}
/**
* Initialize scheduler for a given phase.
*
* @param phase the scheduling phase
*/
Scheduler(int phase) {
this.phase = phase;
}
/**
* Output debugging information.
* @param s string to print
*/
private static void debug(String s) {
System.err.println(s);
}
/**
* Output debugging information with indentation.
* @param depth level of indenting
* @param s string to print
*/
private static void debug(int depth, String s) {
String format = String.format("%% %ds", depth*2);
debug(String.format(format, s));
}
/**
* Set corresponding graph node for instruction.
* @param i given instruction
* @param n dependence graph node for instruction
*/
private void setGraphNode(Instruction i, DepGraphNode n) {
i2gn[i.scratch] = n;
}
/**
* Return corresponding graph node for instruction.
* @param i given instruction
*/
private DepGraphNode getGraphNode(Instruction i) {
return i2gn[i.scratch];
}
/**
* Perform DFS to compute critical path for all instructions.
* @param n start node
* @param depth current DFS depth
*/
private void computeCriticalPath(DepGraphNode n, int depth) {
if (VERBOSE >= 5) {
debug(depth, "Visiting " + n);
}
Instruction i = n.instruction();
if (SchedulingInfo.getCriticalPath(i) != -1) {
return;
}
int cp = 0;
for (GraphNodeEnumeration succ = n.outNodes(); succ.hasMoreElements();) {
DepGraphNode np = (DepGraphNode) succ.nextElement();
Instruction j = np.instruction();
computeCriticalPath(np, depth + 1);
int d = SchedulingInfo.getCriticalPath(j);
if (d + 1 > cp) {
cp = d + 1;
}
}
SchedulingInfo.setCriticalPath(i, cp);
}
/**
* Compute earliest scheduling time for an instruction.
* @param i given instruction
*/
private int computeEarliestTime(Instruction i) {
if (VERBOSE >= 5) {
debug("Computing earliest time for " + i);
}
DepGraphNode n = getGraphNode(i);
int etime = SchedulingInfo.getEarliestTime(i);
if (etime == -1) {
etime = 0;
}
OperatorClass opc = i.operator().getOpClass();
if (VERBOSE >= 7) {
debug("opc=" + opc);
}
if (opc == null) {
throw new OptimizingCompilerException("Missing operator class " + i.operator());
}
for (GraphNodeEnumeration pred = n.inNodes(); pred.hasMoreElements();) {
DepGraphNode np = (DepGraphNode) pred.next();
Instruction j = np.instruction();
int time = SchedulingInfo.getTime(j);
if (VERBOSE >= 6) {
debug("Predecessor " + j + " scheduled at " + time);
}
if (time == -1) {
throw new OptimizingCompilerException("Instructions not in topological order: " + i + "; " + j);
}
if (VERBOSE >= 6) {
debug("Retrieving latency from " + j);
}
OperatorClass joc = j.operator().getOpClass();
if (VERBOSE >= 7) {
debug("j's class=" + joc);
}
if (joc == null) {
throw new OptimizingCompilerException("Missing operator class " + j.operator());
}
int lat = joc.latency(opc);
if (time + lat > etime) {
etime = time + lat;
}
}
if (VERBOSE >= 5) {
debug("Updating time of " + i + " to " + etime);
}
SchedulingInfo.setEarliestTime(i, etime);
return etime;
}
/**
* A class representing sorted list of instructions.
* The instructions are sorted by their position on the critical path.
*/
private static final class InstructionBucket {
/**
* The instruction in the current slot.
*/
public Instruction instruction;
/**
* Next pointer.
*/
public InstructionBucket next;
/**
* Create a list element containing the instruction.
* @param i given instruction
*/
private InstructionBucket(Instruction i) {
instruction = i;
}
/**
* Insert the instruction into a given slot (based on its scheduling time).
* @param pool the bucket pool
* @param i given instruction
*/
public static void insert(InstructionBucket[] pool, Instruction i) {
InstructionBucket ib = new InstructionBucket(i);
int time = SchedulingInfo.getTime(i);
if (pool[time] == null) {
pool[time] = ib;
return;
}
int cp = SchedulingInfo.getCriticalPath(i);
Instruction j = pool[time].instruction;
if (SchedulingInfo.getCriticalPath(j) < cp) {
ib.next = pool[time];
pool[time] = ib;
return;
}
InstructionBucket p = pool[time];
InstructionBucket t = p.next;
while (t != null) {
j = t.instruction;
if (SchedulingInfo.getCriticalPath(j) < cp) {
break;
}
p = t;
t = t.next;
}
ib.next = t;
p.next = ib;
}
}
/**
* Sort basic block by Scheduled Time.
* Uses bucket sort on time, with equal times ordered by critical path.
* @param maxtime the maximum scheduled time
*/
private boolean sortBasicBlock(int maxtime) {
boolean changed = false;
InstructionBucket[] pool = new InstructionBucket[maxtime + 1];
int num = bb.firstInstruction().scratch;
Instruction ins;
while ((ins = bb.firstRealInstruction()) != null) {
InstructionBucket.insert(pool, ins);
ins.remove();
}
for (int i = 0; i <= maxtime; i++) {
for (InstructionBucket t = pool[i]; t != null; t = t.next) {
bb.appendInstruction(t.instruction);
changed = changed || num > t.instruction.scratch;
num = t.instruction.scratch;
}
}
return changed;
}
/**
* Schedule a basic block.
*/
private void scheduleBasicBlock() {
if (VERBOSE >= 2) {
debug("Scheduling " + bb);
}
if (VERBOSE >= 4) {
debug("**** START OF CURRENT BB BEFORE SCHEDULING ****");
for (InstructionEnumeration bi = bb.forwardInstrEnumerator(); bi.hasMoreElements();) {
debug(bi.next().toString());
}
debug("**** END OF CURRENT BB BEFORE SCHEDULING ****");
}
// Build mapping from instructions to graph nodes
for (DepGraphNode dgn = (DepGraphNode) dg.firstNode(); dgn != null; dgn = (DepGraphNode) dgn.getNext())
{
setGraphNode(dgn.instruction(), dgn);
if (VERBOSE >= 4) {
debug("Added node for " + dgn.instruction());
}
}
ResourceMap rmap = new ResourceMap();
int bl = 0;
Instruction fi = bb.firstInstruction();
if (VERBOSE >= 5) {
debug("Computing critical path for " + fi);
}
computeCriticalPath(getGraphNode(fi), 0);
int cp = SchedulingInfo.getCriticalPath(fi);
for (InstructionEnumeration ie = bb.forwardRealInstrEnumerator(); ie.hasMoreElements();) {
Instruction i = ie.next();
if (VERBOSE >= 5) {
debug("Computing critical path for " + i);
}
computeCriticalPath(getGraphNode(i), 0);
int d = SchedulingInfo.getCriticalPath(i);
if (d > cp) {
cp = d;
}
bl++;
}
cp++;
if (PRINT_CRITICAL_PATH_LENGTH) {
System.err.println("::: BL=" + bl + " CP=" + cp + " LOC=" + ir.method + ":" + bb);
}
Priority ilist = new DefaultPriority(bb);
int maxtime = 0;
for (ilist.reset(); ilist.hasMoreElements();) {
Instruction i = ilist.next();
if (VERBOSE >= 3) {
debug("Scheduling " + i + "[" + SchedulingInfo.getInfo(i) + "]");
}
int time = computeEarliestTime(i);
while (!rmap.schedule(i, time)) {
time++;
}
if (VERBOSE >= 5) {
debug("Scheduled " + i + " at time " + time);
}
if (time > maxtime) {
maxtime = time;
}
}
if (VERBOSE >= 2) {
debug("Done scheduling " + bb);
}
if (VERBOSE >= 3) {
debug(rmap.toString());
}
boolean changed = sortBasicBlock(maxtime);
if (changed && VERBOSE >= 2) {
debug("Basic block " + bb + " changed");
}
if (VERBOSE >= 4) {
debug("**** START OF CURRENT BB AFTER SCHEDULING ****");
for (InstructionEnumeration bi = bb.forwardInstrEnumerator(); bi.hasMoreElements();) {
debug(bi.next().toString());
}
debug("**** END OF CURRENT BB AFTER SCHEDULING ****");
}
}
}