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