Label.java revision 953:221a84ef44c0
1/*
2 * Copyright (c) 2010, 2013, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package jdk.nashorn.internal.codegen;
26
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.BitSet;
30import java.util.Iterator;
31import java.util.List;
32import java.util.ListIterator;
33import jdk.nashorn.internal.codegen.types.Type;
34
35/**
36 * Abstraction for labels, separating a label from the underlying
37 * byte code emitter. Also augmenting label with e.g. a name
38 * for easier debugging and reading code
39 *
40 * see -Dnashorn.codegen.debug, --log=codegen
41 */
42public final class Label {
43    //byte code generation evaluation type stack for consistency check
44    //and correct opcode selection. one per label as a label may be a
45    //join point
46    static final class Stack implements Cloneable {
47        static final int NON_LOAD = -1;
48
49        Type[] data;
50        int[]  localLoads;
51        int    sp;
52
53        List<Type> localVariableTypes;
54        int firstTemp; // index of the first temporary local variable
55        // Bitmap marking last slot belonging to a single symbol.
56        BitSet symbolBoundary;
57
58        Stack() {
59            data = new Type[8];
60            localLoads = new int[8];
61            localVariableTypes = new ArrayList<>(8);
62            symbolBoundary = new BitSet();
63        }
64
65        boolean isEmpty() {
66            return sp == 0;
67        }
68
69        int size() {
70            return sp;
71        }
72
73        void clear() {
74            sp = 0;
75        }
76
77        void push(final Type type) {
78            if (data.length == sp) {
79                final Type[] newData = new Type[sp * 2];
80                final int[]  newLocalLoad = new int[sp * 2];
81                System.arraycopy(data, 0, newData, 0, sp);
82                System.arraycopy(localLoads, 0, newLocalLoad, 0, sp);
83                data = newData;
84                localLoads = newLocalLoad;
85            }
86            data[sp] = type;
87            localLoads[sp] = NON_LOAD;
88            sp++;
89        }
90
91        Type peek() {
92            return peek(0);
93        }
94
95        Type peek(final int n) {
96            final int pos = sp - 1 - n;
97            return pos < 0 ? null : data[pos];
98        }
99
100        /**
101         * Retrieve the top <tt>count</tt> types on the stack without modifying it.
102         *
103         * @param count number of types to return
104         * @return array of Types
105         */
106        Type[] getTopTypes(final int count) {
107            final Type[] topTypes = new Type[count];
108            System.arraycopy(data, sp - count, topTypes, 0, count);
109            return topTypes;
110        }
111
112        int[] getLocalLoads(final int from, final int to) {
113            final int count = to - from;
114            final int[] topLocalLoads = new int[count];
115            System.arraycopy(localLoads, from, topLocalLoads, 0, count);
116            return topLocalLoads;
117        }
118
119        /**
120         * Returns the number of used local variable slots, including all live stack-store temporaries.
121         * @return the number of used local variable slots, including all live stack-store temporaries.
122         */
123        int getUsedSlotsWithLiveTemporaries() {
124            // There are at least as many as are declared by the current blocks.
125            int usedSlots = firstTemp;
126            // Look at every load on the stack, and bump the number of used slots up by the temporaries seen there.
127            for(int i = sp; i-->0;) {
128                final int slot = localLoads[i];
129                if(slot != Label.Stack.NON_LOAD) {
130                    final int afterSlot = slot + localVariableTypes.get(slot).getSlots();
131                    if(afterSlot > usedSlots) {
132                        usedSlots = afterSlot;
133                    }
134                }
135            }
136            return usedSlots;
137        }
138
139        /**
140         *
141         * @param joinOrigin the stack from the other branch.
142         */
143        void joinFrom(final Stack joinOrigin, final boolean breakTarget) {
144            assert isStackCompatible(joinOrigin);
145            if(breakTarget) {
146                // As we're joining labels that can jump across block boundaries, the number of local variables can
147                // differ, and we should always respect the one having less variables.
148                firstTemp = Math.min(firstTemp, joinOrigin.firstTemp);
149            } else {
150                assert firstTemp == joinOrigin.firstTemp;
151            }
152            final int[] otherLoads = joinOrigin.localLoads;
153            int firstDeadTemp = firstTemp;
154            for(int i = 0; i < sp; ++i) {
155                final int localLoad = localLoads[i];
156                if(localLoad != otherLoads[i]) {
157                    localLoads[i] = NON_LOAD;
158                } else if(localLoad >= firstDeadTemp) {
159                    firstDeadTemp = localLoad + localVariableTypes.get(localLoad).getSlots();
160                }
161            }
162            // Eliminate dead temporaries
163            undefineLocalVariables(firstDeadTemp, false);
164            assert isVariablePartitioningEqual(joinOrigin, firstDeadTemp);
165            mergeVariableTypes(joinOrigin, firstDeadTemp);
166        }
167
168        private void mergeVariableTypes(final Stack joinOrigin, final int toSlot) {
169            final ListIterator<Type> it1 = localVariableTypes.listIterator();
170            final Iterator<Type> it2 = joinOrigin.localVariableTypes.iterator();
171
172            for(int i = 0; i < toSlot; ++i) {
173                final Type thisType = it1.next();
174                final Type otherType = it2.next();
175                if(otherType == Type.UNKNOWN) {
176                    // Variables that are <unknown> on the other branch will become <unknown> here too.
177                    it1.set(Type.UNKNOWN);
178                } else if (thisType != otherType) {
179                    if(thisType.isObject() && otherType.isObject()) {
180                        // different object types are merged into Object.
181                        // TODO: maybe find most common superclass?
182                        it1.set(Type.OBJECT);
183                    } else {
184                        assert thisType == Type.UNKNOWN;
185                    }
186                }
187            }
188        }
189
190        void joinFromTry(final Stack joinOrigin) {
191            // As we're joining labels that can jump across block boundaries, the number of local variables can
192            // differ, and we should always respect the one having less variables.
193            firstTemp = Math.min(firstTemp, joinOrigin.firstTemp);
194            assert isVariablePartitioningEqual(joinOrigin, firstTemp);
195            mergeVariableTypes(joinOrigin, firstTemp);
196        }
197
198        private int getFirstDeadLocal(final List<Type> types) {
199            int i = types.size();
200            for(final ListIterator<Type> it = types.listIterator(i);
201                it.hasPrevious() && it.previous() == Type.UNKNOWN;
202                --i) {
203                // no body
204            }
205
206            // Respect symbol boundaries; we never chop off half a symbol's storage
207            while(!symbolBoundary.get(i - 1)) {
208                ++i;
209            }
210            return i;
211        }
212
213        private boolean isStackCompatible(final Stack other) {
214            if (sp != other.sp) {
215                return false;
216            }
217            for (int i = 0; i < sp; i++) {
218                if (!data[i].isEquivalentTo(other.data[i])) {
219                    return false;
220                }
221            }
222            return true;
223        }
224
225        private boolean isVariablePartitioningEqual(final Stack other, final int toSlot) {
226            // No difference in the symbol boundaries before the toSlot
227            final BitSet diff = other.getSymbolBoundaryCopy();
228            diff.xor(symbolBoundary);
229            return diff.previousSetBit(toSlot - 1) == -1;
230        }
231
232        void markDeadLocalVariables(final int fromSlot, final int slotCount) {
233            final int localCount = localVariableTypes.size();
234            if(fromSlot >= localCount) {
235                return;
236            }
237            final int toSlot = Math.min(fromSlot + slotCount, localCount);
238            invalidateLocalLoadsOnStack(fromSlot, toSlot);
239            for(int i = fromSlot; i < toSlot; ++i) {
240                localVariableTypes.set(i, Type.UNKNOWN);
241            }
242        }
243
244        @SuppressWarnings("unchecked")
245        List<Type> getLocalVariableTypesCopy() {
246            return (List<Type>)((ArrayList<Type>)localVariableTypes).clone();
247        }
248
249        BitSet getSymbolBoundaryCopy() {
250            return (BitSet)symbolBoundary.clone();
251        }
252
253        /**
254         * Returns a list of local variable slot types, but for those symbols that have multiple values, only the slot
255         * holding the widest type is marked as live.
256         * @return a list of widest local variable slot types.
257         */
258        List<Type> getWidestLiveLocals(final List<Type> lvarTypes) {
259            final List<Type> widestLiveLocals = new ArrayList<>(lvarTypes);
260            boolean keepNextValue = true;
261            final int size = widestLiveLocals.size();
262            for(int i = size - 1; i-- > 0;) {
263                if(symbolBoundary.get(i)) {
264                    keepNextValue = true;
265                }
266                final Type t = widestLiveLocals.get(i);
267                if(t != Type.UNKNOWN) {
268                    if(keepNextValue) {
269                        if(t != Type.SLOT_2) {
270                            keepNextValue = false;
271                        }
272                    } else {
273                        widestLiveLocals.set(i, Type.UNKNOWN);
274                    }
275                }
276            }
277            widestLiveLocals.subList(Math.max(getFirstDeadLocal(widestLiveLocals), firstTemp), widestLiveLocals.size()).clear();
278            return widestLiveLocals;
279        }
280
281        String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) {
282            final char[] chars = lvarDescriptor.toCharArray();
283            int j = 0;
284            for(int i = 0; i < chars.length; ++i) {
285                final char c = chars[i];
286                final int nextj = j + CodeGeneratorLexicalContext.getTypeForSlotDescriptor(c).getSlots();
287                if(!symbolBoundary.get(nextj - 1)) {
288                    chars[i] = Character.toLowerCase(c);
289                }
290                j = nextj;
291            }
292            return new String(chars);
293        }
294
295        Type pop() {
296            assert sp > 0;
297            return data[--sp];
298        }
299
300        @Override
301        public Stack clone() {
302            try {
303                final Stack clone = (Stack)super.clone();
304                clone.data = data.clone();
305                clone.localLoads = localLoads.clone();
306                clone.symbolBoundary = getSymbolBoundaryCopy();
307                clone.localVariableTypes = getLocalVariableTypesCopy();
308                return clone;
309            } catch(final CloneNotSupportedException e) {
310                throw new AssertionError("", e);
311            }
312        }
313
314        private Stack cloneWithEmptyStack() {
315            final Stack stack = clone();
316            stack.sp = 0;
317            return stack;
318        }
319
320        int getTopLocalLoad() {
321            return localLoads[sp - 1];
322        }
323
324        void markLocalLoad(final int slot) {
325            localLoads[sp - 1] = slot;
326        }
327
328        /**
329         * Performs various bookeeping when a value is stored in a local variable slot.
330         * @param slot the slot written to
331         * @param onlySymbolLiveValue if true, this is the symbol's only live value, and other values of the symbol
332         * should be marked dead
333         * @param Type the type written to the slot
334         */
335        void onLocalStore(final Type type, final int slot, final boolean onlySymbolLiveValue) {
336            if(onlySymbolLiveValue) {
337                final int fromSlot = slot == 0 ? 0 : (symbolBoundary.previousSetBit(slot - 1) + 1);
338                final int toSlot = symbolBoundary.nextSetBit(slot) + 1;
339                for(int i = fromSlot; i < toSlot; ++i) {
340                    localVariableTypes.set(i, Type.UNKNOWN);
341                }
342                invalidateLocalLoadsOnStack(fromSlot, toSlot);
343            } else {
344                invalidateLocalLoadsOnStack(slot, slot + type.getSlots());
345            }
346
347            localVariableTypes.set(slot, type);
348            if(type.isCategory2()) {
349                localVariableTypes.set(slot + 1, Type.SLOT_2);
350            }
351        }
352
353        /**
354         * Given a slot range, invalidate knowledge about local loads on stack from these slots (because they're being
355         * killed).
356         * @param fromSlot first slot, inclusive.
357         * @param toSlot last slot, exclusive.
358         */
359        private void invalidateLocalLoadsOnStack(final int fromSlot, final int toSlot) {
360            for(int i = 0; i < sp; ++i) {
361                final int localLoad = localLoads[i];
362                if(localLoad >= fromSlot && localLoad < toSlot) {
363                    localLoads[i] = NON_LOAD;
364                }
365            }
366        }
367
368        /**
369         * Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value
370         * in them.
371         * @param fromSlot first slot, inclusive.
372         * @param toSlot last slot, exclusive.
373         */
374        void defineBlockLocalVariable(final int fromSlot, final int toSlot) {
375            defineLocalVariable(fromSlot, toSlot);
376            assert firstTemp < toSlot;
377            firstTemp = toSlot;
378        }
379
380        /**
381         * Defines a new temporary local variable and returns its allocated index.
382         * @param width the required width (in slots) for the new variable.
383         * @return the bytecode slot index where the newly allocated local begins.
384         */
385        int defineTemporaryLocalVariable(final int width) {
386            final int fromSlot = getUsedSlotsWithLiveTemporaries();
387            defineLocalVariable(fromSlot, fromSlot + width);
388            return fromSlot;
389        }
390
391        /**
392         * Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no
393         * live value in them.
394         * @param fromSlot first slot, inclusive.
395         * @param toSlot last slot, exclusive.
396         */
397        void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) {
398            defineLocalVariable(fromSlot, toSlot);
399        }
400
401        private void defineLocalVariable(final int fromSlot, final int toSlot) {
402            assert !hasLoadsOnStack(fromSlot, toSlot);
403            assert fromSlot < toSlot;
404            symbolBoundary.clear(fromSlot, toSlot - 1);
405            symbolBoundary.set(toSlot - 1);
406            final int lastExisting = Math.min(toSlot, localVariableTypes.size());
407            for(int i = fromSlot; i < lastExisting; ++i) {
408                localVariableTypes.set(i, Type.UNKNOWN);
409            }
410            for(int i = lastExisting; i < toSlot; ++i) {
411                localVariableTypes.add(i, Type.UNKNOWN);
412            }
413        }
414
415        /**
416         * Undefines all local variables past the specified slot.
417         * @param fromSlot the first slot to be undefined
418         * @param canTruncateSymbol if false, the fromSlot must be either the first slot of a symbol, or the first slot
419         * after the last symbol. If true, the fromSlot can be in the middle of the storage area for a symbol. This
420         * should be used with care - it is only meant for use in optimism exception handlers.
421         */
422        void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) {
423            final int lvarCount = localVariableTypes.size();
424            assert lvarCount == symbolBoundary.length();
425            assert !hasLoadsOnStack(fromSlot, lvarCount);
426            if(canTruncateSymbol) {
427                if(fromSlot > 0) {
428                    symbolBoundary.set(fromSlot - 1);
429                }
430            } else {
431                assert fromSlot == 0 || symbolBoundary.get(fromSlot - 1);
432            }
433            if(fromSlot < lvarCount) {
434                symbolBoundary.clear(fromSlot, lvarCount);
435                localVariableTypes.subList(fromSlot, lvarCount).clear();
436            }
437            firstTemp = Math.min(fromSlot, firstTemp);
438            assert symbolBoundary.length() == localVariableTypes.size();
439            assert symbolBoundary.length() == fromSlot;
440        }
441
442        private void markAsOptimisticCatchHandler(final int liveLocalCount) {
443            // Live temporaries that are no longer on stack are undefined
444            undefineLocalVariables(liveLocalCount, true);
445            // Temporaries are promoted
446            firstTemp = liveLocalCount;
447            // No trailing undefineds
448            localVariableTypes.subList(firstTemp, localVariableTypes.size()).clear();
449            assert symbolBoundary.length() == firstTemp;
450            // Generalize all reference types to Object, and promote boolean to int
451            for(final ListIterator<Type> it = localVariableTypes.listIterator(); it.hasNext();) {
452                final Type type = it.next();
453                if(type == Type.BOOLEAN) {
454                    it.set(Type.INT);
455                } else if(type.isObject() && type != Type.OBJECT) {
456                    it.set(Type.OBJECT);
457                }
458            }
459        }
460
461        /**
462         * Returns true if any loads on the stack come from the specified slot range.
463         * @param fromSlot start of the range (inclusive)
464         * @param toSlot end of the range (exclusive)
465         * @return true if any loads on the stack come from the specified slot range.
466         */
467        boolean hasLoadsOnStack(final int fromSlot, final int toSlot) {
468            for(int i = 0; i < sp; ++i) {
469                final int load = localLoads[i];
470                if(load >= fromSlot && load < toSlot) {
471                    return true;
472                }
473            }
474            return false;
475        }
476
477        @Override
478        public String toString() {
479            return "stack=" + Arrays.toString(Arrays.copyOf(data, sp))
480                 + ", symbolBoundaries=" + String.valueOf(symbolBoundary)
481                 + ", firstTemp=" + firstTemp
482                 + ", localTypes=" + String.valueOf(localVariableTypes)
483                 ;
484        }
485    }
486
487    /** Next id for debugging purposes, remove if footprint becomes unmanageable */
488    private static int nextId = 0;
489
490    /** Name of this label */
491    private final String name;
492
493    /** Type stack at this label */
494    private Label.Stack stack;
495
496    /** ASM representation of this label */
497    private jdk.internal.org.objectweb.asm.Label label;
498
499    /** Id for debugging purposes, remove if footprint becomes unmanageable */
500    private final int id;
501
502    /** Is this label reachable (anything ever jumped to it)? */
503    private boolean reachable;
504
505    private boolean breakTarget;
506
507    /**
508     * Constructor
509     *
510     * @param name name of this label
511     */
512    public Label(final String name) {
513        super();
514        this.name = name;
515        this.id   = nextId++;
516    }
517
518    /**
519     * Copy constructor
520     *
521     * @param label a label to clone
522     */
523    public Label(final Label label) {
524        super();
525        this.name = label.name;
526        this.id   = label.id;
527    }
528
529    jdk.internal.org.objectweb.asm.Label getLabel() {
530        if (this.label == null) {
531            this.label = new jdk.internal.org.objectweb.asm.Label();
532        }
533        return label;
534    }
535
536    Label.Stack getStack() {
537        return stack;
538    }
539
540    void joinFrom(final Label.Stack joinOrigin) {
541        this.reachable = true;
542        if(stack == null) {
543            stack = joinOrigin.clone();
544        } else {
545            stack.joinFrom(joinOrigin, breakTarget);
546        }
547    }
548
549    void joinFromTry(final Label.Stack joinOrigin, final boolean isOptimismHandler) {
550        this.reachable = true;
551        if (stack == null) {
552            if(!isOptimismHandler) {
553                stack = joinOrigin.cloneWithEmptyStack();
554                // Optimism handler needs temporaries to remain live, others don't.
555                stack.undefineLocalVariables(stack.firstTemp, false);
556            }
557        } else {
558            assert !isOptimismHandler;
559            stack.joinFromTry(joinOrigin);
560        }
561    }
562
563    void markAsBreakTarget() {
564        breakTarget = true;
565    }
566
567    boolean isBreakTarget() {
568        return breakTarget;
569    }
570
571    void onCatch() {
572        if(stack != null) {
573            stack = stack.cloneWithEmptyStack();
574        }
575    }
576    void markAsOptimisticCatchHandler(final Label.Stack currentStack, final int liveLocalCount) {
577        stack = currentStack.cloneWithEmptyStack();
578        stack.markAsOptimisticCatchHandler(liveLocalCount);
579    }
580
581    void markAsOptimisticContinuationHandlerFor(final Label afterConsumeStackLabel) {
582        stack = afterConsumeStackLabel.stack.cloneWithEmptyStack();
583    }
584
585    boolean isReachable() {
586        return reachable;
587    }
588
589    boolean isAfter(final Label other) {
590        return label.getOffset() > other.label.getOffset();
591    }
592
593    @Override
594    public String toString() {
595        return name + '_' + id;
596    }
597}
598