001/*
002 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.common.alloc;
024
025import java.util.*;
026
027import com.oracle.graal.compiler.common.cfg.*;
028import com.oracle.graal.debug.*;
029import com.oracle.graal.debug.Debug.Scope;
030
031public final class TraceBuilder<T extends AbstractBlockBase<T>> {
032
033    public static final class TraceBuilderResult<T extends AbstractBlockBase<T>> {
034        private final List<List<T>> traces;
035        private final int[] blockToTrace;
036
037        private TraceBuilderResult(List<List<T>> traces, int[] blockToTrace) {
038            this.traces = traces;
039            this.blockToTrace = blockToTrace;
040        }
041
042        public int getTraceForBlock(AbstractBlockBase<?> block) {
043            return blockToTrace[block.getId()];
044        }
045
046        public List<List<T>> getTraces() {
047            return traces;
048        }
049    }
050
051    /**
052     * Build traces of sequentially executed blocks.
053     */
054    public static <T extends AbstractBlockBase<T>> TraceBuilderResult<T> computeTraces(T startBlock, List<T> blocks) {
055        return new TraceBuilder<>(blocks).build(startBlock);
056    }
057
058    private final PriorityQueue<T> worklist;
059    private final BitSet processed;
060    /**
061     * Contains the number of unprocessed predecessors for every {@link AbstractBlockBase#getId()
062     * block}.
063     */
064    private final int[] blocked;
065    private final int[] blockToTrace;
066
067    private TraceBuilder(List<T> blocks) {
068        processed = new BitSet(blocks.size());
069        worklist = new PriorityQueue<T>(TraceBuilder::compare);
070        blocked = new int[blocks.size()];
071        blockToTrace = new int[blocks.size()];
072        for (T block : blocks) {
073            blocked[block.getId()] = block.getPredecessorCount();
074        }
075    }
076
077    private static <T extends AbstractBlockBase<T>> int compare(T a, T b) {
078        return Double.compare(b.probability(), a.probability());
079    }
080
081    private boolean processed(T b) {
082        return processed.get(b.getId());
083    }
084
085    private TraceBuilderResult<T> build(T startBlock) {
086        try (Scope s = Debug.scope("TraceBuilder"); Indent i = Debug.logAndIndent("start trace building: " + startBlock)) {
087            ArrayList<List<T>> traces = buildTraces(startBlock);
088
089            assert verify(traces);
090            return new TraceBuilderResult<>(traces, blockToTrace);
091        }
092    }
093
094    private boolean verify(ArrayList<List<T>> traces) {
095        assert traces.stream().flatMap(l -> l.stream()).distinct().count() == blocked.length : "Not all blocks assigned to traces!";
096        for (List<T> trace : traces) {
097            T last = null;
098            for (T current : trace) {
099                assert last == null || current.getPredecessors().contains(last);
100                last = current;
101            }
102        }
103        return true;
104    }
105
106    protected ArrayList<List<T>> buildTraces(T startBlock) {
107        ArrayList<List<T>> traces = new ArrayList<>();
108        // add start block
109        worklist.add(startBlock);
110        // process worklist
111        while (!worklist.isEmpty()) {
112            T block = worklist.poll();
113            assert block != null;
114            traces.add(startTrace(block, traces.size()));
115        }
116        return traces;
117    }
118
119    /**
120     * Build a new trace starting at {@code block}.
121     */
122    private List<T> startTrace(T block, int traceNumber) {
123        assert block.getPredecessors().stream().allMatch(this::processed) : "Predecessor unscheduled: " + block.getPredecessors().stream().filter(this::processed).findFirst().get();
124        ArrayList<T> trace = new ArrayList<>();
125        int blockNumber = 0;
126        try (Indent i = Debug.logAndIndent("StartTrace: " + block)) {
127            for (T currentBlock = block; currentBlock != null; currentBlock = selectNext(currentBlock)) {
128                Debug.log("add %s (prob: %f)", currentBlock, currentBlock.probability());
129                processed.set(currentBlock.getId());
130                worklist.remove(currentBlock);
131                trace.add(currentBlock);
132                unblock(currentBlock);
133                currentBlock.setLinearScanNumber(blockNumber++);
134                blockToTrace[currentBlock.getId()] = traceNumber;
135            }
136        }
137        return trace;
138    }
139
140    /**
141     * Decrease the {@link #blocked} count for all predecessors and add them to the worklist once
142     * the count reaches 0.
143     */
144    private void unblock(T currentBlock) {
145        for (T successor : currentBlock.getSuccessors()) {
146            if (!processed(successor)) {
147                int blockCount = --blocked[successor.getId()];
148                assert blockCount >= 0;
149                if (blockCount == 0) {
150                    worklist.add(successor);
151                }
152            }
153        }
154    }
155
156    /**
157     * @return The unprocessed predecessor with the highest probability, or {@code null}.
158     */
159    private T selectNext(T currentBlock) {
160        T next = null;
161        for (T succ : currentBlock.getSuccessors()) {
162            if (!processed(succ) && (next == null || succ.probability() > next.probability())) {
163                next = succ;
164            }
165        }
166        return next;
167    }
168}