001/*
002 * Copyright (c) 2012, 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.java;
024
025import static com.oracle.graal.bytecode.Bytecodes.*;
026import static com.oracle.graal.graph.iterators.NodePredicates.*;
027import static com.oracle.graal.java.BytecodeParser.Options.*;
028import static com.oracle.graal.nodes.FrameState.*;
029import static jdk.internal.jvmci.common.JVMCIError.*;
030
031import java.util.*;
032import java.util.function.*;
033
034import jdk.internal.jvmci.code.*;
035import com.oracle.graal.debug.*;
036import jdk.internal.jvmci.meta.*;
037
038import com.oracle.graal.compiler.common.type.*;
039import com.oracle.graal.graphbuilderconf.IntrinsicContext.SideEffectsState;
040import com.oracle.graal.graphbuilderconf.*;
041import com.oracle.graal.java.BciBlockMapping.BciBlock;
042import com.oracle.graal.nodeinfo.*;
043import com.oracle.graal.nodes.*;
044import com.oracle.graal.nodes.calc.*;
045import com.oracle.graal.nodes.java.*;
046import com.oracle.graal.nodes.util.*;
047
048public final class FrameStateBuilder implements SideEffectsState {
049
050    private static final ValueNode[] EMPTY_ARRAY = new ValueNode[0];
051    private static final MonitorIdNode[] EMPTY_MONITOR_ARRAY = new MonitorIdNode[0];
052
053    private final BytecodeParser parser;
054    private final ResolvedJavaMethod method;
055    private int stackSize;
056    protected final ValueNode[] locals;
057    protected final ValueNode[] stack;
058    private ValueNode[] lockedObjects;
059    private boolean canVerifyKind;
060
061    /**
062     * @see BytecodeFrame#rethrowException
063     */
064    private boolean rethrowException;
065
066    private MonitorIdNode[] monitorIds;
067    private final StructuredGraph graph;
068    private FrameState outerFrameState;
069
070    /**
071     * The closest {@link StateSplit#hasSideEffect() side-effect} predecessors. There will be more
072     * than one when the current block contains no side-effects but merging predecessor blocks do.
073     */
074    private List<StateSplit> sideEffects;
075
076    /**
077     * Creates a new frame state builder for the given method and the given target graph.
078     *
079     * @param method the method whose frame is simulated
080     * @param graph the target graph of Graal nodes created by the builder
081     */
082    public FrameStateBuilder(BytecodeParser parser, ResolvedJavaMethod method, StructuredGraph graph) {
083        this.parser = parser;
084        this.method = method;
085        this.locals = allocateArray(method.getMaxLocals());
086        this.stack = allocateArray(Math.max(1, method.getMaxStackSize()));
087        this.lockedObjects = allocateArray(0);
088
089        assert graph != null;
090
091        this.monitorIds = EMPTY_MONITOR_ARRAY;
092        this.graph = graph;
093        this.canVerifyKind = true;
094    }
095
096    public void disableKindVerification() {
097        canVerifyKind = false;
098    }
099
100    public void initializeFromArgumentsArray(ValueNode[] arguments) {
101
102        int javaIndex = 0;
103        int index = 0;
104        if (!method.isStatic()) {
105            // set the receiver
106            locals[javaIndex] = arguments[index];
107            javaIndex = 1;
108            index = 1;
109        }
110        Signature sig = method.getSignature();
111        int max = sig.getParameterCount(false);
112        for (int i = 0; i < max; i++) {
113            Kind kind = sig.getParameterKind(i);
114            locals[javaIndex] = arguments[index];
115            javaIndex++;
116            if (kind.needsTwoSlots()) {
117                locals[javaIndex] = TWO_SLOT_MARKER;
118                javaIndex++;
119            }
120            index++;
121        }
122    }
123
124    public void initializeForMethodStart(boolean eagerResolve, ParameterPlugin[] parameterPlugins) {
125
126        int javaIndex = 0;
127        int index = 0;
128        if (!method.isStatic()) {
129            // add the receiver
130            FloatingNode receiver = null;
131            Stamp receiverStamp = StampFactory.declaredNonNull(method.getDeclaringClass());
132            for (ParameterPlugin plugin : parameterPlugins) {
133                receiver = plugin.interceptParameter(parser, index, receiverStamp);
134                if (receiver != null) {
135                    break;
136                }
137            }
138            if (receiver == null) {
139                receiver = new ParameterNode(javaIndex, receiverStamp);
140            }
141            locals[javaIndex] = graph.unique(receiver);
142            javaIndex = 1;
143            index = 1;
144        }
145        Signature sig = method.getSignature();
146        int max = sig.getParameterCount(false);
147        ResolvedJavaType accessingClass = method.getDeclaringClass();
148        for (int i = 0; i < max; i++) {
149            JavaType type = sig.getParameterType(i, accessingClass);
150            if (eagerResolve) {
151                type = type.resolve(accessingClass);
152            }
153            Kind kind = type.getKind();
154            Stamp stamp;
155            if (kind == Kind.Object && type instanceof ResolvedJavaType) {
156                stamp = StampFactory.declared((ResolvedJavaType) type);
157            } else {
158                stamp = StampFactory.forKind(kind);
159            }
160            FloatingNode param = null;
161            for (ParameterPlugin plugin : parameterPlugins) {
162                param = plugin.interceptParameter(parser, index, stamp);
163                if (param != null) {
164                    break;
165                }
166            }
167            if (param == null) {
168                param = new ParameterNode(index, stamp);
169            }
170            locals[javaIndex] = graph.unique(param);
171            javaIndex++;
172            if (kind.needsTwoSlots()) {
173                locals[javaIndex] = TWO_SLOT_MARKER;
174                javaIndex++;
175            }
176            index++;
177        }
178    }
179
180    private FrameStateBuilder(FrameStateBuilder other) {
181        this.parser = other.parser;
182        this.method = other.method;
183        this.stackSize = other.stackSize;
184        this.locals = other.locals.clone();
185        this.stack = other.stack.clone();
186        this.lockedObjects = other.lockedObjects.length == 0 ? other.lockedObjects : other.lockedObjects.clone();
187        this.rethrowException = other.rethrowException;
188        this.canVerifyKind = other.canVerifyKind;
189
190        assert locals.length == method.getMaxLocals();
191        assert stack.length == Math.max(1, method.getMaxStackSize());
192
193        assert other.graph != null;
194        graph = other.graph;
195        monitorIds = other.monitorIds.length == 0 ? other.monitorIds : other.monitorIds.clone();
196
197        assert locals.length == method.getMaxLocals();
198        assert stack.length == Math.max(1, method.getMaxStackSize());
199        assert lockedObjects.length == monitorIds.length;
200    }
201
202    private static ValueNode[] allocateArray(int length) {
203        return length == 0 ? EMPTY_ARRAY : new ValueNode[length];
204    }
205
206    public ResolvedJavaMethod getMethod() {
207        return method;
208    }
209
210    @Override
211    public String toString() {
212        StringBuilder sb = new StringBuilder();
213        sb.append("[locals: [");
214        for (int i = 0; i < locals.length; i++) {
215            sb.append(i == 0 ? "" : ",").append(locals[i] == null ? "_" : locals[i] == TWO_SLOT_MARKER ? "#" : locals[i].toString(Verbosity.Id));
216        }
217        sb.append("] stack: [");
218        for (int i = 0; i < stackSize; i++) {
219            sb.append(i == 0 ? "" : ",").append(stack[i] == null ? "_" : stack[i] == TWO_SLOT_MARKER ? "#" : stack[i].toString(Verbosity.Id));
220        }
221        sb.append("] locks: [");
222        for (int i = 0; i < lockedObjects.length; i++) {
223            sb.append(i == 0 ? "" : ",").append(lockedObjects[i].toString(Verbosity.Id)).append(" / ").append(monitorIds[i].toString(Verbosity.Id));
224        }
225        sb.append("]");
226        if (rethrowException) {
227            sb.append(" rethrowException");
228        }
229        sb.append("]");
230        return sb.toString();
231    }
232
233    public FrameState create(int bci, StateSplit forStateSplit) {
234        if (parser != null && parser.parsingIntrinsic()) {
235            return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit);
236        }
237
238        // Skip intrinsic frames
239        return create(bci, parser != null ? parser.getNonIntrinsicAncestor() : null, false, null, null);
240    }
241
242    /**
243     * @param pushedValues if non-null, values to {@link #push(Kind, ValueNode)} to the stack before
244     *            creating the {@link FrameState}
245     */
246    public FrameState create(int bci, BytecodeParser parent, boolean duringCall, Kind[] pushedSlotKinds, ValueNode[] pushedValues) {
247        if (outerFrameState == null && parent != null) {
248            outerFrameState = parent.getFrameStateBuilder().create(parent.bci(), null);
249        }
250        if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
251            FrameState newFrameState = outerFrameState.duplicateModified(outerFrameState.bci, true, Kind.Void, new Kind[]{Kind.Object}, new ValueNode[]{stack[0]});
252            return newFrameState;
253        }
254        if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
255            throw shouldNotReachHere();
256        }
257
258        if (pushedValues != null) {
259            assert pushedSlotKinds.length == pushedValues.length;
260            int stackSizeToRestore = stackSize;
261            for (int i = 0; i < pushedValues.length; i++) {
262                push(pushedSlotKinds[i], pushedValues[i]);
263            }
264            FrameState res = graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall));
265            stackSize = stackSizeToRestore;
266            return res;
267        } else {
268            return graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall));
269        }
270    }
271
272    public BytecodePosition createBytecodePosition(int bci) {
273        BytecodeParser parent = parser.getParent();
274        if (HideSubstitutionStates.getValue()) {
275            if (parser.parsingIntrinsic()) {
276                // Attribute to the method being replaced
277                return new BytecodePosition(parent.getFrameStateBuilder().createBytecodePosition(parent.bci()), parser.intrinsicContext.getOriginalMethod(), -1);
278            }
279            // Skip intrinsic frames
280            parent = parser.getNonIntrinsicAncestor();
281        }
282        return create(null, bci, parent);
283    }
284
285    private BytecodePosition create(BytecodePosition o, int bci, BytecodeParser parent) {
286        BytecodePosition outer = o;
287        if (outer == null && parent != null) {
288            outer = parent.getFrameStateBuilder().createBytecodePosition(parent.bci());
289        }
290        if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
291            return FrameState.toBytecodePosition(outerFrameState);
292        }
293        if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
294            throw shouldNotReachHere();
295        }
296        return new BytecodePosition(outer, method, bci);
297    }
298
299    public FrameStateBuilder copy() {
300        return new FrameStateBuilder(this);
301    }
302
303    public boolean isCompatibleWith(FrameStateBuilder other) {
304        assert method.equals(other.method) && graph == other.graph && localsSize() == other.localsSize() : "Can only compare frame states of the same method";
305        assert lockedObjects.length == monitorIds.length && other.lockedObjects.length == other.monitorIds.length : "mismatch between lockedObjects and monitorIds";
306
307        if (stackSize() != other.stackSize()) {
308            return false;
309        }
310        for (int i = 0; i < stackSize(); i++) {
311            ValueNode x = stack[i];
312            ValueNode y = other.stack[i];
313            assert x != null && y != null;
314            if (x != y && (x == TWO_SLOT_MARKER || x.isDeleted() || y == TWO_SLOT_MARKER || y.isDeleted() || x.getKind() != y.getKind())) {
315                return false;
316            }
317        }
318        if (lockedObjects.length != other.lockedObjects.length) {
319            return false;
320        }
321        for (int i = 0; i < lockedObjects.length; i++) {
322            if (GraphUtil.originalValue(lockedObjects[i]) != GraphUtil.originalValue(other.lockedObjects[i]) || monitorIds[i] != other.monitorIds[i]) {
323                throw new BailoutException("unbalanced monitors");
324            }
325        }
326        return true;
327    }
328
329    /**
330     * Phi nodes are recursively deleted in {@link #propagateDelete}. However, this does not cover
331     * frame state builder objects, since these are not nodes and not in the usage list of the phi
332     * node. Therefore, we clean the frame state builder manually here, before we parse a block.
333     */
334    public void cleanDeletedNodes() {
335        for (int i = 0; i < localsSize(); i++) {
336            ValueNode node = locals[i];
337            if (node != null && node.isDeleted()) {
338                assert node instanceof ValuePhiNode || node instanceof ValueProxyNode;
339                locals[i] = null;
340            }
341        }
342        for (int i = 0; i < stackSize(); i++) {
343            ValueNode node = stack[i];
344            assert node == null || !node.isDeleted();
345        }
346        for (int i = 0; i < lockedObjects.length; i++) {
347            ValueNode node = lockedObjects[i];
348            assert !node.isDeleted();
349        }
350    }
351
352    public void merge(AbstractMergeNode block, FrameStateBuilder other) {
353        assert isCompatibleWith(other);
354
355        for (int i = 0; i < localsSize(); i++) {
356            locals[i] = merge(locals[i], other.locals[i], block);
357        }
358        for (int i = 0; i < stackSize(); i++) {
359            stack[i] = merge(stack[i], other.stack[i], block);
360        }
361        for (int i = 0; i < lockedObjects.length; i++) {
362            lockedObjects[i] = merge(lockedObjects[i], other.lockedObjects[i], block);
363            assert monitorIds[i] == other.monitorIds[i];
364        }
365
366        if (sideEffects == null) {
367            sideEffects = other.sideEffects;
368        } else {
369            if (other.sideEffects != null) {
370                sideEffects.addAll(other.sideEffects);
371            }
372        }
373    }
374
375    private ValueNode merge(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) {
376        if (currentValue == null || currentValue.isDeleted()) {
377            return null;
378        } else if (block.isPhiAtMerge(currentValue)) {
379            if (otherValue == null || otherValue == TWO_SLOT_MARKER || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) {
380                propagateDelete((ValuePhiNode) currentValue);
381                return null;
382            }
383            ((PhiNode) currentValue).addInput(otherValue);
384            return currentValue;
385        } else if (currentValue != otherValue) {
386            assert !(block instanceof LoopBeginNode) : String.format("Phi functions for loop headers are create eagerly for changed locals and all stack slots: %s != %s", currentValue, otherValue);
387            if (currentValue == TWO_SLOT_MARKER || otherValue == TWO_SLOT_MARKER) {
388                return null;
389            } else if (otherValue == null || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) {
390                return null;
391            }
392            return createValuePhi(currentValue, otherValue, block);
393        } else {
394            return currentValue;
395        }
396    }
397
398    private ValuePhiNode createValuePhi(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) {
399        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp().unrestricted(), block));
400        for (int i = 0; i < block.phiPredecessorCount(); i++) {
401            phi.addInput(currentValue);
402        }
403        phi.addInput(otherValue);
404        assert phi.valueCount() == block.phiPredecessorCount() + 1;
405        return phi;
406    }
407
408    private void propagateDelete(FloatingNode node) {
409        assert node instanceof ValuePhiNode || node instanceof ProxyNode;
410        if (node.isDeleted()) {
411            return;
412        }
413        // Collect all phi functions that use this phi so that we can delete them recursively (after
414        // we delete ourselves to avoid circles).
415        List<FloatingNode> propagateUsages = node.usages().filter(FloatingNode.class).filter(isA(ValuePhiNode.class).or(ProxyNode.class)).snapshot();
416
417        // Remove the phi function from all FrameStates where it is used and then delete it.
418        assert node.usages().filter(isNotA(FrameState.class).nor(ValuePhiNode.class).nor(ProxyNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states";
419        node.replaceAtUsages(null);
420        node.safeDelete();
421
422        for (FloatingNode phiUsage : propagateUsages) {
423            propagateDelete(phiUsage);
424        }
425    }
426
427    public void insertLoopPhis(LocalLiveness liveness, int loopId, LoopBeginNode loopBegin, boolean forcePhis) {
428        for (int i = 0; i < localsSize(); i++) {
429            boolean needPhi = forcePhis || liveness.localIsChangedInLoop(loopId, i);
430            if (needPhi) {
431                locals[i] = createLoopPhi(loopBegin, locals[i]);
432            }
433        }
434        for (int i = 0; i < stackSize(); i++) {
435            stack[i] = createLoopPhi(loopBegin, stack[i]);
436        }
437        for (int i = 0; i < lockedObjects.length; i++) {
438            lockedObjects[i] = createLoopPhi(loopBegin, lockedObjects[i]);
439        }
440    }
441
442    public void insertLoopProxies(LoopExitNode loopExit, FrameStateBuilder loopEntryState) {
443        for (int i = 0; i < localsSize(); i++) {
444            ValueNode value = locals[i];
445            if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
446                Debug.log(" inserting proxy for %s", value);
447                locals[i] = ProxyNode.forValue(value, loopExit, graph);
448            }
449        }
450        for (int i = 0; i < stackSize(); i++) {
451            ValueNode value = stack[i];
452            if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
453                Debug.log(" inserting proxy for %s", value);
454                stack[i] = ProxyNode.forValue(value, loopExit, graph);
455            }
456        }
457        for (int i = 0; i < lockedObjects.length; i++) {
458            ValueNode value = lockedObjects[i];
459            if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
460                Debug.log(" inserting proxy for %s", value);
461                lockedObjects[i] = ProxyNode.forValue(value, loopExit, graph);
462            }
463        }
464    }
465
466    public void insertProxies(Function<ValueNode, ValueNode> proxyFunction) {
467        for (int i = 0; i < localsSize(); i++) {
468            ValueNode value = locals[i];
469            if (value != null && value != TWO_SLOT_MARKER) {
470                Debug.log(" inserting proxy for %s", value);
471                locals[i] = proxyFunction.apply(value);
472            }
473        }
474        for (int i = 0; i < stackSize(); i++) {
475            ValueNode value = stack[i];
476            if (value != null && value != TWO_SLOT_MARKER) {
477                Debug.log(" inserting proxy for %s", value);
478                stack[i] = proxyFunction.apply(value);
479            }
480        }
481        for (int i = 0; i < lockedObjects.length; i++) {
482            ValueNode value = lockedObjects[i];
483            if (value != null) {
484                Debug.log(" inserting proxy for %s", value);
485                lockedObjects[i] = proxyFunction.apply(value);
486            }
487        }
488    }
489
490    private ValueNode createLoopPhi(AbstractMergeNode block, ValueNode value) {
491        if (value == null || value == TWO_SLOT_MARKER) {
492            return value;
493        }
494        assert !block.isPhiAtMerge(value) : "phi function for this block already created";
495
496        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(value.stamp().unrestricted(), block));
497        phi.addInput(value);
498        return phi;
499    }
500
501    /**
502     * Adds a locked monitor to this frame state.
503     *
504     * @param object the object whose monitor will be locked.
505     */
506    public void pushLock(ValueNode object, MonitorIdNode monitorId) {
507        assert object.isAlive() && object.getKind() == Kind.Object : "unexpected value: " + object;
508        lockedObjects = Arrays.copyOf(lockedObjects, lockedObjects.length + 1);
509        monitorIds = Arrays.copyOf(monitorIds, monitorIds.length + 1);
510        lockedObjects[lockedObjects.length - 1] = object;
511        monitorIds[monitorIds.length - 1] = monitorId;
512        assert lockedObjects.length == monitorIds.length;
513    }
514
515    /**
516     * Removes a locked monitor from this frame state.
517     *
518     * @return the object whose monitor was removed from the locks list.
519     */
520    public ValueNode popLock() {
521        try {
522            return lockedObjects[lockedObjects.length - 1];
523        } finally {
524            lockedObjects = lockedObjects.length == 1 ? EMPTY_ARRAY : Arrays.copyOf(lockedObjects, lockedObjects.length - 1);
525            monitorIds = monitorIds.length == 1 ? EMPTY_MONITOR_ARRAY : Arrays.copyOf(monitorIds, monitorIds.length - 1);
526            assert lockedObjects.length == monitorIds.length;
527        }
528    }
529
530    public MonitorIdNode peekMonitorId() {
531        return monitorIds[monitorIds.length - 1];
532    }
533
534    /**
535     * @return the current lock depth
536     */
537    public int lockDepth(boolean includeParents) {
538        int depth = lockedObjects.length;
539        assert depth == monitorIds.length;
540        if (includeParents && parser.getParent() != null) {
541            depth += parser.getParent().frameState.lockDepth(true);
542        }
543        return depth;
544    }
545
546    public boolean contains(ValueNode value) {
547        for (int i = 0; i < localsSize(); i++) {
548            if (locals[i] == value) {
549                return true;
550            }
551        }
552        for (int i = 0; i < stackSize(); i++) {
553            if (stack[i] == value) {
554                return true;
555            }
556        }
557        assert lockedObjects.length == monitorIds.length;
558        for (int i = 0; i < lockedObjects.length; i++) {
559            if (lockedObjects[i] == value || monitorIds[i] == value) {
560                return true;
561            }
562        }
563        return false;
564    }
565
566    public void clearNonLiveLocals(BciBlock block, LocalLiveness liveness, boolean liveIn) {
567        /*
568         * (lstadler) if somebody is tempted to remove/disable this clearing code: it's possible to
569         * remove it for normal compilations, but not for OSR compilations - otherwise dead object
570         * slots at the OSR entry aren't cleared. it is also not enough to rely on PiNodes with
571         * Kind.Illegal, because the conflicting branch might not have been parsed.
572         */
573        if (!parser.graphBuilderConfig.clearNonLiveLocals()) {
574            return;
575        }
576        if (liveIn) {
577            for (int i = 0; i < locals.length; i++) {
578                if (!liveness.localIsLiveIn(block, i)) {
579                    assert locals[i] != TWO_SLOT_MARKER || locals[i - 1] == null : "Clearing of second slot must have cleared the first slot too";
580                    locals[i] = null;
581                }
582            }
583        } else {
584            for (int i = 0; i < locals.length; i++) {
585                if (!liveness.localIsLiveOut(block, i)) {
586                    assert locals[i] != TWO_SLOT_MARKER || locals[i - 1] == null : "Clearing of second slot must have cleared the first slot too";
587                    locals[i] = null;
588                }
589            }
590        }
591    }
592
593    /**
594     * Clears all local variables.
595     */
596    public void clearLocals() {
597        for (int i = 0; i < locals.length; i++) {
598            locals[i] = null;
599        }
600    }
601
602    /**
603     * @see BytecodeFrame#rethrowException
604     */
605    public boolean rethrowException() {
606        return rethrowException;
607    }
608
609    /**
610     * @see BytecodeFrame#rethrowException
611     */
612    public void setRethrowException(boolean b) {
613        rethrowException = b;
614    }
615
616    /**
617     * Returns the size of the local variables.
618     *
619     * @return the size of the local variables
620     */
621    public int localsSize() {
622        return locals.length;
623    }
624
625    /**
626     * Gets the current size (height) of the stack.
627     */
628    public int stackSize() {
629        return stackSize;
630    }
631
632    private boolean verifyKind(Kind slotKind, ValueNode x) {
633        assert x != null;
634        assert x != TWO_SLOT_MARKER;
635        assert slotKind.getSlotCount() > 0;
636
637        if (canVerifyKind) {
638            assert x.getKind().getStackKind() == slotKind.getStackKind();
639        }
640        return true;
641    }
642
643    /**
644     * Loads the local variable at the specified index, checking that the returned value is non-null
645     * and that two-stack values are properly handled.
646     *
647     * @param i the index of the local variable to load
648     * @param slotKind the kind of the local variable from the point of view of the bytecodes
649     * @return the instruction that produced the specified local
650     */
651    public ValueNode loadLocal(int i, Kind slotKind) {
652        ValueNode x = locals[i];
653        assert verifyKind(slotKind, x);
654        assert slotKind.needsTwoSlots() ? locals[i + 1] == TWO_SLOT_MARKER : (i == locals.length - 1 || locals[i + 1] != TWO_SLOT_MARKER);
655        return x;
656    }
657
658    /**
659     * Stores a given local variable at the specified index. If the value occupies two slots, then
660     * the next local variable index is also overwritten.
661     *
662     * @param i the index at which to store
663     * @param slotKind the kind of the local variable from the point of view of the bytecodes
664     * @param x the instruction which produces the value for the local
665     */
666    public void storeLocal(int i, Kind slotKind, ValueNode x) {
667        assert verifyKind(slotKind, x);
668
669        if (locals[i] == TWO_SLOT_MARKER) {
670            /* Writing the second slot of a two-slot value invalidates the first slot. */
671            locals[i - 1] = null;
672        }
673        locals[i] = x;
674        if (slotKind.needsTwoSlots()) {
675            /* Writing a two-slot value: mark the second slot. */
676            locals[i + 1] = TWO_SLOT_MARKER;
677        } else if (i < locals.length - 1 && locals[i + 1] == TWO_SLOT_MARKER) {
678            /*
679             * Writing a one-slot value to an index previously occupied by a two-slot value: clear
680             * the old marker of the second slot.
681             */
682            locals[i + 1] = null;
683        }
684    }
685
686    /**
687     * Pushes an instruction onto the stack with the expected type.
688     *
689     * @param slotKind the kind of the stack element from the point of view of the bytecodes
690     * @param x the instruction to push onto the stack
691     */
692    public void push(Kind slotKind, ValueNode x) {
693        assert verifyKind(slotKind, x);
694
695        xpush(x);
696        if (slotKind.needsTwoSlots()) {
697            xpush(TWO_SLOT_MARKER);
698        }
699    }
700
701    public void pushReturn(Kind slotKind, ValueNode x) {
702        if (slotKind != Kind.Void) {
703            push(slotKind, x);
704        }
705    }
706
707    /**
708     * Pops an instruction off the stack with the expected type.
709     *
710     * @param slotKind the kind of the stack element from the point of view of the bytecodes
711     * @return the instruction on the top of the stack
712     */
713    public ValueNode pop(Kind slotKind) {
714        if (slotKind.needsTwoSlots()) {
715            ValueNode s = xpop();
716            assert s == TWO_SLOT_MARKER;
717        }
718        ValueNode x = xpop();
719        assert verifyKind(slotKind, x);
720        return x;
721    }
722
723    private void xpush(ValueNode x) {
724        assert x != null;
725        stack[stackSize++] = x;
726    }
727
728    private ValueNode xpop() {
729        ValueNode result = stack[--stackSize];
730        assert result != null;
731        return result;
732    }
733
734    private ValueNode xpeek() {
735        ValueNode result = stack[stackSize - 1];
736        assert result != null;
737        return result;
738    }
739
740    /**
741     * Pop the specified number of slots off of this stack and return them as an array of
742     * instructions.
743     *
744     * @return an array containing the arguments off of the stack
745     */
746    public ValueNode[] popArguments(int argSize) {
747        ValueNode[] result = allocateArray(argSize);
748        for (int i = argSize - 1; i >= 0; i--) {
749            ValueNode x = xpop();
750            if (x == TWO_SLOT_MARKER) {
751                /* Ignore second slot of two-slot value. */
752                x = xpop();
753            }
754            assert x != null && x != TWO_SLOT_MARKER;
755            result[i] = x;
756        }
757        return result;
758    }
759
760    /**
761     * Clears all values on this stack.
762     */
763    public void clearStack() {
764        stackSize = 0;
765    }
766
767    /**
768     * Performs a raw stack operation as defined in the Java bytecode specification.
769     *
770     * @param opcode The Java bytecode.
771     */
772    public void stackOp(int opcode) {
773        switch (opcode) {
774            case POP: {
775                ValueNode w1 = xpop();
776                assert w1 != TWO_SLOT_MARKER;
777                break;
778            }
779            case POP2: {
780                xpop();
781                ValueNode w2 = xpop();
782                assert w2 != TWO_SLOT_MARKER;
783                break;
784            }
785            case DUP: {
786                ValueNode w1 = xpeek();
787                assert w1 != TWO_SLOT_MARKER;
788                xpush(w1);
789                break;
790            }
791            case DUP_X1: {
792                ValueNode w1 = xpop();
793                ValueNode w2 = xpop();
794                assert w1 != TWO_SLOT_MARKER;
795                xpush(w1);
796                xpush(w2);
797                xpush(w1);
798                break;
799            }
800            case DUP_X2: {
801                ValueNode w1 = xpop();
802                ValueNode w2 = xpop();
803                ValueNode w3 = xpop();
804                assert w1 != TWO_SLOT_MARKER;
805                xpush(w1);
806                xpush(w3);
807                xpush(w2);
808                xpush(w1);
809                break;
810            }
811            case DUP2: {
812                ValueNode w1 = xpop();
813                ValueNode w2 = xpop();
814                xpush(w2);
815                xpush(w1);
816                xpush(w2);
817                xpush(w1);
818                break;
819            }
820            case DUP2_X1: {
821                ValueNode w1 = xpop();
822                ValueNode w2 = xpop();
823                ValueNode w3 = xpop();
824                xpush(w2);
825                xpush(w1);
826                xpush(w3);
827                xpush(w2);
828                xpush(w1);
829                break;
830            }
831            case DUP2_X2: {
832                ValueNode w1 = xpop();
833                ValueNode w2 = xpop();
834                ValueNode w3 = xpop();
835                ValueNode w4 = xpop();
836                xpush(w2);
837                xpush(w1);
838                xpush(w4);
839                xpush(w3);
840                xpush(w2);
841                xpush(w1);
842                break;
843            }
844            case SWAP: {
845                ValueNode w1 = xpop();
846                ValueNode w2 = xpop();
847                assert w1 != TWO_SLOT_MARKER;
848                assert w2 != TWO_SLOT_MARKER;
849                xpush(w1);
850                xpush(w2);
851                break;
852            }
853            default:
854                throw shouldNotReachHere();
855        }
856    }
857
858    @Override
859    public int hashCode() {
860        int result = hashCode(locals, locals.length);
861        result *= 13;
862        result += hashCode(stack, this.stackSize);
863        return result;
864    }
865
866    private static int hashCode(Object[] a, int length) {
867        int result = 1;
868        for (int i = 0; i < length; ++i) {
869            Object element = a[i];
870            result = 31 * result + (element == null ? 0 : System.identityHashCode(element));
871        }
872        return result;
873    }
874
875    private static boolean equals(ValueNode[] a, ValueNode[] b, int length) {
876        for (int i = 0; i < length; ++i) {
877            if (a[i] != b[i]) {
878                return false;
879            }
880        }
881        return true;
882    }
883
884    @Override
885    public boolean equals(Object otherObject) {
886        if (otherObject instanceof FrameStateBuilder) {
887            FrameStateBuilder other = (FrameStateBuilder) otherObject;
888            if (!other.method.equals(method)) {
889                return false;
890            }
891            if (other.stackSize != stackSize) {
892                return false;
893            }
894            if (other.parser != parser) {
895                return false;
896            }
897            if (other.rethrowException != rethrowException) {
898                return false;
899            }
900            if (other.graph != graph) {
901                return false;
902            }
903            if (other.locals.length != locals.length) {
904                return false;
905            }
906            return equals(other.locals, locals, locals.length) && equals(other.stack, stack, stackSize) && equals(other.lockedObjects, lockedObjects, lockedObjects.length) &&
907                            equals(other.monitorIds, monitorIds, monitorIds.length);
908        }
909        return false;
910    }
911
912    @Override
913    public boolean isAfterSideEffect() {
914        return sideEffects != null;
915    }
916
917    @Override
918    public Iterable<StateSplit> sideEffects() {
919        return sideEffects;
920    }
921
922    @Override
923    public void addSideEffect(StateSplit sideEffect) {
924        assert sideEffect != null;
925        assert sideEffect.hasSideEffect();
926        if (sideEffects == null) {
927            sideEffects = new ArrayList<>(4);
928        }
929        sideEffects.add(sideEffect);
930    }
931
932    public void traceState() {
933        Debug.log(String.format("|   state [nr locals = %d, stack depth = %d, method = %s]", localsSize(), stackSize(), method));
934        for (int i = 0; i < localsSize(); ++i) {
935            ValueNode value = locals[i];
936            Debug.log(String.format("|   local[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getKind().getJavaName(), value));
937        }
938        for (int i = 0; i < stackSize(); ++i) {
939            ValueNode value = stack[i];
940            Debug.log(String.format("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" : value == TWO_SLOT_MARKER ? "second" : value.getKind().getJavaName(), value));
941        }
942    }
943}