1 /*
   2  * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 
  26 package org.graalvm.compiler.core.common.alloc;
  27 
  28 import java.util.ArrayList;
  29 import java.util.BitSet;
  30 import java.util.Comparator;
  31 import java.util.List;
  32 import java.util.PriorityQueue;
  33 
  34 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  35 import org.graalvm.compiler.core.common.cfg.Loop;
  36 
  37 /**
  38  * Computes an ordering of the block that can be used by the linear scan register allocator and the
  39  * machine code generator. The machine code generation order will start with the first block and
  40  * produce a straight sequence always following the most likely successor. Then it will continue
  41  * with the most likely path that was left out during this process. The process iteratively
  42  * continues until all blocks are scheduled. Additionally, it is guaranteed that all blocks of a
  43  * loop are scheduled before any block following the loop is scheduled.
  44  *
  45  * The machine code generator order includes reordering of loop headers such that the backward jump
  46  * is a conditional jump if there is only one loop end block. Additionally, the target of loop
  47  * backward jumps are always marked as aligned. Aligning the target of conditional jumps does not
  48  * bring a measurable benefit and is therefore avoided to keep the code size small.
  49  *
  50  * The linear scan register allocator order has an additional mechanism that prevents merge nodes
  51  * from being scheduled if there is at least one highly likely predecessor still unscheduled. This
  52  * increases the probability that the merge node and the corresponding predecessor are more closely
  53  * together in the schedule thus decreasing the probability for inserted phi moves. Also, the
  54  * algorithm sets the linear scan order number of the block that corresponds to its index in the
  55  * linear scan order.
  56  */
  57 public final class ComputeBlockOrder {
  58 
  59     /**
  60      * The initial capacities of the worklists used for iteratively finding the block order.
  61      */
  62     private static final int INITIAL_WORKLIST_CAPACITY = 10;
  63 
  64     /**
  65      * Divisor used for degrading the probability of the current path versus unscheduled paths at a
  66      * merge node when calculating the linear scan order. A high value means that predecessors of
  67      * merge nodes are more likely to be scheduled before the merge node.
  68      */
  69     private static final int PENALTY_VERSUS_UNSCHEDULED = 10;
  70 
  71     /**
  72      * Computes the block order used for the linear scan register allocator.
  73      *
  74      * @return sorted list of blocks
  75      */
  76     public static <T extends AbstractBlockBase<T>> AbstractBlockBase<?>[] computeLinearScanOrder(int blockCount, T startBlock) {
  77         List<T> order = new ArrayList<>();
  78         BitSet visitedBlocks = new BitSet(blockCount);
  79         PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks);
  80         computeLinearScanOrder(order, worklist, visitedBlocks);
  81         assert checkOrder(order, blockCount);
  82         return order.toArray(new AbstractBlockBase<?>[0]);
  83     }
  84 
  85     /**
  86      * Computes the block order used for code emission.
  87      *
  88      * @return sorted list of blocks
  89      */
  90     public static <T extends AbstractBlockBase<T>> AbstractBlockBase<?>[] computeCodeEmittingOrder(int blockCount, T startBlock) {
  91         List<T> order = new ArrayList<>();
  92         BitSet visitedBlocks = new BitSet(blockCount);
  93         PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks);
  94         computeCodeEmittingOrder(order, worklist, visitedBlocks);
  95         assert checkOrder(order, blockCount);
  96         return order.toArray(new AbstractBlockBase<?>[0]);
  97     }
  98 
  99     /**
 100      * Iteratively adds paths to the code emission block order.
 101      */
 102     private static <T extends AbstractBlockBase<T>> void computeCodeEmittingOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
 103         while (!worklist.isEmpty()) {
 104             T nextImportantPath = worklist.poll();
 105             addPathToCodeEmittingOrder(nextImportantPath, order, worklist, visitedBlocks);
 106         }
 107     }
 108 
 109     /**
 110      * Iteratively adds paths to the linear scan block order.
 111      */
 112     private static <T extends AbstractBlockBase<T>> void computeLinearScanOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
 113         while (!worklist.isEmpty()) {
 114             T nextImportantPath = worklist.poll();
 115             do {
 116                 nextImportantPath = addPathToLinearScanOrder(nextImportantPath, order, worklist, visitedBlocks);
 117             } while (nextImportantPath != null);
 118         }
 119     }
 120 
 121     /**
 122      * Initializes the priority queue used for the work list of blocks and adds the start block.
 123      */
 124     private static <T extends AbstractBlockBase<T>> PriorityQueue<T> initializeWorklist(T startBlock, BitSet visitedBlocks) {
 125         PriorityQueue<T> result = new PriorityQueue<>(INITIAL_WORKLIST_CAPACITY, new BlockOrderComparator<>());
 126         result.add(startBlock);
 127         visitedBlocks.set(startBlock.getId());
 128         return result;
 129     }
 130 
 131     /**
 132      * Add a linear path to the linear scan order greedily following the most likely successor.
 133      */
 134     private static <T extends AbstractBlockBase<T>> T addPathToLinearScanOrder(T block, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
 135         block.setLinearScanNumber(order.size());
 136         order.add(block);
 137         T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks);
 138         enqueueSuccessors(block, worklist, visitedBlocks);
 139         if (mostLikelySuccessor != null) {
 140             if (!mostLikelySuccessor.isLoopHeader() && mostLikelySuccessor.getPredecessorCount() > 1) {
 141                 // We are at a merge. Check probabilities of predecessors that are not yet
 142                 // scheduled.
 143                 double unscheduledSum = 0.0;
 144                 for (T pred : mostLikelySuccessor.getPredecessors()) {
 145                     if (pred.getLinearScanNumber() == -1) {
 146                         unscheduledSum += pred.getRelativeFrequency();
 147                     }
 148                 }
 149 
 150                 if (unscheduledSum > block.getRelativeFrequency() / PENALTY_VERSUS_UNSCHEDULED) {
 151                     // Add this merge only after at least one additional predecessor gets scheduled.
 152                     visitedBlocks.clear(mostLikelySuccessor.getId());
 153                     return null;
 154                 }
 155             }
 156             return mostLikelySuccessor;
 157         }
 158         return null;
 159     }
 160 
 161     /**
 162      * Add a linear path to the code emission order greedily following the most likely successor.
 163      */
 164     private static <T extends AbstractBlockBase<T>> void addPathToCodeEmittingOrder(T initialBlock, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
 165         T block = initialBlock;
 166         while (block != null) {
 167             // Skip loop headers if there is only a single loop end block to
 168             // make the backward jump be a conditional jump.
 169             if (!skipLoopHeader(block)) {
 170 
 171                 // Align unskipped loop headers as they are the target of the backward jump.
 172                 if (block.isLoopHeader()) {
 173                     block.setAlign(true);
 174                 }
 175                 addBlock(block, order);
 176             }
 177 
 178             Loop<T> loop = block.getLoop();
 179             if (block.isLoopEnd() && skipLoopHeader(loop.getHeader())) {
 180 
 181                 // This is the only loop end of a skipped loop header.
 182                 // Add the header immediately afterwards.
 183                 addBlock(loop.getHeader(), order);
 184 
 185                 // Make sure the loop successors of the loop header are aligned
 186                 // as they are the target
 187                 // of the backward jump.
 188                 for (T successor : loop.getHeader().getSuccessors()) {
 189                     if (successor.getLoopDepth() == block.getLoopDepth()) {
 190                         successor.setAlign(true);
 191                     }
 192                 }
 193             }
 194 
 195             T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks);
 196             enqueueSuccessors(block, worklist, visitedBlocks);
 197             block = mostLikelySuccessor;
 198         }
 199     }
 200 
 201     /**
 202      * Adds a block to the ordering.
 203      */
 204     private static <T extends AbstractBlockBase<T>> void addBlock(T header, List<T> order) {
 205         assert !order.contains(header) : "Cannot insert block twice";
 206         order.add(header);
 207     }
 208 
 209     /**
 210      * Find the highest likely unvisited successor block of a given block.
 211      */
 212     private static <T extends AbstractBlockBase<T>> T findAndMarkMostLikelySuccessor(T block, BitSet visitedBlocks) {
 213         T result = null;
 214         for (T successor : block.getSuccessors()) {
 215             assert successor.getRelativeFrequency() >= 0.0 : "Relative frequencies must be positive";
 216             if (!visitedBlocks.get(successor.getId()) && successor.getLoopDepth() >= block.getLoopDepth() && (result == null || successor.getRelativeFrequency() >= result.getRelativeFrequency())) {
 217                 result = successor;
 218             }
 219         }
 220         if (result != null) {
 221             visitedBlocks.set(result.getId());
 222         }
 223         return result;
 224     }
 225 
 226     /**
 227      * Add successor blocks into the given work list if they are not already marked as visited.
 228      */
 229     private static <T extends AbstractBlockBase<T>> void enqueueSuccessors(T block, PriorityQueue<T> worklist, BitSet visitedBlocks) {
 230         for (T successor : block.getSuccessors()) {
 231             if (!visitedBlocks.get(successor.getId())) {
 232                 visitedBlocks.set(successor.getId());
 233                 worklist.add(successor);
 234             }
 235         }
 236     }
 237 
 238     /**
 239      * Skip the loop header block if the loop consists of more than one block and it has only a
 240      * single loop end block.
 241      */
 242     private static <T extends AbstractBlockBase<T>> boolean skipLoopHeader(AbstractBlockBase<T> block) {
 243         return (block.isLoopHeader() && !block.isLoopEnd() && block.getLoop().numBackedges() == 1);
 244     }
 245 
 246     /**
 247      * Checks that the ordering contains the expected number of blocks.
 248      */
 249     private static boolean checkOrder(List<? extends AbstractBlockBase<?>> order, int expectedBlockCount) {
 250         assert order.size() == expectedBlockCount : String.format("Number of blocks in ordering (%d) does not match expected block count (%d)", order.size(), expectedBlockCount);
 251         return true;
 252     }
 253 
 254     /**
 255      * Comparator for sorting blocks based on loop depth and probability.
 256      */
 257     private static class BlockOrderComparator<T extends AbstractBlockBase<T>> implements Comparator<T> {
 258         private static final double EPSILON = 1E-6;
 259 
 260         @Override
 261         public int compare(T a, T b) {
 262             // Loop blocks before any loop exit block. The only exception are blocks that are
 263             // (almost) impossible to reach.
 264             if (a.getRelativeFrequency() > EPSILON && b.getRelativeFrequency() > EPSILON) {
 265                 int diff = b.getLoopDepth() - a.getLoopDepth();
 266                 if (diff != 0) {
 267                     return diff;
 268                 }
 269             }
 270 
 271             // Blocks with high probability before blocks with low probability.
 272             if (a.getRelativeFrequency() > b.getRelativeFrequency()) {
 273                 return -1;
 274             } else {
 275                 return 1;
 276             }
 277         }
 278     }
 279 }