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