/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_InstructionEnumeration;
/**
* 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 OPT_Scheduler {
/**
* 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 int phase;
/**
* Should we print the dependence graph?
* @param options the options object
* @return true if we should print depgraph, false otherwise
*/
private boolean printDepgraph(OPT_Options options) {
return (phase == PREPASS && options.PRINT_DG_SCHED_PRE) || (phase == POSTPASS && options.PRINT_DG_SCHED_POST);
}
/**
* Should we produce a visualization the dependence graph?
* @param options the options object
* @return true if we should visualize depgraph, false otherwise
*/
private boolean vcgDepgraph(OPT_Options options) {
return (phase == PREPASS && options.VCG_DG_SCHED_PRE) || (phase == POSTPASS && options.VCG_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(OPT_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.HANDLER_LIVENESS) {
new OPT_LiveAnalysis(false, false, true).perform(ir);
}
// Create mapping for dependence graph
i2gn = new OPT_DepGraphNode[ir.numberInstructions()];
// Create scheduling info for each instruction
for (OPT_InstructionEnumeration instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
OPT_SchedulingInfo.createInfo(instr.next());
}
// iterate over each basic block
for (OPT_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 OPT_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 ****");
}
if (vcgDepgraph(ir.options)) {
// output dependence graph in VCG format.
// CAUTION: creates A LOT of files (one per BB)
OPT_VCG.printVCG("depgraph_sched_" + PhaseName[phase] + "_" + ir.method + "_" + bb + ".vcg", dg);
}
scheduleBasicBlock();
}
// Remove scheduling info for each instruction
for (OPT_InstructionEnumeration instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
OPT_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
*/
OPT_Scheduler(int phase) {
this.phase = phase;
}
/**
* Debugging level.
*/
private static final int verbose = 0;
/**
* Output debugging information.
* @param s string to print
*/
private static void debug(String s) {
System.err.println(s);
}
/**
* A string of spaces.
* Used for indenting.
*/
private static String SPACES = null;
/**
* Output debugging information with indentation.
* @param depth level of indenting
* @param s string to print
*/
private static void debug(int depth, String s) {
if (SPACES == null) SPACES = dup(128, ' ');
if (SPACES.length() < depth * 2) SPACES += SPACES;
debug(SPACES.substring(0, depth * 2) + s);
}
/**
* Current IR.
*/
private OPT_IR ir;
/**
* Current basic block.
*/
private OPT_BasicBlock bb;
/**
* Dependence graph for current basic block.
*/
private OPT_DepGraph dg;
/**
* Mapping from OPT_Instruction to OPT_DepGraphNode.
*/
private OPT_DepGraphNode[] i2gn;
/**
* Set corresponding graph node for instruction.
* @param i given instruction
* @param n dependence graph node for instruction
*/
private void setGraphNode(OPT_Instruction i, OPT_DepGraphNode n) {
i2gn[i.scratch] = n;
}
/**
* Return corresponding graph node for instruction.
* @param i given instruction
*/
private OPT_DepGraphNode getGraphNode(OPT_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(OPT_DepGraphNode n, int depth) {
if (verbose >= 5) {
debug(depth, "Visiting " + n);
}
OPT_Instruction i = n.instruction();
if (OPT_SchedulingInfo.getCriticalPath(i) != -1) {
return;
}
int cp = 0;
for (OPT_GraphNodeEnumeration succ = n.outNodes(); succ.hasMoreElements();) {
OPT_DepGraphNode np = (OPT_DepGraphNode) succ.nextElement();
OPT_Instruction j = np.instruction();
computeCriticalPath(np, depth + 1);
int d = OPT_SchedulingInfo.getCriticalPath(j);
if (d + 1 > cp) {
cp = d + 1;
}
}
OPT_SchedulingInfo.setCriticalPath(i, cp);
}
/**
* Compute earliest scheduling time for an instruction.
* @param i given instruction
*/
private int computeEarliestTime(OPT_Instruction i) {
if (verbose >= 5) {
debug("Computing earliest time for " + i);
}
OPT_DepGraphNode n = getGraphNode(i);
int etime = OPT_SchedulingInfo.getEarliestTime(i);
if (etime == -1) {
etime = 0;
}
OPT_OperatorClass opc = i.operator().getOpClass();
if (verbose >= 7) {
debug("opc=" + opc);
}
if (opc == null) {
throw new OPT_OptimizingCompilerException("Missing operator class " + i.operator());
}
for (OPT_GraphNodeEnumeration pred = n.inNodes(); pred.hasMoreElements();) {
OPT_DepGraphNode np = (OPT_DepGraphNode) pred.next();
OPT_Instruction j = np.instruction();
int time = OPT_SchedulingInfo.getTime(j);
if (verbose >= 6) {
debug("Predecessor " + j + " scheduled at " + time);
}
if (time == -1) {
throw new OPT_OptimizingCompilerException("Instructions not in topological order: " + i + "; " + j);
}
if (verbose >= 6) {
debug("Retrieving latency from " + j);
}
OPT_OperatorClass joc = j.operator().getOpClass();
if (verbose >= 7) {
debug("j's class=" + joc);
}
if (joc == null) {
throw new OPT_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);
}
OPT_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 OPT_Instruction instruction;
/**
* Next pointer.
*/
public InstructionBucket next;
/**
* Create a list element containing the instruction.
* @param i given instruction
*/
private InstructionBucket(OPT_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, OPT_Instruction i) {
InstructionBucket ib = new InstructionBucket(i);
int time = OPT_SchedulingInfo.getTime(i);
if (pool[time] == null) {
pool[time] = ib;
return;
}
int cp = OPT_SchedulingInfo.getCriticalPath(i);
OPT_Instruction j = pool[time].instruction;
if (OPT_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 (OPT_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;
OPT_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 (OPT_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 (OPT_DepGraphNode dgn = (OPT_DepGraphNode) dg.firstNode(); dgn != null; dgn = (OPT_DepGraphNode) dgn.getNext())
{
setGraphNode(dgn.instruction(), dgn);
if (verbose >= 4) {
debug("Added node for " + dgn.instruction());
}
}
OPT_ResourceMap rmap = new OPT_ResourceMap();
int bl = 0;
OPT_Instruction fi = bb.firstInstruction();
if (verbose >= 5) {
debug("Computing critical path for " + fi);
}
computeCriticalPath(getGraphNode(fi), 0);
int cp = OPT_SchedulingInfo.getCriticalPath(fi);
for (OPT_InstructionEnumeration ie = bb.forwardRealInstrEnumerator(); ie.hasMoreElements();) {
OPT_Instruction i = ie.next();
if (verbose >= 5) {
debug("Computing critical path for " + i);
}
computeCriticalPath(getGraphNode(i), 0);
int d = OPT_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);
}
OPT_Priority ilist = new OPT_DefaultPriority(bb);
int maxtime = 0;
for (ilist.reset(); ilist.hasMoreElements();) {
OPT_Instruction i = ilist.next();
if (verbose >= 3) {
debug("Scheduling " + i + "[" + OPT_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 (OPT_InstructionEnumeration bi = bb.forwardInstrEnumerator(); bi.hasMoreElements();) {
debug(bi.next().toString());
}
debug("**** END OF CURRENT BB AFTER SCHEDULING ****");
}
}
/**
* Generates a string of a given length filled by a given character.
* @param len the length to generate
* @param c the character to fill the string with
*/
private static String dup(int len, char c) {
StringBuilder ret = new StringBuilder();
StringBuffer sp2 = new StringBuffer(c);
int p2 = 1;
for (int i = 0; i < 32; i++) {
if ((len & p2) != 0) {
ret.append(sp2);
}
sp2.append(sp2);
p2 <<= 1;
}
return ret.toString();
}
}