| /* |
| * Copyright (c) 2000, 2009, 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.runtime; |
| |
| import java.util.*; |
| import sun.jvm.hotspot.code.*; |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.oops.*; |
| import sun.jvm.hotspot.utilities.*; |
| |
| /** FIXME: missing many accessors; all we have right now is the method |
| and BCI. NOTE that this has been modified from the VM's version to |
| handle NULL ScopeDescs for the debugging case. This simplifies |
| using code a great deal. */ |
| |
| public class CompiledVFrame extends JavaVFrame { |
| private ScopeDesc scope; |
| private boolean mayBeImprecise; |
| |
| public CompiledVFrame(Frame fr, RegisterMap regMap, JavaThread thread, ScopeDesc scope, boolean mayBeImprecise) { |
| super(fr, regMap, thread); |
| this.scope = scope; |
| this.mayBeImprecise = mayBeImprecise; |
| if (!VM.getVM().isDebugging()) { |
| Assert.that(scope != null, "scope must be present"); |
| } |
| } |
| |
| public boolean isTop() { |
| if (VM.getVM().isDebugging()) { |
| return (getScope() == null || getScope().isTop()); |
| } else { |
| return getScope().isTop(); |
| } |
| } |
| |
| public boolean isCompiledFrame() { |
| return true; |
| } |
| |
| public boolean isDeoptimized() { |
| return fr.isDeoptimized(); |
| } |
| |
| public boolean mayBeImpreciseDbg() { |
| return mayBeImprecise; |
| } |
| |
| /** Returns the active method */ |
| public NMethod getCode() { |
| return VM.getVM().getCodeCache().findNMethod(fr.getPC()); |
| } |
| |
| /** Returns the active method. Does not perform a guarantee |
| regarding unloaded methods -- more suitable for debugging |
| system. */ |
| public NMethod getCodeUnsafe() { |
| return VM.getVM().getCodeCache().findNMethodUnsafe(fr.getPC()); |
| } |
| |
| /** Returns the ScopeDesc */ |
| public ScopeDesc getScope() { |
| return scope; |
| } |
| |
| public Method getMethod() { |
| if (VM.getVM().isDebugging() && getScope() == null) { |
| return getCodeUnsafe().getMethod(); |
| } |
| return getScope().getMethod(); |
| } |
| |
| public StackValueCollection getLocals() { |
| if (getScope() == null) |
| return new StackValueCollection(); |
| List scvList = getScope().getLocals(); |
| if (scvList == null) |
| return new StackValueCollection(); |
| |
| // scvList is the list of ScopeValues describing the JVM stack state. |
| // There is one scv_list entry for every JVM stack state in use. |
| int length = scvList.size(); |
| StackValueCollection result = new StackValueCollection(length); |
| for( int i = 0; i < length; i++ ) |
| result.add( createStackValue((ScopeValue) scvList.get(i)) ); |
| |
| return result; |
| } |
| |
| public StackValueCollection getExpressions() { |
| if (getScope() == null) |
| return new StackValueCollection(); |
| List scvList = getScope().getExpressions(); |
| if (scvList == null) |
| return new StackValueCollection(); |
| |
| // scvList is the list of ScopeValues describing the JVM stack state. |
| // There is one scv_list entry for every JVM stack state in use. |
| int length = scvList.size(); |
| StackValueCollection result = new StackValueCollection(length); |
| for( int i = 0; i < length; i++ ) |
| result.add( createStackValue((ScopeValue) scvList.get(i)) ); |
| |
| return result; |
| } |
| |
| /** Returns List<MonitorInfo> */ |
| public List getMonitors() { |
| List monitors = getScope().getMonitors(); |
| if (monitors == null) { |
| return new ArrayList(); |
| } |
| List result = new ArrayList(monitors.size()); |
| for (int i = 0; i < monitors.size(); i++) { |
| MonitorValue mv = (MonitorValue) monitors.get(i); |
| ScopeValue ov = mv.owner(); |
| StackValue ownerSV = createStackValue(ov); // it is an oop |
| if (ov.isObject()) { // The owner object was scalar replaced |
| Assert.that(mv.eliminated() && ownerSV.objIsScalarReplaced(), "monitor should be eliminated for scalar replaced object"); |
| // Put klass for scalar replaced object. |
| ScopeValue kv = ((ObjectValue)ov).getKlass(); |
| Assert.that(kv.isConstantOop(), "klass should be oop constant for scalar replaced object"); |
| OopHandle k = ((ConstantOopReadValue)kv).getValue(); |
| result.add(new MonitorInfo(k, resolveMonitorLock(mv.basicLock()), mv.eliminated(), true)); |
| } else { |
| result.add(new MonitorInfo(ownerSV.getObject(), resolveMonitorLock(mv.basicLock()), mv.eliminated(), false)); |
| } |
| } |
| return result; |
| } |
| |
| public int getBCI() { |
| int raw = getRawBCI(); |
| return ((raw == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) ? 0 : raw); |
| } |
| |
| /** Returns SynchronizationEntryBCI or bci() (used for synchronization) */ |
| public int getRawBCI() { |
| if (VM.getVM().isDebugging() && getScope() == null) { |
| return 0; // No debugging information! |
| } |
| return getScope().getBCI(); |
| } |
| |
| /** Returns the sender vframe */ |
| public VFrame sender() { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(isTop(), "just checking"); |
| } |
| return sender(false); |
| } |
| |
| public VFrame sender(boolean mayBeImprecise) { |
| if (!VM.getVM().isDebugging()) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(scope != null, "When new stub generator is in place, then scope can never be NULL"); |
| } |
| } |
| Frame f = (Frame) getFrame().clone(); |
| return (isTop() |
| ? super.sender(false) |
| : new CompiledVFrame(f, getRegisterMap(), getThread(), getScope().sender(), mayBeImprecise)); |
| } |
| |
| private StackValue createStackValue(ScopeValue sv) { |
| // FIXME: this code appears to be out-of-date with respect to the VM especially in 64-bit mode |
| if (sv.isLocation()) { |
| // Stack or register value |
| Location loc = ((LocationValue) sv).getLocation(); |
| |
| if (loc.isIllegal()) return new StackValue(); |
| |
| // First find address of value |
| Address valueAddr = loc.isRegister() |
| // Value was in a callee-save register |
| ? getRegisterMap().getLocation(new VMReg(loc.getRegisterNumber())) |
| // Else value was directly saved on the stack. The frame's original stack pointer, |
| // before any extension by its callee (due to Compiler1 linkage on SPARC), must be used. |
| : ((Address)fr.getUnextendedSP()).addOffsetTo(loc.getStackOffset()); |
| |
| // Then package it right depending on type |
| if (loc.holdsFloat()) { // Holds a float in a double register? |
| // The callee has no clue whether the register holds a float, |
| // double or is unused. He always saves a double. Here we know |
| // a double was saved, but we only want a float back. Narrow the |
| // saved double to the float that the JVM wants. |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that( loc.isRegister(), "floats always saved to stack in 1 word" ); |
| } |
| float value = (float) valueAddr.getJDoubleAt(0); |
| return new StackValue(Float.floatToIntBits(value) & 0xFFFFFFFF); // 64-bit high half is stack junk |
| } else if (loc.holdsInt()) { // Holds an int in a long register? |
| // The callee has no clue whether the register holds an int, |
| // long or is unused. He always saves a long. Here we know |
| // a long was saved, but we only want an int back. Narrow the |
| // saved long to the int that the JVM wants. |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that( loc.isRegister(), "ints always saved to stack in 1 word" ); |
| } |
| return new StackValue(valueAddr.getJLongAt(0) & 0xFFFFFFFF); |
| } else if (loc.holdsNarrowOop()) { // Holds an narrow oop? |
| if (loc.isRegister() && VM.getVM().isBigEndian()) { |
| // The callee has no clue whether the register holds an narrow oop, |
| // long or is unused. He always saves a long. Here we know |
| // a long was saved, but we only want an narrow oop back. Narrow the |
| // saved long to the narrow oop that the JVM wants. |
| return new StackValue(valueAddr.getCompOopHandleAt(VM.getVM().getIntSize()), 0); |
| } else { |
| return new StackValue(valueAddr.getCompOopHandleAt(0), 0); |
| } |
| } else if( loc.holdsOop() ) { // Holds an oop? |
| return new StackValue(valueAddr.getOopHandleAt(0), 0); |
| } else if( loc.holdsDouble() ) { |
| // Double value in a single stack slot |
| return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); |
| } else if(loc.holdsAddr()) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(!VM.getVM().isServerCompiler(), "No address type for locations with C2 (jsr-s are inlined)"); |
| } |
| // FIXME: not yet implemented (no access to safepoint state yet) |
| return new StackValue(0); |
| // intptr_t return_addr_tmp = *(intptr_t *)value_addr; |
| // int bci = -1; |
| // // Get the bci of the jsr that generated this returnAddress value. |
| // // If the destination of a jsr is a block that ends with a return or throw, then |
| // // the GraphBuilder converts the jsr into a direct goto. This has the side effect that |
| // // the return address for the jsr gets emitted as a bci instead of as a real pc |
| // if (code()->contains((address)return_addr_tmp)) { |
| // ScopeDesc* scope = code()->scope_desc_at((address)(return_addr_tmp - jsr_call_offset), false); |
| // bci = scope->bci(); |
| // } else { |
| // bci = (int)return_addr_tmp; |
| // } |
| // // no need to lock method as this happens at Safepoint |
| // assert (SafepointSynchronize::is_at_safepoint(), "must be at safepoint, otherwise lock method()"); |
| // // make sure bci points to jsr |
| // Bytecode* bytecode = Bytecode_at(method()->bcp_from(bci)); |
| // Bytecodes::Code bc = bytecode->code(); |
| // assert (bc == Bytecodes::_jsr || bc == Bytecodes::_jsr_w, "must be jsr"); |
| // |
| // // the real returnAddress is the bytecode following the jsr |
| // return new StackValue((intptr_t)(bci + Bytecodes::length_for(bc))); |
| } else if (VM.getVM().isLP64() && loc.holdsLong()) { |
| if ( loc.isRegister() ) { |
| // Long value in two registers, high half in the first, low in the second |
| return new StackValue(((valueAddr.getJLongAt(0) & 0xFFFFFFFF) << 32) | |
| ((valueAddr.getJLongAt(8) & 0xFFFFFFFF))); |
| } else { |
| // Long value in a single stack slot |
| return new StackValue(valueAddr.getJLongAt(0)); |
| } |
| } else if( loc.isRegister() ) { |
| // At the moment, all non-oop values in registers are 4 bytes, |
| // including double and long halves (see Compile::FillLocArray() in |
| // opto/output.cpp). Haul them out as such and return a StackValue |
| // containing an image of the value as it appears in a stack slot. |
| // If this is a double or long half, the interpreter _must_ deal |
| // with doubles and longs as entities split across two stack slots. |
| // To change this so doubles and/or longs can live in one stack slot, |
| // a StackValue will have to understand that it can contain an |
| // undivided double or long, implying that a Location (and the debug |
| // info mechanism) and FillLocArray() will also have to understand it. |
| return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); |
| } else { |
| return new StackValue(valueAddr.getJIntAt(0) & 0xFFFFFFFF); |
| } |
| } else if (sv.isConstantInt()) { |
| // Constant int: treat same as register int. |
| return new StackValue(((ConstantIntValue) sv).getValue() & 0xFFFFFFFF); |
| } else if (sv.isConstantOop()) { |
| // constant oop |
| return new StackValue(((ConstantOopReadValue) sv).getValue(), 0); |
| } else if (sv.isConstantDouble()) { |
| // Constant double in a single stack slot |
| double d = ((ConstantDoubleValue) sv).getValue(); |
| return new StackValue(Double.doubleToLongBits(d) & 0xFFFFFFFF); |
| } else if (VM.getVM().isLP64() && sv.isConstantLong()) { |
| // Constant long in a single stack slot |
| return new StackValue(((ConstantLongValue) sv).getValue() & 0xFFFFFFFF); |
| } else if (sv.isObject()) { |
| // Scalar replaced object in compiled frame |
| return new StackValue(((ObjectValue)sv).getValue(), 1); |
| } |
| |
| // Unknown ScopeValue type |
| Assert.that(false, "Should not reach here"); |
| return new StackValue(0); // dummy |
| } |
| |
| private BasicLock resolveMonitorLock(Location location) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(location.isStack(), "for now we only look at the stack"); |
| } |
| int byteOffset = location.getStackOffset(); |
| // (stack picture) |
| // high: [ ] byte_offset + wordSize |
| // low [ ] byte_offset |
| // |
| // sp-> [ ] 0 |
| // the byte_offset is the distance from the stack pointer to the lowest address |
| // The frame's original stack pointer, before any extension by its callee |
| // (due to Compiler1 linkage on SPARC), must be used. |
| return new BasicLock(getFrame().getUnextendedSP().addOffsetTo(byteOffset)); |
| } |
| } |