001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.opt.instrsched;
014    
015    import java.util.Enumeration;
016    
017    import org.jikesrvm.compilers.opt.OptOptions;
018    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
019    import org.jikesrvm.compilers.opt.depgraph.DepGraph;
020    import org.jikesrvm.compilers.opt.depgraph.DepGraphNode;
021    import org.jikesrvm.compilers.opt.ir.BasicBlock;
022    import org.jikesrvm.compilers.opt.ir.IR;
023    import org.jikesrvm.compilers.opt.ir.Instruction;
024    import org.jikesrvm.compilers.opt.liveness.LiveAnalysis;
025    import org.jikesrvm.compilers.opt.util.GraphNode;
026    
027    /**
028     * This a simple list-based instruction scheduler.
029     * <p>
030     * TODO:
031     * <ul>
032     *   <li>Add more priority lists
033     *   <li>When scheduling an instruction, verify that its predecessors have
034     *   already been scheduled.
035     *   <li>Change forward propagation of earliest time to computing it from the
036     *   scheduling time of predecessors + latencies.
037     *   <li>Change bubble sort to insertion sort.
038     * </ul>
039     */
040    
041    final class Scheduler {
042      /**
043       * Debugging level.
044       */
045      private static final int VERBOSE = 0;
046    
047      /**
048       * Should we print the length of the critical path for each basic block?
049       */
050      private static final boolean PRINT_CRITICAL_PATH_LENGTH = false;
051    
052      /**
053       * A constant signifying pre-pass scheduling phase.
054       */
055      public static final int PREPASS = 1;
056    
057      /**
058       * A constant signifying post-pass scheduling phase.
059       * <p>
060       * WARNING: POSTPASS INSTRUCTION SCHEDULING (AFTER REGISTER ALLOCATION)
061       * Cannot be done safely due to failure to update GCMapping information.
062       */
063      public static final int POSTPASS = 2;
064    
065      /**
066       * Names of various scheduling phases.
067       */
068      public static final String[] PhaseName = new String[]{"Invalid Phase!!!!!!!!", "pre-pass", "post-pass"};
069    
070      /**
071       * Current phase (prepass/postpass).
072       */
073      private final int phase;
074    
075      /**
076       * Current IR.
077       */
078      private IR ir;
079    
080      /**
081       * Current basic block.
082       */
083      private BasicBlock bb;
084    
085      /**
086       * Dependence graph for current basic block.
087       */
088      private DepGraph dg;
089    
090      /**
091       * Mapping from Instruction to DepGraphNode.
092       */
093      private DepGraphNode[] i2gn;
094    
095      /**
096       * Should we print the dependence graph?
097       * @param options the options object
098       * @return true if we should print depgraph, false otherwise
099       */
100      private boolean printDepgraph(OptOptions options) {
101        return (phase == PREPASS && options.PRINT_DG_SCHED_PRE) || (phase == POSTPASS && options.PRINT_DG_SCHED_POST);
102      }
103    
104      /**
105       * For each basic block, build the dependence graph and
106       * perform instruction scheduling.
107       * This is an MIR to MIR transformation.
108       *
109       * @param _ir the IR in question
110       */
111      void perform(IR _ir) {
112        // Remember the ir to schedule
113        ir = _ir;
114        if (VERBOSE >= 1) {
115          debug("Scheduling " +
116                ir.method.getDeclaringClass() +
117                ' ' +
118                ir.method.getName() +
119                ' ' +
120                ir.method.getDescriptor());
121        }
122    
123        // Performing live analysis may reduce dependences between PEIs and stores
124        if (ir.options.L2M_HANDLER_LIVENESS) {
125          new LiveAnalysis(false, false, true).perform(ir);
126        }
127    
128        // Create mapping for dependence graph
129        i2gn = new DepGraphNode[ir.numberInstructions()];
130        // Create scheduling info for each instruction
131        for (Enumeration<Instruction> instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
132          SchedulingInfo.createInfo(instr.nextElement());
133        }
134        // iterate over each basic block
135        for (Enumeration<BasicBlock> e = ir.getBasicBlocks(); e.hasMoreElements();) {
136          bb = e.nextElement();
137          if (bb.isEmpty()) {
138            continue;
139          }
140          // HACK: temporarily disable scheduling of unsafe basic blocks.
141          // TODO: remove when UNINT_BEGIN/END are working properly.
142          if (bb.isUnsafeToSchedule()) {
143            continue;
144          }
145          // Build Dependence graph
146          dg = new DepGraph(ir, bb.firstInstruction(), bb.lastRealInstruction(), bb);
147          if (printDepgraph(ir.options)) {
148            // print dependence graph.
149            System.out.println("**** START OF " + PhaseName[phase].toUpperCase() + " DEPENDENCE GRAPH ****");
150            dg.printDepGraph();
151            System.out.println("**** END   OF " + PhaseName[phase].toUpperCase() + " DEPENDENCE GRAPH ****");
152          }
153          scheduleBasicBlock();
154        }
155    
156        // Remove scheduling info for each instruction
157        for (Enumeration<Instruction> instr = ir.forwardInstrEnumerator(); instr.hasMoreElements();) {
158          SchedulingInfo.removeInfo(instr.nextElement());
159        }
160        // Remove mapping for dependence graph
161        i2gn = null;
162        // Clear the ir to schedule
163        bb = null;
164        dg = null;
165        ir = null;
166      }
167    
168      /**
169       * Initialize scheduler for a given phase.
170       *
171       * @param phase the scheduling phase
172       */
173      Scheduler(int phase) {
174        this.phase = phase;
175      }
176    
177      /**
178       * Output debugging information.
179       * @param s string to print
180       */
181      private static void debug(String s) {
182        System.err.println(s);
183      }
184    
185      /**
186       * Output debugging information with indentation.
187       * @param depth level of indenting
188       * @param s string to print
189       */
190      private static void debug(int depth, String s) {
191        String format = String.format("%% %ds", depth*2);
192        debug(String.format(format, s));
193      }
194    
195      /**
196       * Set corresponding graph node for instruction.
197       * @param i given instruction
198       * @param n dependence graph node for instruction
199       */
200      private void setGraphNode(Instruction i, DepGraphNode n) {
201        i2gn[i.scratch] = n;
202      }
203    
204      /**
205       * Return corresponding graph node for instruction.
206       * @param i given instruction
207       */
208      private DepGraphNode getGraphNode(Instruction i) {
209        return i2gn[i.scratch];
210      }
211    
212      /**
213       * Perform DFS to compute critical path for all instructions.
214       * @param n start node
215       * @param depth current DFS depth
216       */
217      private void computeCriticalPath(DepGraphNode n, int depth) {
218        if (VERBOSE >= 5) {
219          debug(depth, "Visiting " + n);
220        }
221        Instruction i = n.instruction();
222        if (SchedulingInfo.getCriticalPath(i) != -1) {
223          return;
224        }
225        int cp = 0;
226        for (Enumeration<GraphNode> succ = n.outNodes(); succ.hasMoreElements();) {
227          DepGraphNode np = (DepGraphNode) succ.nextElement();
228          Instruction j = np.instruction();
229          computeCriticalPath(np, depth + 1);
230          int d = SchedulingInfo.getCriticalPath(j);
231          if (d + 1 > cp) {
232            cp = d + 1;
233          }
234        }
235        SchedulingInfo.setCriticalPath(i, cp);
236      }
237    
238      /**
239       * Compute earliest scheduling time for an instruction.
240       * @param i given instruction
241       */
242      private int computeEarliestTime(Instruction i) {
243        if (VERBOSE >= 5) {
244          debug("Computing earliest time for " + i);
245        }
246        DepGraphNode n = getGraphNode(i);
247        int etime = SchedulingInfo.getEarliestTime(i);
248        if (etime == -1) {
249          etime = 0;
250        }
251        OperatorClass opc = i.operator().getOpClass();
252        if (VERBOSE >= 7) {
253          debug("opc=" + opc);
254        }
255        if (opc == null) {
256          throw new OptimizingCompilerException("Missing operator class " + i.operator());
257        }
258        for (Enumeration<GraphNode> pred = n.inNodes(); pred.hasMoreElements();) {
259          DepGraphNode np = (DepGraphNode) pred.nextElement();
260          Instruction j = np.instruction();
261          int time = SchedulingInfo.getTime(j);
262          if (VERBOSE >= 6) {
263            debug("Predecessor " + j + " scheduled at " + time);
264          }
265          if (time == -1) {
266            throw new OptimizingCompilerException("Instructions not in topological order: " + i + "; " + j);
267          }
268          if (VERBOSE >= 6) {
269            debug("Retrieving latency from " + j);
270          }
271          OperatorClass joc = j.operator().getOpClass();
272          if (VERBOSE >= 7) {
273            debug("j's class=" + joc);
274          }
275          if (joc == null) {
276            throw new OptimizingCompilerException("Missing operator class " + j.operator());
277          }
278          int lat = joc.latency(opc);
279          if (time + lat > etime) {
280            etime = time + lat;
281          }
282        }
283        if (VERBOSE >= 5) {
284          debug("Updating time of " + i + " to " + etime);
285        }
286        SchedulingInfo.setEarliestTime(i, etime);
287        return etime;
288      }
289    
290      /**
291       * A class representing sorted list of instructions.
292       * The instructions are sorted by their position on the critical path.
293       */
294      private static final class InstructionBucket {
295        /**
296         * The instruction in the current slot.
297         */
298        public Instruction instruction;
299        /**
300         * Next pointer.
301         */
302        public InstructionBucket next;
303    
304        /**
305         * Create a list element containing the instruction.
306         * @param i given instruction
307         */
308        private InstructionBucket(Instruction i) {
309          instruction = i;
310        }
311    
312        /**
313         * Insert the instruction into a given slot (based on its scheduling time).
314         * @param pool the bucket pool
315         * @param i given instruction
316         */
317        public static void insert(InstructionBucket[] pool, Instruction i) {
318          InstructionBucket ib = new InstructionBucket(i);
319          int time = SchedulingInfo.getTime(i);
320          if (pool[time] == null) {
321            pool[time] = ib;
322            return;
323          }
324          int cp = SchedulingInfo.getCriticalPath(i);
325          Instruction j = pool[time].instruction;
326          if (SchedulingInfo.getCriticalPath(j) < cp) {
327            ib.next = pool[time];
328            pool[time] = ib;
329            return;
330          }
331          InstructionBucket p = pool[time];
332          InstructionBucket t = p.next;
333          while (t != null) {
334            j = t.instruction;
335            if (SchedulingInfo.getCriticalPath(j) < cp) {
336              break;
337            }
338            p = t;
339            t = t.next;
340          }
341          ib.next = t;
342          p.next = ib;
343        }
344      }
345    
346      /**
347       * Sort basic block by Scheduled Time.
348       * Uses bucket sort on time, with equal times ordered by critical path.
349       * @param maxtime the maximum scheduled time
350       */
351      private boolean sortBasicBlock(int maxtime) {
352        boolean changed = false;
353        InstructionBucket[] pool = new InstructionBucket[maxtime + 1];
354        int num = bb.firstInstruction().scratch;
355        Instruction ins;
356        while ((ins = bb.firstRealInstruction()) != null) {
357          InstructionBucket.insert(pool, ins);
358          ins.remove();
359        }
360        for (int i = 0; i <= maxtime; i++) {
361          for (InstructionBucket t = pool[i]; t != null; t = t.next) {
362            bb.appendInstruction(t.instruction);
363            changed = changed || num > t.instruction.scratch;
364            num = t.instruction.scratch;
365          }
366        }
367        return changed;
368      }
369    
370      /**
371       * Schedule a basic block.
372       */
373      private void scheduleBasicBlock() {
374        if (VERBOSE >= 2) {
375          debug("Scheduling " + bb);
376        }
377        if (VERBOSE >= 4) {
378          debug("**** START OF CURRENT BB BEFORE SCHEDULING ****");
379          for (Enumeration<Instruction> bi = bb.forwardInstrEnumerator(); bi.hasMoreElements();) {
380            debug(bi.nextElement().toString());
381          }
382          debug("**** END   OF CURRENT BB BEFORE SCHEDULING ****");
383        }
384        // Build mapping from instructions to graph nodes
385        for (DepGraphNode dgn = (DepGraphNode) dg.firstNode(); dgn != null; dgn = (DepGraphNode) dgn.getNext())
386        {
387          setGraphNode(dgn.instruction(), dgn);
388          if (VERBOSE >= 4) {
389            debug("Added node for " + dgn.instruction());
390          }
391        }
392        ResourceMap rmap = new ResourceMap();
393        int bl = 0;
394        Instruction fi = bb.firstInstruction();
395        if (VERBOSE >= 5) {
396          debug("Computing critical path for " + fi);
397        }
398        computeCriticalPath(getGraphNode(fi), 0);
399        int cp = SchedulingInfo.getCriticalPath(fi);
400        for (Enumeration<Instruction> ie = bb.forwardRealInstrEnumerator(); ie.hasMoreElements();) {
401          Instruction i = ie.nextElement();
402          if (VERBOSE >= 5) {
403            debug("Computing critical path for " + i);
404          }
405          computeCriticalPath(getGraphNode(i), 0);
406          int d = SchedulingInfo.getCriticalPath(i);
407          if (d > cp) {
408            cp = d;
409          }
410          bl++;
411        }
412        cp++;
413        if (PRINT_CRITICAL_PATH_LENGTH) {
414          System.err.println("::: BL=" + bl + " CP=" + cp + " LOC=" + ir.method + ":" + bb);
415        }
416        Priority ilist = new DefaultPriority(bb);
417        int maxtime = 0;
418        for (ilist.reset(); ilist.hasMoreElements();) {
419          Instruction i = ilist.nextElement();
420          if (VERBOSE >= 3) {
421            debug("Scheduling " + i + "[" + SchedulingInfo.getInfo(i) + "]");
422          }
423          int time = computeEarliestTime(i);
424          while (!rmap.schedule(i, time)) {
425            time++;
426          }
427          if (VERBOSE >= 5) {
428            debug("Scheduled " + i + " at time " + time);
429          }
430          if (time > maxtime) {
431            maxtime = time;
432          }
433        }
434        if (VERBOSE >= 2) {
435          debug("Done scheduling " + bb);
436        }
437        if (VERBOSE >= 3) {
438          debug(rmap.toString());
439        }
440        boolean changed = sortBasicBlock(maxtime);
441        if (changed && VERBOSE >= 2) {
442          debug("Basic block " + bb + " changed");
443        }
444        if (VERBOSE >= 4) {
445          debug("**** START OF CURRENT BB AFTER SCHEDULING ****");
446          for (Enumeration<Instruction> bi = bb.forwardInstrEnumerator(); bi.hasMoreElements();) {
447            debug(bi.nextElement().toString());
448          }
449          debug("**** END   OF CURRENT BB AFTER SCHEDULING ****");
450        }
451      }
452    }
453