FixPointIntervalBuilder.java revision 12651:6ef01bd40ce2
1/*
2 * Copyright (c) 2015, 2016, 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 */
23package org.graalvm.compiler.lir.stackslotalloc;
24
25import static org.graalvm.compiler.lir.LIRValueUtil.asVirtualStackSlot;
26import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
27
28import java.util.ArrayDeque;
29import java.util.BitSet;
30import java.util.Deque;
31import java.util.EnumSet;
32import java.util.HashSet;
33import java.util.List;
34import java.util.Set;
35
36import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
37import org.graalvm.compiler.core.common.cfg.BlockMap;
38import org.graalvm.compiler.debug.Debug;
39import org.graalvm.compiler.debug.DebugCounter;
40import org.graalvm.compiler.debug.Indent;
41import org.graalvm.compiler.lir.InstructionValueConsumer;
42import org.graalvm.compiler.lir.InstructionValueProcedure;
43import org.graalvm.compiler.lir.LIR;
44import org.graalvm.compiler.lir.LIRInstruction;
45import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
46import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
47import org.graalvm.compiler.lir.VirtualStackSlot;
48
49import jdk.vm.ci.meta.Value;
50
51/**
52 * Calculates the stack intervals using a worklist-based backwards data-flow analysis.
53 */
54final class FixPointIntervalBuilder {
55    private final BlockMap<BitSet> liveInMap;
56    private final BlockMap<BitSet> liveOutMap;
57    private final LIR lir;
58    private final int maxOpId;
59    private final StackInterval[] stackSlotMap;
60    private final HashSet<LIRInstruction> usePos;
61
62    /**
63     * The number of allocated stack slots.
64     */
65    private static final DebugCounter uninitializedSlots = Debug.counter("StackSlotAllocator[uninitializedSlots]");
66
67    FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId) {
68        this.lir = lir;
69        this.stackSlotMap = stackSlotMap;
70        this.maxOpId = maxOpId;
71        liveInMap = new BlockMap<>(lir.getControlFlowGraph());
72        liveOutMap = new BlockMap<>(lir.getControlFlowGraph());
73        this.usePos = new HashSet<>();
74    }
75
76    /**
77     * Builds the lifetime intervals for {@link VirtualStackSlot virtual stack slots}, sets up
78     * {@link #stackSlotMap} and returns a set of use positions, i.e. instructions that contain
79     * virtual stack slots.
80     */
81    Set<LIRInstruction> build() {
82        Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
83        AbstractBlockBase<?>[] blocks = lir.getControlFlowGraph().getBlocks();
84        for (int i = blocks.length - 1; i >= 0; i--) {
85            worklist.add(blocks[i]);
86        }
87        for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
88            liveInMap.put(block, new BitSet(stackSlotMap.length));
89        }
90        while (!worklist.isEmpty()) {
91            AbstractBlockBase<?> block = worklist.poll();
92            processBlock(block, worklist);
93        }
94        return usePos;
95    }
96
97    /**
98     * Merge outSet with in-set of successors.
99     */
100    private boolean updateOutBlock(AbstractBlockBase<?> block) {
101        BitSet union = new BitSet(stackSlotMap.length);
102        for (AbstractBlockBase<?> succ : block.getSuccessors()) {
103            union.or(liveInMap.get(succ));
104        }
105        BitSet outSet = liveOutMap.get(block);
106        // check if changed
107        if (outSet == null || !union.equals(outSet)) {
108            liveOutMap.put(block, union);
109            return true;
110        }
111        return false;
112    }
113
114    @SuppressWarnings("try")
115    private void processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist) {
116        if (updateOutBlock(block)) {
117            try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
118                List<LIRInstruction> instructions = lir.getLIRforBlock(block);
119                // get out set and mark intervals
120                BitSet outSet = liveOutMap.get(block);
121                markOutInterval(outSet, getBlockEnd(instructions));
122                printLiveSet("liveOut", outSet);
123
124                // process instructions
125                BlockClosure closure = new BlockClosure((BitSet) outSet.clone());
126                for (int i = instructions.size() - 1; i >= 0; i--) {
127                    LIRInstruction inst = instructions.get(i);
128                    closure.processInstructionBottomUp(inst);
129                }
130
131                // add predecessors to work list
132                for (AbstractBlockBase<?> b : block.getPredecessors()) {
133                    worklist.add(b);
134                }
135                // set in set and mark intervals
136                BitSet inSet = closure.getCurrentSet();
137                liveInMap.put(block, inSet);
138                markInInterval(inSet, getBlockBegin(instructions));
139                printLiveSet("liveIn", inSet);
140            }
141        }
142    }
143
144    @SuppressWarnings("try")
145    private void printLiveSet(String label, BitSet liveSet) {
146        if (Debug.isLogEnabled()) {
147            try (Indent indent = Debug.logAndIndent(label)) {
148                Debug.log("%s", liveSetToString(liveSet));
149            }
150        }
151    }
152
153    private String liveSetToString(BitSet liveSet) {
154        StringBuilder sb = new StringBuilder();
155        for (int i = liveSet.nextSetBit(0); i >= 0; i = liveSet.nextSetBit(i + 1)) {
156            StackInterval interval = getIntervalFromStackId(i);
157            sb.append(interval.getOperand()).append(" ");
158        }
159        return sb.toString();
160    }
161
162    private void markOutInterval(BitSet outSet, int blockEndOpId) {
163        for (int i = outSet.nextSetBit(0); i >= 0; i = outSet.nextSetBit(i + 1)) {
164            StackInterval interval = getIntervalFromStackId(i);
165            Debug.log("mark live operand: %s", interval.getOperand());
166            interval.addTo(blockEndOpId);
167        }
168    }
169
170    private void markInInterval(BitSet inSet, int blockFirstOpId) {
171        for (int i = inSet.nextSetBit(0); i >= 0; i = inSet.nextSetBit(i + 1)) {
172            StackInterval interval = getIntervalFromStackId(i);
173            Debug.log("mark live operand: %s", interval.getOperand());
174            interval.addFrom(blockFirstOpId);
175        }
176    }
177
178    private final class BlockClosure {
179        private final BitSet currentSet;
180
181        private BlockClosure(BitSet set) {
182            currentSet = set;
183        }
184
185        private BitSet getCurrentSet() {
186            return currentSet;
187        }
188
189        /**
190         * Process all values of an instruction bottom-up, i.e. definitions before usages. Values
191         * that start or end at the current operation are not included.
192         */
193        @SuppressWarnings("try")
194        private void processInstructionBottomUp(LIRInstruction op) {
195            try (Indent indent = Debug.logAndIndent("handle op %d, %s", op.id(), op)) {
196                // kills
197                op.visitEachTemp(defConsumer);
198                op.visitEachOutput(defConsumer);
199
200                // gen - values that are considered alive for this state
201                op.visitEachAlive(useConsumer);
202                op.visitEachState(useConsumer);
203                // mark locations
204                // gen
205                op.visitEachInput(useConsumer);
206            }
207        }
208
209        InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
210            @Override
211            public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
212                if (isVirtualStackSlot(operand)) {
213                    VirtualStackSlot vslot = asVirtualStackSlot(operand);
214                    addUse(vslot, inst, flags);
215                    addRegisterHint(inst, vslot, mode, flags, false);
216                    usePos.add(inst);
217                    Debug.log("set operand: %s", operand);
218                    currentSet.set(vslot.getId());
219                }
220            }
221        };
222
223        InstructionValueConsumer defConsumer = new InstructionValueConsumer() {
224            @Override
225            public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
226                if (isVirtualStackSlot(operand)) {
227                    VirtualStackSlot vslot = asVirtualStackSlot(operand);
228                    addDef(vslot, inst);
229                    addRegisterHint(inst, vslot, mode, flags, true);
230                    usePos.add(inst);
231                    Debug.log("clear operand: %s", operand);
232                    currentSet.clear(vslot.getId());
233                }
234
235            }
236        };
237
238        private void addUse(VirtualStackSlot stackSlot, LIRInstruction inst, EnumSet<OperandFlag> flags) {
239            StackInterval interval = getOrCreateInterval(stackSlot);
240            if (flags.contains(OperandFlag.UNINITIALIZED)) {
241                // Stack slot is marked uninitialized so we have to assume it is live all
242                // the time.
243                if (Debug.isCountEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) {
244                    uninitializedSlots.increment();
245                }
246                interval.addFrom(0);
247                interval.addTo(maxOpId);
248            } else {
249                interval.addTo(inst.id());
250            }
251        }
252
253        private void addDef(VirtualStackSlot stackSlot, LIRInstruction inst) {
254            StackInterval interval = getOrCreateInterval(stackSlot);
255            interval.addFrom(inst.id());
256        }
257
258        void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
259            if (flags.contains(OperandFlag.HINT)) {
260                InstructionValueProcedure proc = new InstructionValueProcedure() {
261                    @Override
262                    public Value doValue(LIRInstruction instruction, Value registerHint, OperandMode vaueMode, EnumSet<OperandFlag> valueFlags) {
263                        if (isVirtualStackSlot(registerHint)) {
264                            StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint);
265                            StackInterval to = getOrCreateInterval(targetValue);
266
267                            // hints always point from def to use
268                            if (hintAtDef) {
269                                to.setLocationHint(from);
270                            } else {
271                                from.setLocationHint(to);
272                            }
273                            if (Debug.isLogEnabled()) {
274                                Debug.log("operation %s at opId %d: added hint from interval %d to %d", op, op.id(), from, to);
275                            }
276
277                            return registerHint;
278                        }
279                        return null;
280                    }
281                };
282                op.forEachRegisterHint(targetValue, mode, proc);
283            }
284        }
285
286    }
287
288    private StackInterval get(VirtualStackSlot stackSlot) {
289        return stackSlotMap[stackSlot.getId()];
290    }
291
292    private void put(VirtualStackSlot stackSlot, StackInterval interval) {
293        stackSlotMap[stackSlot.getId()] = interval;
294    }
295
296    private StackInterval getOrCreateInterval(VirtualStackSlot stackSlot) {
297        StackInterval interval = get(stackSlot);
298        if (interval == null) {
299            interval = new StackInterval(stackSlot, stackSlot.getValueKind());
300            put(stackSlot, interval);
301        }
302        return interval;
303    }
304
305    private StackInterval getIntervalFromStackId(int id) {
306        return stackSlotMap[id];
307    }
308
309    private static int getBlockBegin(List<LIRInstruction> instructions) {
310        return instructions.get(0).id();
311    }
312
313    private static int getBlockEnd(List<LIRInstruction> instructions) {
314        return instructions.get(instructions.size() - 1).id() + 1;
315    }
316
317}
318