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.lir.stackslotalloc;
024
025import static jdk.internal.jvmci.code.ValueUtil.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.code.*;
030import com.oracle.graal.debug.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.compiler.common.cfg.*;
034import com.oracle.graal.lir.*;
035import com.oracle.graal.lir.LIRInstruction.OperandFlag;
036import com.oracle.graal.lir.LIRInstruction.OperandMode;
037
038/**
039 * Calculates the stack intervals using a worklist-based backwards data-flow analysis.
040 */
041final class FixPointIntervalBuilder {
042    private final BlockMap<BitSet> liveInMap;
043    private final BlockMap<BitSet> liveOutMap;
044    private final LIR lir;
045    private final int maxOpId;
046    private final StackInterval[] stackSlotMap;
047    private final HashSet<LIRInstruction> usePos;
048
049    /**
050     * The number of allocated stack slots.
051     */
052    private static final DebugMetric uninitializedSlots = Debug.metric("StackSlotAllocator[uninitializedSlots]");
053
054    FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId) {
055        this.lir = lir;
056        this.stackSlotMap = stackSlotMap;
057        this.maxOpId = maxOpId;
058        liveInMap = new BlockMap<>(lir.getControlFlowGraph());
059        liveOutMap = new BlockMap<>(lir.getControlFlowGraph());
060        this.usePos = new HashSet<>();
061    }
062
063    /**
064     * Builds the lifetime intervals for {@link VirtualStackSlot virtual stack slots}, sets up
065     * {@link #stackSlotMap} and returns a set of use positions, i.e. instructions that contain
066     * virtual stack slots.
067     */
068    Set<LIRInstruction> build() {
069        Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
070        for (int i = lir.getControlFlowGraph().getBlocks().size() - 1; i >= 0; i--) {
071            worklist.add(lir.getControlFlowGraph().getBlocks().get(i));
072        }
073        for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
074            liveInMap.put(block, new BitSet(stackSlotMap.length));
075        }
076        while (!worklist.isEmpty()) {
077            AbstractBlockBase<?> block = worklist.poll();
078            processBlock(block, worklist);
079        }
080        return usePos;
081    }
082
083    /**
084     * Merge outSet with in-set of successors.
085     */
086    private boolean updateOutBlock(AbstractBlockBase<?> block) {
087        BitSet union = new BitSet(stackSlotMap.length);
088        block.getSuccessors().forEach(succ -> union.or(liveInMap.get(succ)));
089        BitSet outSet = liveOutMap.get(block);
090        // check if changed
091        if (outSet == null || !union.equals(outSet)) {
092            liveOutMap.put(block, union);
093            return true;
094        }
095        return false;
096    }
097
098    private void processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist) {
099        if (updateOutBlock(block)) {
100            try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
101                List<LIRInstruction> instructions = lir.getLIRforBlock(block);
102                // get out set and mark intervals
103                BitSet outSet = liveOutMap.get(block);
104                markOutInterval(outSet, getBlockEnd(instructions));
105                printLiveSet("liveOut", outSet);
106
107                // process instructions
108                BlockClosure closure = new BlockClosure((BitSet) outSet.clone());
109                for (int i = instructions.size() - 1; i >= 0; i--) {
110                    LIRInstruction inst = instructions.get(i);
111                    closure.processInstructionBottomUp(inst);
112                }
113
114                // add predecessors to work list
115                worklist.addAll(block.getPredecessors());
116                // set in set and mark intervals
117                BitSet inSet = closure.getCurrentSet();
118                liveInMap.put(block, inSet);
119                markInInterval(inSet, getBlockBegin(instructions));
120                printLiveSet("liveIn", inSet);
121            }
122        }
123    }
124
125    private void printLiveSet(String label, BitSet liveSet) {
126        if (Debug.isLogEnabled()) {
127            try (Indent indent = Debug.logAndIndent(label)) {
128                Debug.log("%s", liveSetToString(liveSet));
129            }
130        }
131    }
132
133    private String liveSetToString(BitSet liveSet) {
134        StringBuilder sb = new StringBuilder();
135        for (int i = liveSet.nextSetBit(0); i >= 0; i = liveSet.nextSetBit(i + 1)) {
136            StackInterval interval = getIntervalFromStackId(i);
137            sb.append(interval.getOperand()).append(" ");
138        }
139        return sb.toString();
140    }
141
142    private void markOutInterval(BitSet outSet, int blockEndOpId) {
143        for (int i = outSet.nextSetBit(0); i >= 0; i = outSet.nextSetBit(i + 1)) {
144            StackInterval interval = getIntervalFromStackId(i);
145            Debug.log("mark live operand: %s", interval.getOperand());
146            interval.addTo(blockEndOpId);
147        }
148    }
149
150    private void markInInterval(BitSet inSet, int blockFirstOpId) {
151        for (int i = inSet.nextSetBit(0); i >= 0; i = inSet.nextSetBit(i + 1)) {
152            StackInterval interval = getIntervalFromStackId(i);
153            Debug.log("mark live operand: %s", interval.getOperand());
154            interval.addFrom(blockFirstOpId);
155        }
156    }
157
158    private final class BlockClosure {
159        private final BitSet currentSet;
160
161        private BlockClosure(BitSet set) {
162            currentSet = set;
163        }
164
165        private BitSet getCurrentSet() {
166            return currentSet;
167        }
168
169        /**
170         * Process all values of an instruction bottom-up, i.e. definitions before usages. Values
171         * that start or end at the current operation are not included.
172         */
173        private void processInstructionBottomUp(LIRInstruction op) {
174            try (Indent indent = Debug.logAndIndent("handle op %d, %s", op.id(), op)) {
175                // kills
176                op.visitEachTemp(defConsumer);
177                op.visitEachOutput(defConsumer);
178
179                // gen - values that are considered alive for this state
180                op.visitEachAlive(useConsumer);
181                op.visitEachState(useConsumer);
182                // mark locations
183                // gen
184                op.visitEachInput(useConsumer);
185            }
186        }
187
188        InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
189            public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
190                if (isVirtualStackSlot(operand)) {
191                    VirtualStackSlot vslot = asVirtualStackSlot(operand);
192                    addUse(vslot, inst, flags);
193                    addRegisterHint(inst, vslot, mode, flags, false);
194                    usePos.add(inst);
195                    Debug.log("set operand: %s", operand);
196                    currentSet.set(vslot.getId());
197                }
198            }
199        };
200
201        InstructionValueConsumer defConsumer = new InstructionValueConsumer() {
202            public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
203                if (isVirtualStackSlot(operand)) {
204                    VirtualStackSlot vslot = asVirtualStackSlot(operand);
205                    addDef(vslot, inst);
206                    addRegisterHint(inst, vslot, mode, flags, true);
207                    usePos.add(inst);
208                    Debug.log("clear operand: %s", operand);
209                    currentSet.clear(vslot.getId());
210                }
211
212            }
213        };
214
215        private void addUse(VirtualStackSlot stackSlot, LIRInstruction inst, EnumSet<OperandFlag> flags) {
216            StackInterval interval = getOrCreateInterval(stackSlot);
217            if (flags.contains(OperandFlag.UNINITIALIZED)) {
218                // Stack slot is marked uninitialized so we have to assume it is live all
219                // the time.
220                if (Debug.isMeterEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) {
221                    uninitializedSlots.increment();
222                }
223                interval.addFrom(0);
224                interval.addTo(maxOpId);
225            } else {
226                interval.addTo(inst.id());
227            }
228        }
229
230        private void addDef(VirtualStackSlot stackSlot, LIRInstruction inst) {
231            StackInterval interval = getOrCreateInterval(stackSlot);
232            interval.addFrom(inst.id());
233        }
234
235        void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
236            if (flags.contains(OperandFlag.HINT)) {
237
238                op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> {
239                    if (isVirtualStackSlot(registerHint)) {
240                        StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint);
241                        StackInterval to = getOrCreateInterval(targetValue);
242
243                        /* hints always point from def to use */
244                        if (hintAtDef) {
245                            to.setLocationHint(from);
246                        } else {
247                            from.setLocationHint(to);
248                        }
249                        if (Debug.isLogEnabled()) {
250                            Debug.log("operation %s at opId %d: added hint from interval %d to %d", op, op.id(), from, to);
251                        }
252
253                        return registerHint;
254                    }
255                    return null;
256                });
257            }
258        }
259
260    }
261
262    private StackInterval get(VirtualStackSlot stackSlot) {
263        return stackSlotMap[stackSlot.getId()];
264    }
265
266    private void put(VirtualStackSlot stackSlot, StackInterval interval) {
267        stackSlotMap[stackSlot.getId()] = interval;
268    }
269
270    private StackInterval getOrCreateInterval(VirtualStackSlot stackSlot) {
271        StackInterval interval = get(stackSlot);
272        if (interval == null) {
273            interval = new StackInterval(stackSlot, stackSlot.getLIRKind());
274            put(stackSlot, interval);
275        }
276        return interval;
277    }
278
279    private StackInterval getIntervalFromStackId(int id) {
280        return stackSlotMap[id];
281    }
282
283    private static int getBlockBegin(List<LIRInstruction> instructions) {
284        return instructions.get(0).id();
285    }
286
287    private static int getBlockEnd(List<LIRInstruction> instructions) {
288        return instructions.get(instructions.size() - 1).id() + 1;
289    }
290
291}