| /* |
| * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.oops; |
| |
| import java.io.*; |
| import java.util.*; |
| import sun.jvm.hotspot.interpreter.*; |
| import sun.jvm.hotspot.runtime.*; |
| import sun.jvm.hotspot.utilities.*; |
| |
| /** Minimal port of the VM's oop map generator for interpreted frames */ |
| |
| public class GenerateOopMap { |
| interface JumpClosure { |
| public void process(GenerateOopMap c, int bcpDelta, int[] data); |
| } |
| |
| // Used for debugging this code |
| private static final boolean DEBUG = false; |
| |
| // These two should be removed. But requires som code to be cleaned up |
| private static final int MAXARGSIZE = 256; // This should be enough |
| private static final int MAX_LOCAL_VARS = 65536; // 16-bit entry |
| private static final boolean TraceMonitorMismatch = true; |
| private static final boolean TraceOopMapRewrites = true; |
| |
| // Commonly used constants |
| static CellTypeState[] epsilonCTS = { CellTypeState.bottom }; |
| static CellTypeState refCTS = CellTypeState.ref; |
| static CellTypeState valCTS = CellTypeState.value; |
| static CellTypeState[] vCTS = { CellTypeState.value, CellTypeState.bottom }; |
| static CellTypeState[] rCTS = { CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] rrCTS = { CellTypeState.ref, CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] vrCTS = { CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] vvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; |
| static CellTypeState[] rvrCTS = { CellTypeState.ref, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] vvrCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] vvvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; |
| static CellTypeState[] vvvrCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.ref, CellTypeState.bottom }; |
| static CellTypeState[] vvvvCTS = { CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.value, CellTypeState.bottom }; |
| |
| /** Specialization of SignatureIterator - compute the effects of a call */ |
| static class ComputeCallStack extends SignatureIterator { |
| CellTypeStateList _effect; |
| int _idx; |
| |
| void set(CellTypeState state) { _effect.get(_idx++).set(state); } |
| int length() { return _idx; }; |
| |
| public void doBool () { set(CellTypeState.value); } |
| public void doChar () { set(CellTypeState.value); } |
| public void doFloat () { set(CellTypeState.value); } |
| public void doByte () { set(CellTypeState.value); } |
| public void doShort () { set(CellTypeState.value); } |
| public void doInt () { set(CellTypeState.value); } |
| public void doVoid () { set(CellTypeState.bottom);} |
| public void doObject(int begin, int end) { set(CellTypeState.ref); } |
| public void doArray (int begin, int end) { set(CellTypeState.ref); } |
| |
| public void doDouble() { set(CellTypeState.value); |
| set(CellTypeState.value); } |
| public void doLong () { set(CellTypeState.value); |
| set(CellTypeState.value); } |
| |
| ComputeCallStack(Symbol signature) { |
| super(signature); |
| } |
| |
| // Compute methods |
| int computeForParameters(boolean is_static, CellTypeStateList effect) { |
| _idx = 0; |
| _effect = effect; |
| |
| if (!is_static) { |
| effect.get(_idx++).set(CellTypeState.ref); |
| } |
| |
| iterateParameters(); |
| |
| return length(); |
| }; |
| |
| int computeForReturntype(CellTypeStateList effect) { |
| _idx = 0; |
| _effect = effect; |
| iterateReturntype(); |
| set(CellTypeState.bottom); // Always terminate with a bottom state, so ppush works |
| |
| return length(); |
| } |
| } |
| |
| /** Specialization of SignatureIterator - in order to set up first stack frame */ |
| static class ComputeEntryStack extends SignatureIterator { |
| CellTypeStateList _effect; |
| int _idx; |
| |
| void set(CellTypeState state) { _effect.get(_idx++).set(state); } |
| int length() { return _idx; }; |
| |
| public void doBool () { set(CellTypeState.value); } |
| public void doChar () { set(CellTypeState.value); } |
| public void doFloat () { set(CellTypeState.value); } |
| public void doByte () { set(CellTypeState.value); } |
| public void doShort () { set(CellTypeState.value); } |
| public void doInt () { set(CellTypeState.value); } |
| public void doVoid () { set(CellTypeState.bottom);} |
| public void doObject(int begin, int end) { set(CellTypeState.makeSlotRef(_idx)); } |
| public void doArray (int begin, int end) { set(CellTypeState.makeSlotRef(_idx)); } |
| |
| public void doDouble() { set(CellTypeState.value); |
| set(CellTypeState.value); } |
| public void doLong () { set(CellTypeState.value); |
| set(CellTypeState.value); } |
| |
| ComputeEntryStack(Symbol signature) { |
| super(signature); |
| } |
| |
| // Compute methods |
| int computeForParameters(boolean is_static, CellTypeStateList effect) { |
| _idx = 0; |
| _effect = effect; |
| |
| if (!is_static) { |
| effect.get(_idx++).set(CellTypeState.makeSlotRef(0)); |
| } |
| |
| iterateParameters(); |
| |
| return length(); |
| }; |
| |
| int computeForReturntype(CellTypeStateList effect) { |
| _idx = 0; |
| _effect = effect; |
| iterateReturntype(); |
| set(CellTypeState.bottom); // Always terminate with a bottom state, so ppush works |
| |
| return length(); |
| } |
| } |
| |
| /** Contains maping between jsr targets and there return addresses. |
| One-to-many mapping. */ |
| static class RetTableEntry { |
| private static int _init_nof_jsrs; // Default size of jsrs list |
| private int _target_bci; // Target PC address of jump (bytecode index) |
| private List/*<int>*/ _jsrs; // List of return addresses (bytecode index) |
| private RetTableEntry _next; // Link to next entry |
| |
| RetTableEntry(int target, RetTableEntry next) { |
| _target_bci = target; |
| _jsrs = new ArrayList(_init_nof_jsrs); |
| _next = next; |
| } |
| |
| // Query |
| int targetBci() { return _target_bci; } |
| int nofJsrs() { return _jsrs.size(); } |
| int jsrs(int i) { return ((Integer) _jsrs.get(i)).intValue(); } |
| |
| // Update entry |
| void addJsr (int return_bci) { _jsrs.add(new Integer(return_bci)); } |
| void addDelta(int bci, int delta) { |
| if (_target_bci > bci) { |
| _target_bci += delta; |
| } |
| |
| for (int k = 0; k < nofJsrs(); k++) { |
| int jsr = jsrs(k); |
| if (jsr > bci) { |
| _jsrs.set(k, new Integer(jsr+delta)); |
| } |
| } |
| } |
| RetTableEntry next() { return _next; } |
| } |
| |
| static class RetTable { |
| private RetTableEntry _first; |
| private static int _init_nof_entries; |
| |
| private void addJsr(int return_bci, int target_bci) { |
| RetTableEntry entry = _first; |
| |
| // Scan table for entry |
| for (;(entry != null) && (entry.targetBci() != target_bci); entry = entry.next()); |
| |
| if (entry == null) { |
| // Allocate new entry and put in list |
| entry = new RetTableEntry(target_bci, _first); |
| _first = entry; |
| } |
| |
| // Now "entry" is set. Make sure that the entry is initialized |
| // and has room for the new jsr. |
| entry.addJsr(return_bci); |
| } |
| |
| RetTable() {} |
| void computeRetTable(Method method) { |
| BytecodeStream i = new BytecodeStream(method); |
| int bytecode; |
| |
| while( (bytecode = i.next()) >= 0) { |
| switch (bytecode) { |
| case Bytecodes._jsr: |
| addJsr(i.nextBCI(), i.dest()); |
| break; |
| case Bytecodes._jsr_w: |
| addJsr(i.nextBCI(), i.dest_w()); |
| break; |
| } |
| } |
| } |
| void updateRetTable(int bci, int delta) { |
| RetTableEntry cur = _first; |
| while(cur != null) { |
| cur.addDelta(bci, delta); |
| cur = cur.next(); |
| } |
| } |
| RetTableEntry findJsrsForTarget(int targBci) { |
| RetTableEntry cur = _first; |
| |
| while(cur != null) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(cur.targetBci() != -1, "sanity check"); |
| } |
| if (cur.targetBci() == targBci) { |
| return cur; |
| } |
| cur = cur.next(); |
| } |
| throw new RuntimeException("Should not reach here"); |
| } |
| } |
| |
| static class BasicBlock { |
| private boolean _changed; // Reached a fixpoint or not |
| static final int _dead_basic_block = -2; |
| // Alive but not yet reached by analysis |
| static final int _unreached = -1; |
| // >=0: Alive and has a merged state |
| |
| int _bci; // Start of basic block |
| int _end_bci; // Bci of last instruction in basicblock |
| int _max_locals; // Determines split between vars and stack |
| int _max_stack; // Determines split between stack and monitors |
| CellTypeStateList _state; // State (vars, stack) at entry. |
| int _stack_top; // -1 indicates bottom stack value. |
| int _monitor_top; // -1 indicates bottom monitor stack value. |
| |
| CellTypeStateList vars() { return _state; } |
| CellTypeStateList stack() { return _state.subList(_max_locals, _state.size()); } |
| |
| boolean changed() { return _changed; } |
| void setChanged(boolean s) { _changed = s; } |
| |
| // Analysis has reached this basicblock |
| boolean isReachable() { return _stack_top >= 0; } |
| |
| // All basicblocks that are unreachable are going to have a _stack_top == _dead_basic_block. |
| // This info. is setup in a pre-parse before the real abstract interpretation starts. |
| boolean isDead() { return _stack_top == _dead_basic_block; } |
| boolean isAlive() { return _stack_top != _dead_basic_block; } |
| void markAsAlive() { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(isDead(), "must be dead"); |
| _stack_top = _unreached; |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| // Protected routines for GenerateOopMap |
| // |
| |
| // _monitor_top is set to this constant to indicate that a monitor matching |
| // problem was encountered prior to this point in control flow. |
| protected static final int bad_monitors = -1; |
| |
| // Main variables |
| Method _method; // The method we are examining |
| RetTable _rt; // Contains the return address mappings |
| int _max_locals; // Cached value of no. of locals |
| int _max_stack; // Cached value of max. stack depth |
| int _max_monitors; // Cached value of max. monitor stack depth |
| boolean _has_exceptions; // True, if exceptions exist for method |
| boolean _got_error; // True, if an error occured during interpretation. |
| String _error_msg; // Error message. Set if _got_error is true. |
| // bool _did_rewriting; // was bytecodes rewritten |
| // bool _did_relocation; // was relocation neccessary |
| boolean _monitor_safe; // The monitors in this method have been determined |
| // to be safe. |
| |
| // Working Cell type state |
| int _state_len; // Size of states |
| CellTypeStateList _state; // list of states |
| char[] _state_vec_buf; // Buffer used to print a readable version of a state |
| int _stack_top; |
| int _monitor_top; |
| |
| // Timing and statistics |
| // static elapsedTimer _total_oopmap_time; // Holds cumulative oopmap generation time |
| // static long _total_byte_count; // Holds cumulative number of bytes inspected |
| |
| // Monitor query logic |
| int _report_for_exit_bci; |
| int _matching_enter_bci; |
| |
| // Cell type methods |
| void initState() { |
| _state_len = _max_locals + _max_stack + _max_monitors; |
| _state = new CellTypeStateList(_state_len); |
| _state_vec_buf = new char[Math.max(_max_locals, Math.max(_max_stack, Math.max(_max_monitors, 1)))]; |
| } |
| void makeContextUninitialized () { |
| CellTypeStateList vs = vars(); |
| |
| for (int i = 0; i < _max_locals; i++) |
| vs.get(i).set(CellTypeState.uninit); |
| |
| _stack_top = 0; |
| _monitor_top = 0; |
| } |
| |
| int methodsigToEffect (Symbol signature, boolean isStatic, CellTypeStateList effect) { |
| ComputeEntryStack ces = new ComputeEntryStack(signature); |
| return ces.computeForParameters(isStatic, effect); |
| } |
| |
| boolean mergeStateVectors (CellTypeStateList cts, CellTypeStateList bbts) { |
| int i; |
| int len = _max_locals + _stack_top; |
| boolean change = false; |
| |
| for (i = len - 1; i >= 0; i--) { |
| CellTypeState v = cts.get(i).merge(bbts.get(i), i); |
| change = change || !v.equal(bbts.get(i)); |
| bbts.get(i).set(v); |
| } |
| |
| if (_max_monitors > 0 && _monitor_top != bad_monitors) { |
| // If there are no monitors in the program, or there has been |
| // a monitor matching error before this point in the program, |
| // then we do not merge in the monitor state. |
| |
| int base = _max_locals + _max_stack; |
| len = base + _monitor_top; |
| for (i = len - 1; i >= base; i--) { |
| CellTypeState v = cts.get(i).merge(bbts.get(i), i); |
| |
| // Can we prove that, when there has been a change, it will already |
| // have been detected at this point? That would make this equal |
| // check here unnecessary. |
| change = change || !v.equal(bbts.get(i)); |
| bbts.get(i).set(v); |
| } |
| } |
| |
| return change; |
| } |
| |
| void copyState (CellTypeStateList dst, CellTypeStateList src) { |
| int len = _max_locals + _stack_top; |
| for (int i = 0; i < len; i++) { |
| if (src.get(i).isNonlockReference()) { |
| dst.get(i).set(CellTypeState.makeSlotRef(i)); |
| } else { |
| dst.get(i).set(src.get(i)); |
| } |
| } |
| if (_max_monitors > 0 && _monitor_top != bad_monitors) { |
| int base = _max_locals + _max_stack; |
| len = base + _monitor_top; |
| for (int i = base; i < len; i++) { |
| dst.get(i).set(src.get(i)); |
| } |
| } |
| } |
| |
| void mergeStateIntoBB (BasicBlock bb) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bb.isAlive(), "merging state into a dead basicblock"); |
| } |
| |
| if (_stack_top == bb._stack_top) { |
| if (_monitor_top == bb._monitor_top) { |
| if (mergeStateVectors(_state, bb._state)) { |
| bb.setChanged(true); |
| } |
| } else { |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("monitor stack height merge conflict"); |
| } |
| // When the monitor stacks are not matched, we set _monitor_top to |
| // bad_monitors. This signals that, from here on, the monitor stack cannot |
| // be trusted. In particular, monitorexit bytecodes may throw |
| // exceptions. We mark this block as changed so that the change |
| // propagates properly. |
| bb._monitor_top = bad_monitors; |
| bb.setChanged(true); |
| _monitor_safe = false; |
| } |
| } else if (!bb.isReachable()) { |
| // First time we look at this BB |
| copyState(bb._state, _state); |
| bb._stack_top = _stack_top; |
| bb._monitor_top = _monitor_top; |
| bb.setChanged(true); |
| } else { |
| throw new RuntimeException("stack height conflict: " + |
| _stack_top + " vs. " + bb._stack_top); |
| } |
| } |
| |
| void mergeState (int bci, int[] data) { |
| mergeStateIntoBB(getBasicBlockAt(bci)); |
| } |
| |
| void setVar (int localNo, CellTypeState cts) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(cts.isReference() || cts.isValue() || cts.isAddress(), |
| "wrong celltypestate"); |
| } |
| if (localNo < 0 || localNo > _max_locals) { |
| throw new RuntimeException("variable write error: r" + localNo); |
| } |
| vars().get(localNo).set(cts); |
| } |
| |
| CellTypeState getVar (int localNo) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(localNo < _max_locals + _nof_refval_conflicts, "variable read error"); |
| } |
| if (localNo < 0 || localNo > _max_locals) { |
| throw new RuntimeException("variable read error: r" + localNo); |
| } |
| return vars().get(localNo).copy(); |
| } |
| |
| CellTypeState pop () { |
| if ( _stack_top <= 0) { |
| throw new RuntimeException("stack underflow"); |
| } |
| return stack().get(--_stack_top).copy(); |
| } |
| |
| void push (CellTypeState cts) { |
| if ( _stack_top >= _max_stack) { |
| if (DEBUG) { |
| System.err.println("Method: " + method().getName().asString() + method().getSignature().asString() + |
| " _stack_top: " + _stack_top + " _max_stack: " + _max_stack); |
| } |
| throw new RuntimeException("stack overflow"); |
| } |
| stack().get(_stack_top++).set(cts); |
| if (DEBUG) { |
| System.err.println("After push: _stack_top: " + _stack_top + |
| " _max_stack: " + _max_stack + |
| " just pushed: " + cts.toChar()); |
| } |
| } |
| |
| CellTypeState monitorPop () { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(_monitor_top != bad_monitors, "monitorPop called on error monitor stack"); |
| } |
| if (_monitor_top == 0) { |
| // We have detected a pop of an empty monitor stack. |
| _monitor_safe = false; |
| _monitor_top = bad_monitors; |
| |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("monitor stack underflow"); |
| } |
| return CellTypeState.ref; // just to keep the analysis going. |
| } |
| return monitors().get(--_monitor_top).copy(); |
| } |
| |
| void monitorPush (CellTypeState cts) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(_monitor_top != bad_monitors, "monitorPush called on error monitor stack"); |
| } |
| if (_monitor_top >= _max_monitors) { |
| // Some monitorenter is being executed more than once. |
| // This means that the monitor stack cannot be simulated. |
| _monitor_safe = false; |
| _monitor_top = bad_monitors; |
| |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("monitor stack overflow"); |
| } |
| return; |
| } |
| monitors().get(_monitor_top++).set(cts); |
| } |
| |
| CellTypeStateList vars () { return _state; } |
| CellTypeStateList stack () { return _state.subList(_max_locals, _state.size()); } |
| CellTypeStateList monitors() { return _state.subList(_max_locals+_max_stack, _state.size()); } |
| |
| void replaceAllCTSMatches (CellTypeState match, |
| CellTypeState replace) { |
| int i; |
| int len = _max_locals + _stack_top; |
| boolean change = false; |
| |
| for (i = len - 1; i >= 0; i--) { |
| if (match.equal(_state.get(i))) { |
| _state.get(i).set(replace); |
| } |
| } |
| |
| if (_monitor_top > 0) { |
| int base = _max_locals + _max_stack; |
| len = base + _monitor_top; |
| for (i = len - 1; i >= base; i--) { |
| if (match.equal(_state.get(i))) { |
| _state.get(i).set(replace); |
| } |
| } |
| } |
| } |
| |
| void printStates (PrintStream tty, CellTypeStateList vector, int num) { |
| for (int i = 0; i < num; i++) { |
| vector.get(i).print(tty); |
| } |
| } |
| |
| void printCurrentState (PrintStream tty, |
| BytecodeStream currentBC, |
| boolean detailed) { |
| if (detailed) { |
| tty.print(" " + currentBC.bci() + " vars = "); |
| printStates(tty, vars(), _max_locals); |
| tty.print(" " + Bytecodes.name(currentBC.code())); |
| switch(currentBC.code()) { |
| case Bytecodes._invokevirtual: |
| case Bytecodes._invokespecial: |
| case Bytecodes._invokestatic: |
| case Bytecodes._invokeinterface: |
| case Bytecodes._invokedynamic: |
| // FIXME: print signature of referenced method (need more |
| // accessors in ConstantPool and ConstantPoolCache) |
| int idx = currentBC.hasIndexU4() ? currentBC.getIndexU4() : currentBC.getIndexU2(); |
| tty.print(" idx " + idx); |
| /* |
| int idx = currentBC.getIndexU2(); |
| ConstantPool cp = method().getConstants(); |
| int nameAndTypeIdx = cp.name_and_type_ref_index_at(idx); |
| int signatureIdx = cp.signature_ref_index_at(nameAndTypeIdx); |
| Symbol* signature = cp.symbol_at(signatureIdx); |
| tty.print("%s", signature.as_C_string()); |
| */ |
| } |
| tty.println(); |
| tty.print(" stack = "); |
| printStates(tty, stack(), _stack_top); |
| tty.println(); |
| if (_monitor_top != bad_monitors) { |
| tty.print(" monitors = "); |
| printStates(tty, monitors(), _monitor_top); |
| } else { |
| tty.print(" [bad monitor stack]"); |
| } |
| tty.println(); |
| } else { |
| tty.print(" " + currentBC.bci() + " vars = '" + |
| stateVecToString(vars(), _max_locals) + "' "); |
| tty.print(" stack = '" + stateVecToString(stack(), _stack_top) + "' "); |
| if (_monitor_top != bad_monitors) { |
| tty.print(" monitors = '" + stateVecToString(monitors(), _monitor_top) + "' \t" + |
| Bytecodes.name(currentBC.code())); |
| } else { |
| tty.print(" [bad monitor stack]"); |
| } |
| switch(currentBC.code()) { |
| case Bytecodes._invokevirtual: |
| case Bytecodes._invokespecial: |
| case Bytecodes._invokestatic: |
| case Bytecodes._invokeinterface: |
| case Bytecodes._invokedynamic: |
| // FIXME: print signature of referenced method (need more |
| // accessors in ConstantPool and ConstantPoolCache) |
| int idx = currentBC.hasIndexU4() ? currentBC.getIndexU4() : currentBC.getIndexU2(); |
| tty.print(" idx " + idx); |
| /* |
| int idx = currentBC.getIndexU2(); |
| ConstantPool* cp = method().constants(); |
| int nameAndTypeIdx = cp.name_and_type_ref_index_at(idx); |
| int signatureIdx = cp.signature_ref_index_at(nameAndTypeIdx); |
| Symbol* signature = cp.symbol_at(signatureIdx); |
| tty.print("%s", signature.as_C_string()); |
| */ |
| } |
| tty.println(); |
| } |
| } |
| |
| void reportMonitorMismatch (String msg) { |
| if (Assert.ASSERTS_ENABLED) { |
| System.err.print(" Monitor mismatch in method "); |
| method().printValueOn(System.err); |
| System.err.println(": " + msg); |
| } |
| } |
| |
| // Basicblock info |
| BasicBlock[] _basic_blocks; // Array of basicblock info |
| int _gc_points; |
| int _bb_count; |
| BitMap _bb_hdr_bits; |
| |
| // Basicblocks methods |
| void initializeBB () { |
| _gc_points = 0; |
| _bb_count = 0; |
| _bb_hdr_bits = new BitMap((int) _method.getCodeSize()); |
| } |
| |
| void markBBHeadersAndCountGCPoints() { |
| initializeBB(); |
| |
| boolean fellThrough = false; // False to get first BB marked. |
| |
| // First mark all exception handlers as start of a basic-block |
| if (method().hasExceptionTable()) { |
| ExceptionTableElement[] excps = method().getExceptionTable(); |
| for(int i = 0; i < excps.length; i++) { |
| markBB(excps[i].getHandlerPC(), null); |
| } |
| } |
| |
| // Then iterate through the code |
| BytecodeStream bcs = new BytecodeStream(_method); |
| int bytecode; |
| |
| while( (bytecode = bcs.next()) >= 0) { |
| int bci = bcs.bci(); |
| |
| if (!fellThrough) |
| markBB(bci, null); |
| |
| fellThrough = jumpTargetsDo(bcs, |
| new JumpClosure() { |
| public void process(GenerateOopMap c, int bcpDelta, int[] data) { |
| c.markBB(bcpDelta, data); |
| } |
| }, |
| null); |
| |
| /* We will also mark successors of jsr's as basic block headers. */ |
| switch (bytecode) { |
| case Bytecodes._jsr: |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(!fellThrough, "should not happen"); |
| } |
| markBB(bci + Bytecodes.lengthFor(bytecode), null); |
| break; |
| case Bytecodes._jsr_w: |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(!fellThrough, "should not happen"); |
| } |
| markBB(bci + Bytecodes.lengthFor(bytecode), null); |
| break; |
| } |
| |
| if (possibleGCPoint(bcs)) |
| _gc_points++; |
| } |
| } |
| |
| boolean isBBHeader (int bci) { |
| return _bb_hdr_bits.at(bci); |
| } |
| |
| int gcPoints () { |
| return _gc_points; |
| } |
| |
| int bbCount () { |
| return _bb_count; |
| } |
| |
| void setBBMarkBit (int bci) { |
| _bb_hdr_bits.atPut(bci, true); |
| } |
| |
| void clear_bbmark_bit (int bci) { |
| _bb_hdr_bits.atPut(bci, false); |
| } |
| |
| BasicBlock getBasicBlockAt (int bci) { |
| BasicBlock bb = getBasicBlockContaining(bci); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bb._bci == bci, "should have found BB"); |
| } |
| return bb; |
| } |
| |
| BasicBlock getBasicBlockContaining (int bci) { |
| BasicBlock[] bbs = _basic_blocks; |
| int lo = 0, hi = _bb_count - 1; |
| |
| while (lo <= hi) { |
| int m = (lo + hi) / 2; |
| int mbci = bbs[m]._bci; |
| int nbci; |
| |
| if ( m == _bb_count-1) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that( bci >= mbci && bci < method().getCodeSize(), "sanity check failed"); |
| } |
| return bbs[m]; |
| } else { |
| nbci = bbs[m+1]._bci; |
| } |
| |
| if ( mbci <= bci && bci < nbci) { |
| return bbs[m]; |
| } else if (mbci < bci) { |
| lo = m + 1; |
| } else { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(mbci > bci, "sanity check"); |
| } |
| hi = m - 1; |
| } |
| } |
| |
| throw new RuntimeException("should have found BB"); |
| } |
| |
| void interpBB (BasicBlock bb) { |
| // We do not want to do anything in case the basic-block has not been initialized. This |
| // will happen in the case where there is dead-code hang around in a method. |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bb.isReachable(), "should be reachable or deadcode exist"); |
| } |
| restoreState(bb); |
| |
| BytecodeStream itr = new BytecodeStream(_method); |
| |
| // Set iterator interval to be the current basicblock |
| int lim_bci = nextBBStartPC(bb); |
| itr.setInterval(bb._bci, lim_bci); |
| |
| if (DEBUG) { |
| System.err.println("interpBB: method = " + method().getName().asString() + |
| method().getSignature().asString() + |
| ", BCI interval [" + bb._bci + ", " + lim_bci + ")"); |
| { |
| System.err.print("Bytecodes:"); |
| for (int i = bb._bci; i < lim_bci; i++) { |
| System.err.print(" 0x" + Long.toHexString(method().getBytecodeOrBPAt(i))); |
| } |
| System.err.println(); |
| } |
| } |
| |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(lim_bci != bb._bci, "must be at least one instruction in a basicblock"); |
| } |
| itr.next(); // read first instruction |
| |
| // Iterates through all bytecodes except the last in a basic block. |
| // We handle the last one special, since there is controlflow change. |
| while(itr.nextBCI() < lim_bci && !_got_error) { |
| if (_has_exceptions || (_monitor_top != 0)) { |
| // We do not need to interpret the results of exceptional |
| // continuation from this instruction when the method has no |
| // exception handlers and the monitor stack is currently |
| // empty. |
| doExceptionEdge(itr); |
| } |
| interp1(itr); |
| itr.next(); |
| } |
| |
| // Handle last instruction. |
| if (!_got_error) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(itr.nextBCI() == lim_bci, "must point to end"); |
| } |
| if (_has_exceptions || (_monitor_top != 0)) { |
| doExceptionEdge(itr); |
| } |
| interp1(itr); |
| |
| boolean fall_through = jumpTargetsDo(itr, new JumpClosure() { |
| public void process(GenerateOopMap c, int bcpDelta, int[] data) { |
| c.mergeState(bcpDelta, data); |
| } |
| }, null); |
| if (_got_error) return; |
| |
| if (itr.code() == Bytecodes._ret) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(!fall_through, "cannot be set if ret instruction"); |
| } |
| // Automatically handles 'wide' ret indicies |
| retJumpTargetsDo(itr, new JumpClosure() { |
| public void process(GenerateOopMap c, int bcpDelta, int[] data) { |
| c.mergeState(bcpDelta, data); |
| } |
| }, itr.getIndex(), null); |
| } else if (fall_through) { |
| // Hit end of BB, but the instr. was a fall-through instruction, |
| // so perform transition as if the BB ended in a "jump". |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(lim_bci == _basic_blocks[bbIndex(bb) + 1]._bci, "there must be another bb"); |
| } |
| mergeStateIntoBB(_basic_blocks[bbIndex(bb) + 1]); |
| } |
| } |
| } |
| |
| void restoreState (BasicBlock bb) { |
| for (int i = 0; i < _state_len; i++) { |
| _state.get(i).set(bb._state.get(i)); |
| } |
| _stack_top = bb._stack_top; |
| _monitor_top = bb._monitor_top; |
| } |
| |
| int nextBBStartPC (BasicBlock bb) { |
| int bbNum = bbIndex(bb) + 1; |
| if (bbNum == _bb_count) |
| return (int) method().getCodeSize(); |
| |
| return _basic_blocks[bbNum]._bci; |
| } |
| |
| void updateBasicBlocks (int bci, int delta) { |
| BitMap bbBits = new BitMap((int) (_method.getCodeSize() + delta)); |
| for(int k = 0; k < _bb_count; k++) { |
| if (_basic_blocks[k]._bci > bci) { |
| _basic_blocks[k]._bci += delta; |
| _basic_blocks[k]._end_bci += delta; |
| } |
| bbBits.atPut(_basic_blocks[k]._bci, true); |
| } |
| _bb_hdr_bits = bbBits; |
| } |
| |
| void markBB(int bci, int[] data) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bci>= 0 && bci < method().getCodeSize(), "index out of bounds"); |
| } |
| if (isBBHeader(bci)) |
| return; |
| |
| // FIXME: remove |
| // if (TraceNewOopMapGeneration) { |
| // tty.print_cr("Basicblock#%d begins at: %d", c._bb_count, bci); |
| // } |
| setBBMarkBit(bci); |
| _bb_count++; |
| } |
| |
| // Dead code detection |
| void markReachableCode() { |
| final int[] change = new int[1]; |
| change[0] = 1; |
| |
| // Mark entry basic block as alive and all exception handlers |
| _basic_blocks[0].markAsAlive(); |
| if (method().hasExceptionTable()) { |
| ExceptionTableElement[] excps = method().getExceptionTable(); |
| for(int i = 0; i < excps.length; i ++) { |
| BasicBlock bb = getBasicBlockAt(excps[i].getHandlerPC()); |
| // If block is not already alive (due to multiple exception handlers to same bb), then |
| // make it alive |
| if (bb.isDead()) |
| bb.markAsAlive(); |
| } |
| } |
| |
| BytecodeStream bcs = new BytecodeStream(_method); |
| |
| // Iterate through all basic blocks until we reach a fixpoint |
| while (change[0] != 0) { |
| change[0] = 0; |
| |
| for (int i = 0; i < _bb_count; i++) { |
| BasicBlock bb = _basic_blocks[i]; |
| if (bb.isAlive()) { |
| // Position bytecodestream at last bytecode in basicblock |
| bcs.setStart(bb._end_bci); |
| bcs.next(); |
| int bytecode = bcs.code(); |
| int bci = bcs.bci(); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bci == bb._end_bci, "wrong bci"); |
| } |
| |
| boolean fell_through = jumpTargetsDo(bcs, new JumpClosure() { |
| public void process(GenerateOopMap c, int bciDelta, int[] change) { |
| c.reachableBasicblock(bciDelta, change); |
| } |
| }, change); |
| |
| // We will also mark successors of jsr's as alive. |
| switch (bytecode) { |
| case Bytecodes._jsr: |
| case Bytecodes._jsr_w: |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(!fell_through, "should not happen"); |
| } |
| reachableBasicblock(bci + Bytecodes.lengthFor(bytecode), change); |
| break; |
| } |
| if (fell_through) { |
| // Mark successor as alive |
| if (_basic_blocks[i+1].isDead()) { |
| _basic_blocks[i+1].markAsAlive(); |
| change[0] = 1; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void reachableBasicblock (int bci, int[] data) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bci>= 0 && bci < method().getCodeSize(), "index out of bounds"); |
| } |
| BasicBlock bb = getBasicBlockAt(bci); |
| if (bb.isDead()) { |
| bb.markAsAlive(); |
| data[0] = 1; // Mark basicblock as changed |
| } |
| } |
| |
| // Interpretation methods (primary) |
| void doInterpretation () { |
| // "i" is just for debugging, so we can detect cases where this loop is |
| // iterated more than once. |
| int i = 0; |
| do { |
| // FIXME: remove |
| // if (TraceNewOopMapGeneration) { |
| // tty.print("\n\nIteration #%d of do_interpretation loop, method:\n", i); |
| // method().print_name(tty); |
| // tty.print("\n\n"); |
| // } |
| _conflict = false; |
| _monitor_safe = true; |
| // init_state is now called from init_basic_blocks. The length of a |
| // state vector cannot be determined until we have made a pass through |
| // the bytecodes counting the possible monitor entries. |
| if (!_got_error) initBasicBlocks(); |
| if (!_got_error) setupMethodEntryState(); |
| if (!_got_error) interpAll(); |
| if (!_got_error) rewriteRefvalConflicts(); |
| i++; |
| } while (_conflict && !_got_error); |
| } |
| |
| void initBasicBlocks () { |
| // Note: Could consider reserving only the needed space for each BB's state |
| // (entry stack may not be of maximal height for every basic block). |
| // But cumbersome since we don't know the stack heights yet. (Nor the |
| // monitor stack heights...) |
| |
| _basic_blocks = new BasicBlock[_bb_count]; |
| for (int i = 0; i < _bb_count; i++) { |
| _basic_blocks[i] = new BasicBlock(); |
| } |
| |
| // Make a pass through the bytecodes. Count the number of monitorenters. |
| // This can be used an upper bound on the monitor stack depth in programs |
| // which obey stack discipline with their monitor usage. Initialize the |
| // known information about basic blocks. |
| BytecodeStream j = new BytecodeStream(_method); |
| int bytecode; |
| |
| int bbNo = 0; |
| int monitor_count = 0; |
| int prev_bci = -1; |
| while( (bytecode = j.next()) >= 0) { |
| if (j.code() == Bytecodes._monitorenter) { |
| monitor_count++; |
| } |
| |
| int bci = j.bci(); |
| if (isBBHeader(bci)) { |
| // Initialize the basicblock structure |
| BasicBlock bb = _basic_blocks[bbNo]; |
| bb._bci = bci; |
| bb._max_locals = _max_locals; |
| bb._max_stack = _max_stack; |
| bb.setChanged(false); |
| bb._stack_top = BasicBlock._dead_basic_block; // Initialize all basicblocks are dead. |
| bb._monitor_top = bad_monitors; |
| |
| if (bbNo > 0) { |
| _basic_blocks[bbNo - 1]._end_bci = prev_bci; |
| } |
| |
| bbNo++; |
| } |
| // Remember prevous bci. |
| prev_bci = bci; |
| } |
| // Set |
| _basic_blocks[bbNo-1]._end_bci = prev_bci; |
| |
| _max_monitors = monitor_count; |
| |
| // Now that we have a bound on the depth of the monitor stack, we can |
| // initialize the CellTypeState-related information. |
| initState(); |
| |
| // We allocate space for all state-vectors for all basicblocks in one huge chuck. |
| // Then in the next part of the code, we set a pointer in each _basic_block that |
| // points to each piece. |
| CellTypeStateList basicBlockState = new CellTypeStateList(bbNo * _state_len); |
| |
| // Make a pass over the basicblocks and assign their state vectors. |
| for (int blockNum=0; blockNum < bbNo; blockNum++) { |
| BasicBlock bb = _basic_blocks[blockNum]; |
| bb._state = basicBlockState.subList(blockNum * _state_len, (blockNum + 1) * _state_len); |
| |
| if (Assert.ASSERTS_ENABLED) { |
| if (blockNum + 1 < bbNo) { |
| int bc_len = Bytecodes.javaLengthAt(_method, bb._end_bci); |
| Assert.that(bb._end_bci + bc_len == _basic_blocks[blockNum + 1]._bci, |
| "unmatched bci info in basicblock"); |
| } |
| } |
| } |
| if (Assert.ASSERTS_ENABLED) { |
| BasicBlock bb = _basic_blocks[bbNo-1]; |
| int bc_len = Bytecodes.javaLengthAt(_method, bb._end_bci); |
| Assert.that(bb._end_bci + bc_len == _method.getCodeSize(), "wrong end bci"); |
| } |
| |
| // Check that the correct number of basicblocks was found |
| if (bbNo !=_bb_count) { |
| if (bbNo < _bb_count) { |
| throw new RuntimeException("jump into the middle of instruction?"); |
| } else { |
| throw new RuntimeException("extra basic blocks - should not happen?"); |
| } |
| } |
| |
| // Mark all alive blocks |
| markReachableCode(); |
| } |
| |
| void setupMethodEntryState () { |
| // Initialize all locals to 'uninit' and set stack-height to 0 |
| makeContextUninitialized(); |
| |
| // Initialize CellState type of arguments |
| methodsigToEffect(method().getSignature(), method().isStatic(), vars()); |
| |
| // If some references must be pre-assigned to null, then set that up |
| initializeVars(); |
| |
| // This is the start state |
| mergeStateIntoBB(_basic_blocks[0]); |
| |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(_basic_blocks[0].changed(), "we are not getting off the ground"); |
| } |
| } |
| |
| void interpAll () { |
| boolean change = true; |
| |
| while (change && !_got_error) { |
| change = false; |
| for (int i = 0; i < _bb_count && !_got_error; i++) { |
| BasicBlock bb = _basic_blocks[i]; |
| if (bb.changed()) { |
| if (_got_error) return; |
| change = true; |
| bb.setChanged(false); |
| interpBB(bb); |
| } |
| } |
| } |
| } |
| |
| // |
| // Interpretation methods (secondary) |
| // |
| |
| /** Sets the current state to be the state after executing the |
| current instruction, starting in the current state. */ |
| void interp1 (BytecodeStream itr) { |
| if (DEBUG) { |
| System.err.println(" - bci " + itr.bci() + " " + itr.code()); |
| printCurrentState(System.err, itr, false); |
| } |
| |
| // if (TraceNewOopMapGeneration) { |
| // print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); |
| // } |
| |
| // Should we report the results? Result is reported *before* the |
| // instruction at the current bci is executed. However, not for |
| // calls. For calls we do not want to include the arguments, so we |
| // postpone the reporting until they have been popped (in method |
| // ppl). |
| if (_report_result == true) { |
| switch(itr.code()) { |
| case Bytecodes._invokevirtual: |
| case Bytecodes._invokespecial: |
| case Bytecodes._invokestatic: |
| case Bytecodes._invokeinterface: |
| case Bytecodes._invokedynamic: |
| _itr_send = itr; |
| _report_result_for_send = true; |
| break; |
| default: |
| fillStackmapForOpcodes(itr, vars(), stack(), _stack_top); |
| break; |
| } |
| } |
| |
| // abstract interpretation of current opcode |
| switch(itr.code()) { |
| case Bytecodes._nop: break; |
| case Bytecodes._goto: break; |
| case Bytecodes._goto_w: break; |
| case Bytecodes._iinc: break; |
| case Bytecodes._return: doReturnMonitorCheck(); |
| break; |
| |
| case Bytecodes._aconst_null: |
| case Bytecodes._new: ppush1(CellTypeState.makeLineRef(itr.bci())); |
| break; |
| |
| case Bytecodes._iconst_m1: |
| case Bytecodes._iconst_0: |
| case Bytecodes._iconst_1: |
| case Bytecodes._iconst_2: |
| case Bytecodes._iconst_3: |
| case Bytecodes._iconst_4: |
| case Bytecodes._iconst_5: |
| case Bytecodes._fconst_0: |
| case Bytecodes._fconst_1: |
| case Bytecodes._fconst_2: |
| case Bytecodes._bipush: |
| case Bytecodes._sipush: ppush1(valCTS); break; |
| |
| case Bytecodes._lconst_0: |
| case Bytecodes._lconst_1: |
| case Bytecodes._dconst_0: |
| case Bytecodes._dconst_1: ppush(vvCTS); break; |
| |
| case Bytecodes._ldc2_w: ppush(vvCTS); break; |
| |
| case Bytecodes._ldc: doLdc(itr.bci()); break; |
| case Bytecodes._ldc_w: doLdc(itr.bci()); break; |
| |
| case Bytecodes._iload: |
| case Bytecodes._fload: ppload(vCTS, itr.getIndex()); break; |
| |
| case Bytecodes._lload: |
| case Bytecodes._dload: ppload(vvCTS,itr.getIndex()); break; |
| |
| case Bytecodes._aload: ppload(rCTS, itr.getIndex()); break; |
| |
| case Bytecodes._iload_0: |
| case Bytecodes._fload_0: ppload(vCTS, 0); break; |
| case Bytecodes._iload_1: |
| case Bytecodes._fload_1: ppload(vCTS, 1); break; |
| case Bytecodes._iload_2: |
| case Bytecodes._fload_2: ppload(vCTS, 2); break; |
| case Bytecodes._iload_3: |
| case Bytecodes._fload_3: ppload(vCTS, 3); break; |
| |
| case Bytecodes._lload_0: |
| case Bytecodes._dload_0: ppload(vvCTS, 0); break; |
| case Bytecodes._lload_1: |
| case Bytecodes._dload_1: ppload(vvCTS, 1); break; |
| case Bytecodes._lload_2: |
| case Bytecodes._dload_2: ppload(vvCTS, 2); break; |
| case Bytecodes._lload_3: |
| case Bytecodes._dload_3: ppload(vvCTS, 3); break; |
| |
| case Bytecodes._aload_0: ppload(rCTS, 0); break; |
| case Bytecodes._aload_1: ppload(rCTS, 1); break; |
| case Bytecodes._aload_2: ppload(rCTS, 2); break; |
| case Bytecodes._aload_3: ppload(rCTS, 3); break; |
| |
| case Bytecodes._iaload: |
| case Bytecodes._faload: |
| case Bytecodes._baload: |
| case Bytecodes._caload: |
| case Bytecodes._saload: pp(vrCTS, vCTS); break; |
| |
| case Bytecodes._laload: pp(vrCTS, vvCTS); break; |
| case Bytecodes._daload: pp(vrCTS, vvCTS); break; |
| |
| case Bytecodes._aaload: ppNewRef(vrCTS, itr.bci()); break; |
| |
| case Bytecodes._istore: |
| case Bytecodes._fstore: ppstore(vCTS, itr.getIndex()); break; |
| |
| case Bytecodes._lstore: |
| case Bytecodes._dstore: ppstore(vvCTS, itr.getIndex()); break; |
| |
| case Bytecodes._astore: doAstore(itr.getIndex()); break; |
| |
| case Bytecodes._istore_0: |
| case Bytecodes._fstore_0: ppstore(vCTS, 0); break; |
| case Bytecodes._istore_1: |
| case Bytecodes._fstore_1: ppstore(vCTS, 1); break; |
| case Bytecodes._istore_2: |
| case Bytecodes._fstore_2: ppstore(vCTS, 2); break; |
| case Bytecodes._istore_3: |
| case Bytecodes._fstore_3: ppstore(vCTS, 3); break; |
| |
| case Bytecodes._lstore_0: |
| case Bytecodes._dstore_0: ppstore(vvCTS, 0); break; |
| case Bytecodes._lstore_1: |
| case Bytecodes._dstore_1: ppstore(vvCTS, 1); break; |
| case Bytecodes._lstore_2: |
| case Bytecodes._dstore_2: ppstore(vvCTS, 2); break; |
| case Bytecodes._lstore_3: |
| case Bytecodes._dstore_3: ppstore(vvCTS, 3); break; |
| |
| case Bytecodes._astore_0: doAstore(0); break; |
| case Bytecodes._astore_1: doAstore(1); break; |
| case Bytecodes._astore_2: doAstore(2); break; |
| case Bytecodes._astore_3: doAstore(3); break; |
| |
| case Bytecodes._iastore: |
| case Bytecodes._fastore: |
| case Bytecodes._bastore: |
| case Bytecodes._castore: |
| case Bytecodes._sastore: ppop(vvrCTS); break; |
| case Bytecodes._lastore: |
| case Bytecodes._dastore: ppop(vvvrCTS); break; |
| case Bytecodes._aastore: ppop(rvrCTS); break; |
| |
| case Bytecodes._pop: ppopAny(1); break; |
| case Bytecodes._pop2: ppopAny(2); break; |
| |
| case Bytecodes._dup: ppdupswap(1, "11"); break; |
| case Bytecodes._dup_x1: ppdupswap(2, "121"); break; |
| case Bytecodes._dup_x2: ppdupswap(3, "1321"); break; |
| case Bytecodes._dup2: ppdupswap(2, "2121"); break; |
| case Bytecodes._dup2_x1: ppdupswap(3, "21321"); break; |
| case Bytecodes._dup2_x2: ppdupswap(4, "214321"); break; |
| case Bytecodes._swap: ppdupswap(2, "12"); break; |
| |
| case Bytecodes._iadd: |
| case Bytecodes._fadd: |
| case Bytecodes._isub: |
| case Bytecodes._fsub: |
| case Bytecodes._imul: |
| case Bytecodes._fmul: |
| case Bytecodes._idiv: |
| case Bytecodes._fdiv: |
| case Bytecodes._irem: |
| case Bytecodes._frem: |
| case Bytecodes._ishl: |
| case Bytecodes._ishr: |
| case Bytecodes._iushr: |
| case Bytecodes._iand: |
| case Bytecodes._ior: |
| case Bytecodes._ixor: |
| case Bytecodes._l2f: |
| case Bytecodes._l2i: |
| case Bytecodes._d2f: |
| case Bytecodes._d2i: |
| case Bytecodes._fcmpl: |
| case Bytecodes._fcmpg: pp(vvCTS, vCTS); break; |
| |
| case Bytecodes._ladd: |
| case Bytecodes._dadd: |
| case Bytecodes._lsub: |
| case Bytecodes._dsub: |
| case Bytecodes._lmul: |
| case Bytecodes._dmul: |
| case Bytecodes._ldiv: |
| case Bytecodes._ddiv: |
| case Bytecodes._lrem: |
| case Bytecodes._drem: |
| case Bytecodes._land: |
| case Bytecodes._lor: |
| case Bytecodes._lxor: pp(vvvvCTS, vvCTS); break; |
| |
| case Bytecodes._ineg: |
| case Bytecodes._fneg: |
| case Bytecodes._i2f: |
| case Bytecodes._f2i: |
| case Bytecodes._i2c: |
| case Bytecodes._i2s: |
| case Bytecodes._i2b: pp(vCTS, vCTS); break; |
| |
| case Bytecodes._lneg: |
| case Bytecodes._dneg: |
| case Bytecodes._l2d: |
| case Bytecodes._d2l: pp(vvCTS, vvCTS); break; |
| |
| case Bytecodes._lshl: |
| case Bytecodes._lshr: |
| case Bytecodes._lushr: pp(vvvCTS, vvCTS); break; |
| |
| case Bytecodes._i2l: |
| case Bytecodes._i2d: |
| case Bytecodes._f2l: |
| case Bytecodes._f2d: pp(vCTS, vvCTS); break; |
| |
| case Bytecodes._lcmp: pp(vvvvCTS, vCTS); break; |
| case Bytecodes._dcmpl: |
| case Bytecodes._dcmpg: pp(vvvvCTS, vCTS); break; |
| |
| case Bytecodes._ifeq: |
| case Bytecodes._ifne: |
| case Bytecodes._iflt: |
| case Bytecodes._ifge: |
| case Bytecodes._ifgt: |
| case Bytecodes._ifle: |
| case Bytecodes._tableswitch: ppop1(valCTS); |
| break; |
| case Bytecodes._ireturn: |
| case Bytecodes._freturn: doReturnMonitorCheck(); |
| ppop1(valCTS); |
| break; |
| case Bytecodes._if_icmpeq: |
| case Bytecodes._if_icmpne: |
| case Bytecodes._if_icmplt: |
| case Bytecodes._if_icmpge: |
| case Bytecodes._if_icmpgt: |
| case Bytecodes._if_icmple: ppop(vvCTS); |
| break; |
| |
| case Bytecodes._lreturn: doReturnMonitorCheck(); |
| ppop(vvCTS); |
| break; |
| |
| case Bytecodes._dreturn: doReturnMonitorCheck(); |
| ppop(vvCTS); |
| break; |
| |
| case Bytecodes._if_acmpeq: |
| case Bytecodes._if_acmpne: ppop(rrCTS); break; |
| |
| case Bytecodes._jsr: doJsr(itr.dest()); break; |
| case Bytecodes._jsr_w: doJsr(itr.dest_w()); break; |
| |
| case Bytecodes._getstatic: doField(true, true, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._putstatic: doField(false, true, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._getfield: doField(true, false, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._putfield: doField(false, false, itr.getIndexU2Cpcache(), itr.bci()); break; |
| |
| case Bytecodes._invokevirtual: |
| case Bytecodes._invokespecial: doMethod(false, false, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._invokestatic: doMethod(true, false, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._invokedynamic: doMethod(true, false, itr.getIndexU4(), itr.bci()); break; |
| case Bytecodes._invokeinterface: doMethod(false, true, itr.getIndexU2Cpcache(), itr.bci()); break; |
| case Bytecodes._newarray: |
| case Bytecodes._anewarray: ppNewRef(vCTS, itr.bci()); break; |
| case Bytecodes._checkcast: doCheckcast(); break; |
| case Bytecodes._arraylength: |
| case Bytecodes._instanceof: pp(rCTS, vCTS); break; |
| case Bytecodes._monitorenter: doMonitorenter(itr.bci()); break; |
| case Bytecodes._monitorexit: doMonitorexit(itr.bci()); break; |
| |
| case Bytecodes._athrow: // handled by do_exception_edge() BUT ... |
| // vlh(apple): doExceptionEdge() does not get |
| // called if method has no exception handlers |
| if ((!_has_exceptions) && (_monitor_top > 0)) { |
| _monitor_safe = false; |
| } |
| break; |
| |
| case Bytecodes._areturn: doReturnMonitorCheck(); |
| ppop1(refCTS); |
| break; |
| case Bytecodes._ifnull: |
| case Bytecodes._ifnonnull: ppop1(refCTS); break; |
| case Bytecodes._multianewarray: doMultianewarray(itr.codeAt(itr.bci() + 3), itr.bci()); break; |
| |
| case Bytecodes._wide: throw new RuntimeException("Iterator should skip this bytecode"); |
| case Bytecodes._ret: break; |
| |
| // Java opcodes |
| case Bytecodes._fast_aaccess_0: ppNewRef(rCTS, itr.bci()); break; // Pair bytecode for (aload_0, _fast_agetfield) |
| |
| case Bytecodes._fast_iaccess_0: ppush1(valCTS); break; // Pair bytecode for (aload_0, _fast_igetfield) |
| |
| case Bytecodes._fast_igetfield: pp(rCTS, vCTS); break; |
| |
| case Bytecodes._fast_agetfield: ppNewRef(rCTS, itr.bci()); break; |
| |
| case Bytecodes._fast_aload_0: ppload(rCTS, 0); break; |
| |
| case Bytecodes._lookupswitch: |
| case Bytecodes._fast_linearswitch: |
| case Bytecodes._fast_binaryswitch: ppop1(valCTS); break; |
| |
| default: |
| throw new RuntimeException("unexpected opcode: " + itr.code()); |
| } |
| } |
| |
| void doExceptionEdge (BytecodeStream itr) { |
| // Only check exception edge, if bytecode can trap |
| if (!Bytecodes.canTrap(itr.code())) return; |
| switch (itr.code()) { |
| case Bytecodes._aload_0: |
| case Bytecodes._fast_aload_0: |
| // These bytecodes can trap for rewriting. We need to assume that |
| // they do not throw exceptions to make the monitor analysis work. |
| return; |
| |
| case Bytecodes._ireturn: |
| case Bytecodes._lreturn: |
| case Bytecodes._freturn: |
| case Bytecodes._dreturn: |
| case Bytecodes._areturn: |
| case Bytecodes._return: |
| // If the monitor stack height is not zero when we leave the method, |
| // then we are either exiting with a non-empty stack or we have |
| // found monitor trouble earlier in our analysis. In either case, |
| // assume an exception could be taken here. |
| if (_monitor_top == 0) { |
| return; |
| } |
| break; |
| |
| case Bytecodes._monitorexit: |
| // If the monitor stack height is bad_monitors, then we have detected a |
| // monitor matching problem earlier in the analysis. If the |
| // monitor stack height is 0, we are about to pop a monitor |
| // off of an empty stack. In either case, the bytecode |
| // could throw an exception. |
| if (_monitor_top != bad_monitors && _monitor_top != 0) { |
| return; |
| } |
| break; |
| } |
| |
| if (_has_exceptions) { |
| int bci = itr.bci(); |
| ExceptionTableElement[] exct = method().getExceptionTable(); |
| for(int i = 0; i< exct.length; i++) { |
| int start_pc = exct[i].getStartPC(); |
| int end_pc = exct[i].getEndPC(); |
| int handler_pc = exct[i].getHandlerPC(); |
| int catch_type = exct[i].getCatchTypeIndex(); |
| |
| if (start_pc <= bci && bci < end_pc) { |
| BasicBlock excBB = getBasicBlockAt(handler_pc); |
| CellTypeStateList excStk = excBB.stack(); |
| CellTypeStateList cOpStck = stack(); |
| CellTypeState cOpStck_0 = cOpStck.get(0).copy(); |
| int cOpStackTop = _stack_top; |
| |
| // Exception stacks are always the same. |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(method().getMaxStack() > 0, "sanity check"); |
| } |
| |
| // We remembered the size and first element of "cOpStck" |
| // above; now we temporarily set them to the appropriate |
| // values for an exception handler. |
| cOpStck.get(0).set(CellTypeState.makeSlotRef(_max_locals)); |
| _stack_top = 1; |
| |
| mergeStateIntoBB(excBB); |
| |
| // Now undo the temporary change. |
| cOpStck.get(0).set(cOpStck_0); |
| _stack_top = cOpStackTop; |
| |
| // If this is a "catch all" handler, then we do not need to |
| // consider any additional handlers. |
| if (catch_type == 0) { |
| return; |
| } |
| } |
| } |
| } |
| |
| // It is possible that none of the exception handlers would have caught |
| // the exception. In this case, we will exit the method. We must |
| // ensure that the monitor stack is empty in this case. |
| if (_monitor_top == 0) { |
| return; |
| } |
| |
| // We pessimistically assume that this exception can escape the |
| // method. (It is possible that it will always be caught, but |
| // we don't care to analyse the types of the catch clauses.) |
| |
| // We don't set _monitor_top to bad_monitors because there are no successors |
| // to this exceptional exit. |
| |
| if (TraceMonitorMismatch && _monitor_safe) { |
| // We check _monitor_safe so that we only report the first mismatched |
| // exceptional exit. |
| reportMonitorMismatch("non-empty monitor stack at exceptional exit"); |
| } |
| _monitor_safe = false; |
| } |
| |
| void checkType (CellTypeState expected, CellTypeState actual) { |
| if (!expected.equalKind(actual)) { |
| throw new RuntimeException("wrong type on stack (found: " + |
| actual.toChar() + " expected: " + |
| expected.toChar() + ")"); |
| } |
| } |
| |
| void ppstore (CellTypeState[] in, int loc_no) { |
| for (int i = 0; i < in.length && !in[i].equal(CellTypeState.bottom); i++) { |
| CellTypeState expected = in[i]; |
| CellTypeState actual = pop(); |
| checkType(expected, actual); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(loc_no >= 0, "sanity check"); |
| } |
| setVar(loc_no++, actual); |
| } |
| } |
| |
| void ppload (CellTypeState[] out, int loc_no) { |
| for (int i = 0; i < out.length && !out[i].equal(CellTypeState.bottom); i++) { |
| CellTypeState out1 = out[i]; |
| CellTypeState vcts = getVar(loc_no); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(out1.canBeReference() || out1.canBeValue(), |
| "can only load refs. and values."); |
| } |
| if (out1.isReference()) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(loc_no>=0, "sanity check"); |
| } |
| if (!vcts.isReference()) { |
| // We were asked to push a reference, but the type of the |
| // variable can be something else |
| _conflict = true; |
| if (vcts.canBeUninit()) { |
| // It is a ref-uninit conflict (at least). If there are other |
| // problems, we'll get them in the next round |
| addToRefInitSet(loc_no); |
| vcts = out1; |
| } else { |
| // It wasn't a ref-uninit conflict. So must be a |
| // ref-val or ref-pc conflict. Split the variable. |
| recordRefvalConflict(loc_no); |
| vcts = out1; |
| } |
| push(out1); // recover... |
| } else { |
| push(vcts); // preserve reference. |
| } |
| // Otherwise it is a conflict, but one that verification would |
| // have caught if illegal. In particular, it can't be a topCTS |
| // resulting from mergeing two difference pcCTS's since the verifier |
| // would have rejected any use of such a merge. |
| } else { |
| push(out1); // handle val/init conflict |
| } |
| loc_no++; |
| } |
| } |
| |
| void ppush1 (CellTypeState in) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(in.isReference() | in.isValue(), "sanity check"); |
| } |
| if (DEBUG) { |
| System.err.println(" - pushing " + in.toChar()); |
| } |
| push(in); |
| } |
| |
| void ppush (CellTypeState[] in) { |
| for (int i = 0; i < in.length && !in[i].equal(CellTypeState.bottom); i++) { |
| ppush1(in[i]); |
| } |
| } |
| |
| void ppush (CellTypeStateList in) { |
| for (int i = 0; i < in.size() && !in.get(i).equal(CellTypeState.bottom); i++) { |
| ppush1(in.get(i)); |
| } |
| } |
| |
| void ppop1 (CellTypeState out) { |
| CellTypeState actual = pop(); |
| if (DEBUG) { |
| System.err.println(" - popping " + actual.toChar() + ", expecting " + out.toChar()); |
| } |
| checkType(out, actual); |
| } |
| |
| void ppop (CellTypeState[] out) { |
| for (int i = 0; i < out.length && !out[i].equal(CellTypeState.bottom); i++) { |
| ppop1(out[i]); |
| } |
| } |
| |
| void ppopAny (int poplen) { |
| if (_stack_top >= poplen) { |
| _stack_top -= poplen; |
| } else { |
| throw new RuntimeException("stack underflow"); |
| } |
| } |
| |
| void pp (CellTypeState[] in, CellTypeState[] out) { |
| ppop(in); |
| ppush(out); |
| } |
| |
| void ppNewRef (CellTypeState[] in, int bci) { |
| ppop(in); |
| ppush1(CellTypeState.makeLineRef(bci)); |
| } |
| |
| void ppdupswap (int poplen, String out) { |
| CellTypeState[] actual = new CellTypeState[5]; |
| Assert.that(poplen < 5, "this must be less than length of actual vector"); |
| |
| // pop all arguments |
| for(int i = 0; i < poplen; i++) actual[i] = pop(); |
| |
| // put them back |
| for (int i = 0; i < out.length(); i++) { |
| char push_ch = out.charAt(i); |
| int idx = push_ch - '1'; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(idx >= 0 && idx < poplen, "wrong arguments"); |
| } |
| push(actual[idx]); |
| } |
| } |
| |
| void doLdc (int bci) { |
| BytecodeLoadConstant ldc = BytecodeLoadConstant.at(_method, bci); |
| ConstantPool cp = method().getConstants(); |
| BasicType bt = ldc.resultType(); |
| CellTypeState cts = (bt == BasicType.T_OBJECT) ? CellTypeState.makeLineRef(bci) : valCTS; |
| ppush1(cts); |
| } |
| |
| void doAstore (int idx) { |
| CellTypeState r_or_p = pop(); |
| if (!r_or_p.isAddress() && !r_or_p.isReference()) { |
| // We actually expected ref or pc, but we only report that we expected a ref. It does not |
| // really matter (at least for now) |
| throw new RuntimeException("wrong type on stack (found: " + |
| r_or_p.toChar() + ", expected: {pr})"); |
| } |
| setVar(idx, r_or_p); |
| } |
| |
| void doJsr (int targBCI) { |
| push(CellTypeState.makeAddr(targBCI)); |
| } |
| |
| void doField (boolean is_get, boolean is_static, int idx, int bci) { |
| // Dig up signature for field in constant pool |
| ConstantPool cp = method().getConstants(); |
| int nameAndTypeIdx = cp.getNameAndTypeRefIndexAt(idx); |
| int signatureIdx = cp.getSignatureRefIndexAt(nameAndTypeIdx); |
| Symbol signature = cp.getSymbolAt(signatureIdx); |
| |
| if (DEBUG) { |
| System.err.println("doField: signature = " + signature.asString() + ", idx = " + idx + |
| ", nameAndTypeIdx = " + nameAndTypeIdx + ", signatureIdx = " + signatureIdx + ", bci = " + bci); |
| } |
| |
| // Parse signature (espcially simple for fields) |
| // The signature is UFT8 encoded, but the first char is always ASCII for signatures. |
| char sigch = (char) signature.getByteAt(0); |
| CellTypeState[] temp = new CellTypeState[4]; |
| CellTypeState[] eff = sigcharToEffect(sigch, bci, temp); |
| |
| CellTypeState[] in = new CellTypeState[4]; |
| CellTypeState[] out; |
| int i = 0; |
| |
| if (is_get) { |
| out = eff; |
| } else { |
| out = epsilonCTS; |
| i = copyCTS(in, eff); |
| } |
| if (!is_static) in[i++] = CellTypeState.ref; |
| in[i] = CellTypeState.bottom; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(i<=3, "sanity check"); |
| } |
| pp(in, out); |
| } |
| |
| void doMethod (boolean is_static, boolean is_interface, int idx, int bci) { |
| // Dig up signature for field in constant pool |
| ConstantPool cp = _method.getConstants(); |
| Symbol signature = cp.getSignatureRefAt(idx); |
| |
| // Parse method signature |
| CellTypeStateList out = new CellTypeStateList(4); |
| CellTypeStateList in = new CellTypeStateList(MAXARGSIZE+1); // Includes result |
| ComputeCallStack cse = new ComputeCallStack(signature); |
| |
| // Compute return type |
| int res_length = cse.computeForReturntype(out); |
| |
| // Temporary hack. |
| if (out.get(0).equal(CellTypeState.ref) && out.get(1).equal(CellTypeState.bottom)) { |
| out.get(0).set(CellTypeState.makeLineRef(bci)); |
| } |
| |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(res_length<=4, "max value should be vv"); |
| } |
| |
| // Compute arguments |
| int arg_length = cse.computeForParameters(is_static, in); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(arg_length<=MAXARGSIZE, "too many locals"); |
| } |
| |
| // Pop arguments |
| for (int i = arg_length - 1; i >= 0; i--) ppop1(in.get(i));// Do args in reverse order. |
| |
| // Report results |
| if (_report_result_for_send == true) { |
| fillStackmapForOpcodes(_itr_send, vars(), stack(), _stack_top); |
| _report_result_for_send = false; |
| } |
| |
| // Push return address |
| ppush(out); |
| } |
| |
| void doMultianewarray (int dims, int bci) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(dims >= 1, "sanity check"); |
| } |
| for(int i = dims -1; i >=0; i--) { |
| ppop1(valCTS); |
| } |
| ppush1(CellTypeState.makeLineRef(bci)); |
| } |
| |
| void doMonitorenter (int bci) { |
| CellTypeState actual = pop(); |
| if (_monitor_top == bad_monitors) { |
| return; |
| } |
| |
| // Bail out when we get repeated locks on an identical monitor. This case |
| // isn't too hard to handle and can be made to work if supporting nested |
| // redundant synchronized statements becomes a priority. |
| // |
| // See also "Note" in do_monitorexit(), below. |
| if (actual.isLockReference()) { |
| _monitor_top = bad_monitors; |
| _monitor_safe = false; |
| |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("nested redundant lock -- bailout..."); |
| } |
| return; |
| } |
| |
| CellTypeState lock = CellTypeState.makeLockRef(bci); |
| checkType(refCTS, actual); |
| if (!actual.isInfoTop()) { |
| replaceAllCTSMatches(actual, lock); |
| monitorPush(lock); |
| } |
| } |
| |
| void doMonitorexit (int bci) { |
| CellTypeState actual = pop(); |
| if (_monitor_top == bad_monitors) { |
| return; |
| } |
| checkType(refCTS, actual); |
| CellTypeState expected = monitorPop(); |
| if (!actual.isLockReference() || !expected.equal(actual)) { |
| // The monitor we are exiting is not verifiably the one |
| // on the top of our monitor stack. This causes a monitor |
| // mismatch. |
| _monitor_top = bad_monitors; |
| _monitor_safe = false; |
| |
| // We need to mark this basic block as changed so that |
| // this monitorexit will be visited again. We need to |
| // do this to ensure that we have accounted for the |
| // possibility that this bytecode will throw an |
| // exception. |
| BasicBlock bb = getBasicBlockContaining(bci); |
| bb.setChanged(true); |
| bb._monitor_top = bad_monitors; |
| |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("improper monitor pair"); |
| } |
| } else { |
| // This code is a fix for the case where we have repeated |
| // locking of the same object in straightline code. We clear |
| // out the lock when it is popped from the monitor stack |
| // and replace it with an unobtrusive reference value that can |
| // be locked again. |
| // |
| // Note: when generateOopMap is fixed to properly handle repeated, |
| // nested, redundant locks on the same object, then this |
| // fix will need to be removed at that time. |
| replaceAllCTSMatches(actual, CellTypeState.makeLineRef(bci)); |
| } |
| |
| if (_report_for_exit_bci == bci) { |
| _matching_enter_bci = expected.getMonitorSource(); |
| } |
| } |
| |
| void doReturnMonitorCheck () { |
| if (_monitor_top > 0) { |
| // The monitor stack must be empty when we leave the method |
| // for the monitors to be properly matched. |
| _monitor_safe = false; |
| |
| // Since there are no successors to the *return bytecode, it |
| // isn't necessary to set _monitor_top to bad_monitors. |
| |
| if (TraceMonitorMismatch) { |
| reportMonitorMismatch("non-empty monitor stack at return"); |
| } |
| } |
| } |
| |
| void doCheckcast () { |
| CellTypeState actual = pop(); |
| checkType(refCTS, actual); |
| push(actual); |
| } |
| |
| CellTypeState[] sigcharToEffect (char sigch, int bci, CellTypeState[] out) { |
| // Object and array |
| if (sigch=='L' || sigch=='[') { |
| out[0] = CellTypeState.makeLineRef(bci); |
| out[1] = CellTypeState.bottom; |
| return out; |
| } |
| if (sigch == 'J' || sigch == 'D' ) return vvCTS; // Long and Double |
| if (sigch == 'V' ) return epsilonCTS; // Void |
| return vCTS; // Otherwise |
| } |
| |
| // Copies (optionally bottom/zero terminated) CTS string from "src" into "dst". |
| // Does NOT terminate with a bottom. Returns the number of cells copied. |
| int copyCTS (CellTypeState[] dst, CellTypeState[] src) { |
| int idx = 0; |
| for (; idx < src.length && !src[idx].isBottom(); idx++) { |
| dst[idx] = src[idx]; |
| } |
| return idx; |
| } |
| |
| // Create result set |
| boolean _report_result; |
| boolean _report_result_for_send; // Unfortunatly, stackmaps for sends are special, so we need some extra |
| BytecodeStream _itr_send; // variables to handle them properly. |
| |
| void reportResult () { |
| // if (TraceNewOopMapGeneration) tty.print_cr("Report result pass"); |
| |
| // We now want to report the result of the parse |
| _report_result = true; |
| |
| // Prolog code |
| fillStackmapProlog(_gc_points); |
| |
| // Mark everything changed, then do one interpretation pass. |
| for (int i = 0; i<_bb_count; i++) { |
| if (_basic_blocks[i].isReachable()) { |
| _basic_blocks[i].setChanged(true); |
| interpBB(_basic_blocks[i]); |
| } |
| } |
| |
| // Note: Since we are skipping dead-code when we are reporting results, then |
| // the no. of encountered gc-points might be fewer than the previously number |
| // we have counted. (dead-code is a pain - it should be removed before we get here) |
| fillStackmapEpilog(); |
| |
| // Report initvars |
| fillInitVars(_init_vars); |
| |
| _report_result = false; |
| } |
| |
| // Initvars |
| List/*<Integer>*/ _init_vars; |
| |
| void initializeVars () { |
| for (int k = 0; k < _init_vars.size(); k++) |
| _state.get(((Integer) _init_vars.get(k)).intValue()).set(CellTypeState.makeSlotRef(k)); |
| } |
| |
| void addToRefInitSet (int localNo) { |
| // if (TraceNewOopMapGeneration) |
| // tty.print_cr("Added init vars: %d", localNo); |
| |
| Integer local = new Integer(localNo); |
| |
| // Is it already in the set? |
| if (_init_vars.contains(local)) |
| return; |
| |
| _init_vars.add(local); |
| } |
| |
| // Conflicts rewrite logic |
| boolean _conflict; // True, if a conflict occured during interpretation |
| int _nof_refval_conflicts; // No. of conflicts that require rewrites |
| int[] _new_var_map; |
| |
| void recordRefvalConflict (int varNo) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(varNo>=0 && varNo< _max_locals, "index out of range"); |
| } |
| |
| if (TraceOopMapRewrites) { |
| System.err.println("### Conflict detected (local no: " + varNo + ")"); |
| } |
| |
| if (_new_var_map == null) { |
| _new_var_map = new int[_max_locals]; |
| for (int k = 0; k < _max_locals; k++) _new_var_map[k] = k; |
| } |
| |
| if ( _new_var_map[varNo] == varNo) { |
| // Check if max. number of locals has been reached |
| if (_max_locals + _nof_refval_conflicts >= MAX_LOCAL_VARS) { |
| throw new RuntimeException("Rewriting exceeded local variable limit"); |
| } |
| _new_var_map[varNo] = _max_locals + _nof_refval_conflicts; |
| _nof_refval_conflicts++; |
| } |
| } |
| |
| void rewriteRefvalConflicts () { |
| if (_nof_refval_conflicts > 0) { |
| if (VM.getVM().isDebugging()) { |
| throw new RuntimeException("Should not reach here (method rewriting should have been done by the VM already)"); |
| } else { |
| throw new RuntimeException("Method rewriting not yet implemented in Java"); |
| } |
| } |
| } |
| // Rewriting-related routines are not needed here |
| // void rewrite_refval_conflict (int from, int to); |
| // bool rewrite_refval_conflict_inst (BytecodeStream *i, int from, int to); |
| // bool rewrite_load_or_store (BytecodeStream *i, Bytecodes.Code bc, Bytecodes.Code bc0, unsigned int varNo); |
| |
| // bool expand_current_instr (int bci, int ilen, int newIlen, u_char inst_buffer[]); |
| // bool is_astore (BytecodeStream *itr, int *index); |
| // bool is_aload (BytecodeStream *itr, int *index); |
| |
| // List of bci's where a return address is on top of the stack |
| // GrowableArray<intptr_t> *_ret_adr_tos; |
| |
| // bool stack_top_holds_ret_addr (int bci); |
| // void compute_ret_adr_at_TOS (); |
| // void update_ret_adr_at_TOS (int bci, int delta); |
| |
| String stateVecToString (CellTypeStateList vec, int len) { |
| for (int i = 0; i < len; i++) { |
| _state_vec_buf[i] = vec.get(i).toChar(); |
| } |
| return new String(_state_vec_buf, 0, len); |
| } |
| |
| // Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instuction |
| // is a control transfer, then calls the jmpFct all possible destinations. |
| void retJumpTargetsDo (BytecodeStream bcs, JumpClosure closure, int varNo, int[] data) { |
| CellTypeState ra = vars().get(varNo); |
| if (!ra.isGoodAddress()) { |
| throw new RuntimeException("ret returns from two jsr subroutines?"); |
| } |
| int target = ra.getInfo(); |
| |
| RetTableEntry rtEnt = _rt.findJsrsForTarget(target); |
| int bci = bcs.bci(); |
| for (int i = 0; i < rtEnt.nofJsrs(); i++) { |
| int target_bci = rtEnt.jsrs(i); |
| // Make sure a jrtRet does not set the changed bit for dead basicblock. |
| BasicBlock jsr_bb = getBasicBlockContaining(target_bci - 1); |
| if (Assert.ASSERTS_ENABLED) { |
| BasicBlock target_bb = _basic_blocks[1 + bbIndex(jsr_bb)]; |
| Assert.that(target_bb == getBasicBlockAt(target_bci), "wrong calc. of successor basicblock"); |
| } |
| boolean alive = jsr_bb.isAlive(); |
| // if (TraceNewOopMapGeneration) { |
| // tty.print("pc = %d, ret . %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); |
| // } |
| if (alive) { |
| closure.process(this, target_bci, data); |
| } |
| } |
| } |
| |
| /** If the current instruction in "c" has no effect on control flow, |
| returns "true". Otherwise, calls "closure.process()" one or |
| more times, with "c", an appropriate "pcDelta", and "data" as |
| arguments, then returns "false". There is one exception: if the |
| current instruction is a "ret", returns "false" without calling |
| "jmpFct". Arrangements for tracking the control flow of a "ret" |
| must be made externally. */ |
| boolean jumpTargetsDo (BytecodeStream bcs, JumpClosure closure, int[] data) { |
| int bci = bcs.bci(); |
| |
| switch (bcs.code()) { |
| case Bytecodes._ifeq: |
| case Bytecodes._ifne: |
| case Bytecodes._iflt: |
| case Bytecodes._ifge: |
| case Bytecodes._ifgt: |
| case Bytecodes._ifle: |
| case Bytecodes._if_icmpeq: |
| case Bytecodes._if_icmpne: |
| case Bytecodes._if_icmplt: |
| case Bytecodes._if_icmpge: |
| case Bytecodes._if_icmpgt: |
| case Bytecodes._if_icmple: |
| case Bytecodes._if_acmpeq: |
| case Bytecodes._if_acmpne: |
| case Bytecodes._ifnull: |
| case Bytecodes._ifnonnull: |
| closure.process(this, bcs.dest(), data); |
| closure.process(this, bci + 3, data); |
| break; |
| |
| case Bytecodes._goto: |
| closure.process(this, bcs.dest(), data); |
| break; |
| case Bytecodes._goto_w: |
| closure.process(this, bcs.dest_w(), data); |
| break; |
| case Bytecodes._tableswitch: |
| { |
| BytecodeTableswitch tableswitch = BytecodeTableswitch.at(bcs); |
| int len = tableswitch.length(); |
| |
| closure.process(this, bci + tableswitch.defaultOffset(), data); /* Default. jump address */ |
| while (--len >= 0) { |
| closure.process(this, bci + tableswitch.destOffsetAt(len), data); |
| } |
| break; |
| } |
| |
| case Bytecodes._fast_linearswitch: // Java opcodes |
| case Bytecodes._fast_binaryswitch: // get_int_table handles conversions |
| case Bytecodes._lookupswitch: |
| { |
| BytecodeLookupswitch lookupswitch = BytecodeLookupswitch.at(bcs); |
| int npairs = lookupswitch.numberOfPairs(); |
| closure.process(this, bci + lookupswitch.defaultOffset(), data); /* Default. */ |
| while(--npairs >= 0) { |
| LookupswitchPair pair = lookupswitch.pairAt(npairs); |
| closure.process(this, bci + pair.offset(), data); |
| } |
| break; |
| } |
| case Bytecodes._jsr: |
| Assert.that(bcs.isWide()==false, "sanity check"); |
| closure.process(this, bcs.dest(), data); |
| break; |
| case Bytecodes._jsr_w: |
| closure.process(this, bcs.dest_w(), data); |
| break; |
| case Bytecodes._wide: |
| throw new RuntimeException("Should not reach here"); |
| case Bytecodes._athrow: |
| case Bytecodes._ireturn: |
| case Bytecodes._lreturn: |
| case Bytecodes._freturn: |
| case Bytecodes._dreturn: |
| case Bytecodes._areturn: |
| case Bytecodes._return: |
| case Bytecodes._ret: |
| break; |
| default: |
| return true; |
| } |
| return false; |
| } |
| |
| // Monitor matching |
| // int fill_out_arrays (int *enter, int *exit, int max); |
| |
| // friend class RelocCallback; |
| |
| //---------------------------------------------------------------------- |
| // Public routines for GenerateOopMap |
| // |
| public GenerateOopMap(Method method) { |
| // We have to initialize all variables here, that can be queried direcly |
| _method = method; |
| _max_locals=0; |
| _init_vars = null; |
| _rt = new RetTable(); |
| } |
| |
| |
| // Compute the map. |
| public void computeMap() { |
| if (DEBUG) { |
| System.err.println("*** GenerateOopMap: computing for " + |
| method().getMethodHolder().getName().asString() + "." + |
| method().getName().asString() + |
| method().getSignature().asString()); |
| } |
| |
| // Initialize values |
| _got_error = false; |
| _conflict = false; |
| _max_locals = (int) method().getMaxLocals(); |
| _max_stack = (int) method().getMaxStack(); |
| _has_exceptions = (method().hasExceptionTable()); |
| _nof_refval_conflicts = 0; |
| _init_vars = new ArrayList(5); // There are seldom more than 5 init_vars |
| _report_result = false; |
| _report_result_for_send = false; |
| _report_for_exit_bci = -1; |
| _new_var_map = null; |
| // _ret_adr_tos = new GrowableArray<intptr_t>(5); // 5 seems like a good number; |
| // _did_rewriting = false; |
| // _did_relocation = false; |
| |
| // FIXME: remove |
| /* |
| if (TraceNewOopMapGeneration) { |
| tty.print("Method name: %s\n", method().name().as_C_string()); |
| if (Verbose) { |
| _method.print_codes(); |
| tty.print_cr("Exception table:"); |
| typeArrayOop excps = method().exception_table(); |
| for(int i = 0; i < excps.length(); i += 4) { |
| tty.print_cr("[%d - %d] . %d", excps.int_at(i + 0), excps.int_at(i + 1), excps.int_at(i + 2)); |
| } |
| } |
| } |
| */ |
| |
| // if no code - do nothing |
| // compiler needs info |
| if (method().getCodeSize() == 0 || _max_locals + method().getMaxStack() == 0) { |
| fillStackmapProlog(0); |
| fillStackmapEpilog(); |
| return; |
| } |
| // Step 1: Compute all jump targets and their return value |
| if (!_got_error) |
| _rt.computeRetTable(_method); |
| |
| // Step 2: Find all basic blocks and count GC points |
| if (!_got_error) |
| markBBHeadersAndCountGCPoints(); |
| |
| // Step 3: Calculate stack maps |
| if (!_got_error) |
| doInterpretation(); |
| |
| // Step 4:Return results |
| if (!_got_error && reportResults()) |
| reportResult(); |
| |
| if (_got_error) { |
| // We could expand this code to throw somekind of exception (e.g., VerifyException). However, |
| // an exception thrown in this part of the code is likly to mean that we are executing some |
| // illegal bytecodes (that the verifier should have caught if turned on), so we will just exit |
| // with a fatal. |
| throw new RuntimeException("Illegal bytecode sequence encountered while generating interpreter pointer maps - method should be rejected by verifier."); |
| } |
| } |
| |
| // Do a callback on fill_stackmap_for_opcodes for basicblock containing bci |
| public void resultForBasicblock(int bci) { |
| // FIXME: remove |
| // if (TraceNewOopMapGeneration) tty.print_cr("Report result pass for basicblock"); |
| |
| // We now want to report the result of the parse |
| _report_result = true; |
| |
| // Find basicblock and report results |
| BasicBlock bb = getBasicBlockContaining(bci); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(bb.isReachable(), "getting result from unreachable basicblock"); |
| } |
| bb.setChanged(true); |
| interpBB(bb); |
| } |
| |
| // Query |
| public int maxLocals() { return _max_locals; } |
| public Method method() { return _method; } |
| |
| // bool did_rewriting() { return _did_rewriting; } |
| // bool did_relocation() { return _did_relocation; } |
| |
| // static void print_time(); |
| |
| // Monitor query |
| public boolean monitorSafe() { return _monitor_safe; } |
| // Takes as input the bci of a monitorexit bytecode. |
| // Returns the bci of the corresponding monitorenter. |
| // Can only be called safely after computeMap() is run. |
| public int getMonitorMatch(int bci) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(_monitor_safe, "Attempt to match monitor in broken code."); |
| } |
| |
| // if (TraceNewOopMapGeneration) |
| // tty.print_cr("Report monitor match for bci : %d", bci); |
| |
| // We want to report the line number of the monitorenter. |
| _report_for_exit_bci = bci; |
| _matching_enter_bci = -1; |
| |
| // Find basicblock and report results |
| BasicBlock bb = getBasicBlockContaining(bci); |
| if (bb.isReachable()) { |
| bb.setChanged(true); |
| interpBB(bb); |
| _report_for_exit_bci = -1; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(_matching_enter_bci != -1, "monitor matching invariant"); |
| } |
| } |
| return _matching_enter_bci; |
| } |
| |
| // Returns a Arena allocated object that contains pairing info. |
| // MonitorPairs* get_pairing(Arena *arena); |
| |
| // copies monitor pairing info into area; area_count specifies maximum |
| // possible number of monitor pairs |
| // int copy_pairing(int pair_count, MonitorPairs* pairs); |
| |
| private int bbIndex(BasicBlock bb) { |
| for (int i = 0; i < _basic_blocks.length; i++) { |
| if (_basic_blocks[i] == bb) { |
| return i; |
| } |
| } |
| throw new RuntimeException("Should have found block"); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Specialization methods. Intended use: |
| // - possibleGCPoint must return true for every bci for which the |
| // stackmaps must be returned |
| // - fillStackmapProlog is called just before the result is |
| // reported. The arguments tells the estimated number of gc points |
| // - fillStackmapForOpcodes is called once for each bytecode index |
| // in order (0...code_length-1) |
| // - fillStackmapEpilog is called after all results has been |
| // reported. Note: Since the algorithm does not report stackmaps for |
| // deadcode, fewer gc_points might have been encounted than assumed |
| // during the epilog. It is the responsibility of the subclass to |
| // count the correct number. |
| // - fillInitVars are called once with the result of the init_vars |
| // computation |
| // |
| // All these methods are used during a call to computeMap. Note: |
| // None of the return results are valid after computeMap returns, |
| // since all values are allocated as resource objects. |
| // |
| // All virtual method must be implemented in subclasses |
| public boolean allowRewrites () { return false; } |
| public boolean reportResults () { return true; } |
| public boolean reportInitVars () { return true; } |
| public boolean possibleGCPoint (BytecodeStream bcs) { throw new RuntimeException("ShouldNotReachHere"); } |
| public void fillStackmapProlog (int nofGCPoints) { throw new RuntimeException("ShouldNotReachHere"); } |
| public void fillStackmapEpilog () { throw new RuntimeException("ShouldNotReachHere"); } |
| public void fillStackmapForOpcodes (BytecodeStream bcs, |
| CellTypeStateList vars, |
| CellTypeStateList stack, |
| int stackTop) { throw new RuntimeException("ShouldNotReachHere"); } |
| public void fillInitVars (List/*<Integer>*/ init_vars) { throw new RuntimeException("ShouldNotReachHere"); } |
| } |