/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.harness.lang.runtime; import static org.mmtk.harness.lang.Trace.Item.ROOTS; import static org.vmmagic.unboxed.harness.MemoryConstants.BYTES_IN_WORD; import static org.vmmagic.unboxed.harness.MemoryConstants.LOG_BYTES_IN_WORD; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.mmtk.harness.lang.Declaration; import org.mmtk.harness.lang.Env; import org.mmtk.harness.lang.Trace; import org.mmtk.harness.lang.Trace.Item; import org.mmtk.harness.lang.compiler.CompiledMethod; import org.mmtk.harness.lang.pcode.PseudoOp; import org.mmtk.harness.lang.type.Type; import org.mmtk.harness.sanity.Sanity; import org.mmtk.harness.vm.ObjectModel; import org.mmtk.harness.vm.ReferenceProcessor; import org.mmtk.plan.TraceLocal; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.harness.Clock; /** * A stack frame. Currently assumes each slot contains exactly one * variable, and that all variables are live all the time. */ public class StackFrame { /** * Enable the assertion that objects won't move after being traced. * This is notably not true for MC, but can be useful for debugging other * collectors. */ public static final boolean ASSERT_WILL_NOT_MOVE = false; /** A sentinel for slots that have no value */ public static final int NO_SUCH_SLOT = Integer.MAX_VALUE; /** The method executed by this stack frame */ private final CompiledMethod method; /** Size in words of the stack frame */ private final int size; /** The values of variables and temporaries */ private final Value[] values; /** The saved program counter during a method call */ private int savedPc; /** The saved instruction array for a method call */ private PseudoOp[] savedCode; /** The slot for the return value of a method call */ private int resultSlot = NO_SUCH_SLOT; /** The base heap address of the stack frame */ private final Address base; /** The address of this thread's environment */ private final Env env; /** * Create a stack frame, given a list of declarations and a quantity of temporaries * * @param env The current execution environment * @param method The method being executed in this frame * @param base The base address of the shadow stack in the heap */ public StackFrame(Env env, CompiledMethod method, Address base) { this.method = method; this.size = method.frameSize(); this.values = new Value[size]; this.base = base; this.env = env; for (Declaration d : method.getDecls()) { setInternal(d.getSlot(),d.getInitial(), true); } } private Address slotAddress(int slot) { return base.plus(slot << LOG_BYTES_IN_WORD); } /** * Return the variable at the given slot in the current stack frame * @param slot The stack frame slot * @return The value in the slot */ public Value get(int slot) { if (slot >= 0) { Value value = getInternal(slot); if (value == null) { Clock.stop(); Trace.printf("Error: getInternal(%d) = null, variable=%s",slot,method.getSlotName(slot)); throw new Error(); } if (Trace.isEnabled(Item.EVAL) || method.isWatched(method.getSlotName(slot))) { Clock.stop(); Trace.printf(Item.EVAL, "stack get: (slot %d) %s %s = %s%n", slot, value.type().toString(), method.getSlotName(slot), value); Clock.start(); } return value; } return ConstantPool.get(slot); } /** * Read a value from a stack frame slot. If the slot contains an object * reference, this may involve reading the shadow stack and updating * the 'cooked' copy from it. * @param slot * @return */ private Value getInternal(int slot) { Value value = values[slot]; if (value instanceof ObjectValue) { ObjectReference shadowValue = env.getReferenceStackSlot(slotAddress(slot)); if (!shadowValue.equals(value.getObjectValue())) { Clock.stop(); Trace.trace(ROOTS, "Updating %s from shadow stack, slot=%s, stack=%s, shadow=%s", method.getSlotName(slot), slotAddress(slot), value, shadowValue); Clock.start(); value = values[slot] = new ObjectValue(shadowValue); } } return value; } /** * Return the type of the variable at the given slot. * @param slot The stack frame slot * @return The type of the value in the slot */ public Type getType(int slot) { return values[slot].type(); } private void setShadowStack(int slot, Value value) { env.setStackSlot(slotAddress(slot), value.getObjectValue()); } /** * Assign a new value to the given slot * @param slot Stack-frame slot to modify * @param value New value */ public void set(int slot, Value value) { assert value != null : "Unexpected null value"; setInternal(slot, value, false); } private void setInternal(int slot, Value value, boolean isInitial) { if (Trace.isEnabled(Item.EVAL) || method.isWatched(method.getSlotName(slot))) { Clock.stop(); Trace.printf(Item.EVAL, "stack set: (slot %d) %s %s : %s->%s%s%n", slot, value.type().toString(), method.getSlotName(slot), getInternal(slot), value.toString(), isInitial ? " (I)" : ""); Clock.start(); } values[slot] = value; if (value instanceof ObjectValue) { setShadowStack(slot, value); } } /** * GC support: trace this stack frame. * @param trace The MMTk trace object to receive the roots * @return The number of roots found */ public int computeRoots(TraceLocal trace) { Clock.stop(); Trace.trace(Item.ROOTS, "--- Computing roots, stack frame %s (%s) ---", method.getName(),base); Clock.start(); int rootCount = 0; for (Address root : getRootAddresses()) { ObjectReference obj = root.loadObjectReference(); if (!obj.isNull()) { Clock.stop(); Sanity.assertValid(obj); if (Trace.isEnabled(Item.ROOTS)) { Trace.trace(Item.ROOTS, "Tracing root %s->%s", root, ObjectModel.getString(obj)); } Clock.start(); trace.reportDelayedRootEdge(root); rootCount++; } } Clock.stop(); Trace.trace(Item.REFERENCES, "Discovering references"); Clock.start(); for (ReferenceValue reference : getReferences()) { ReferenceProcessor.discover(reference); } return rootCount; } /** * * @return The root ObjectValues for this stack frame */ public Collection<ObjectValue> getRoots() { List<ObjectValue> roots = new ArrayList<ObjectValue>(); for (Value value : values) { if (value != null && value instanceof ObjectValue) { roots.add((ObjectValue)value); } } return roots; } public List<Address> getRootAddresses() { List<Address> roots = new ArrayList<Address>(); Set<Integer> gcMap = getGcMap(); for (int i = 0; i < values.length; i++) { Value value = values[i]; if (value != null && value instanceof ObjectValue) { Clock.stop(); if (!gcMap.contains(i)) { Trace.trace(Item.ROOTS, "Slot %d not in GCmap, name=%s", i, method.getSlotName(i)); } Trace.trace(Item.ROOTS, "Root %s (%s->%s)", method.getSlotName(i), slotAddress(i), slotAddress(i).loadObjectReference()); Clock.start(); roots.add(slotAddress(i)); } } for (int root : gcMap) { Value value = values[root]; Clock.stop(); if (value == null || !(value instanceof ObjectValue)) { Trace.trace(Item.ROOTS, "Slot %d in GCmap, but would not be scanned, name=%s", root, method.getSlotName(root)); } Clock.start(); } return roots; } /** * * @return The root ReferenceValues for this stack frame */ private Collection<ReferenceValue> getReferences() { List<ReferenceValue> roots = new ArrayList<ReferenceValue>(); for (Value value : values) { if (value != null && value instanceof ReferenceValue) { roots.add((ReferenceValue)value); } } return roots; } /** * Debug printing support: dump this stack frame and return roots. * @param width Output field width * @return The collection of roots in this frame */ public Collection<ObjectReference> dumpRoots(int width) { List<ObjectReference> roots = new ArrayList<ObjectReference>(); for (int i = 0; i < values.length; i++) { Value value = get(i); String name = method.getSlotName(i); if (value != null && value instanceof ObjectValue) { ObjectReference ref = ((ObjectValue)value).getObjectValue(); System.err.printf(" %s=%s", name, ObjectModel.formatObject(width, ref)); if (!ref.isNull()) roots.add(ref); } } return roots; } /** * Save a program counter value * @param pc The program counter */ public void savePc(int pc) { savedPc = pc; } /** @return the saved program counter */ public int getSavedPc() { return savedPc; } /** * Save a method code array * @param code The code array */ public void saveMethod(PseudoOp[] code) { savedCode = code; } /** * @return the saved code array */ public PseudoOp[] getSavedMethod() { return savedCode; } /** * Set the slot for the return value * @param slot The slot in which to store the return value */ public void setResultSlot(int slot) { resultSlot = slot; } /** * Clear the return value slot */ public void clearResultSlot() { resultSlot = NO_SUCH_SLOT; } /** * Set the return value * @param returnValue The procedure return value */ public void setResult(Value returnValue) { assert resultSlot != NO_SUCH_SLOT : "Attempt to return a value to a method call with no result slot"; set(resultSlot,returnValue); } /** * @return The size in bytes of this stack frame */ public int sizeInBytes() { return size * BYTES_IN_WORD; } public void prepare() { for (int root : getGcMap()) { Value value = values[root]; setShadowStack(root,value); } } public void release() { for (int root : getGcMap()) { Trace.trace(Item.ROOTS, "Releasing stack slot %s", slotAddress(root)); getInternal(root); } } /** * @return The set of (variable,object-id) pairs */ @SuppressWarnings("unused") private Map<String,Integer> objectShadowMap() { Map<String,Integer> result = new HashMap<String,Integer>(); for (int i = 0; i < size; i++) { if (values[i] != null && values[i] instanceof ObjectValue) { ObjectReference obj = slotAddress(i).loadObjectReference(); result.put(method.getSlotName(i), obj.isNull() ? 0 : ObjectModel.getId(obj)); } } return result; } /** * @return The set of (variable,object-id) pairs */ @SuppressWarnings("unused") private Map<String,Integer> objectMap() { Map<String,Integer> result = new HashMap<String,Integer>(); for (int i = 0; i < size; i++) { if (values[i] != null && values[i] instanceof ObjectValue) { ObjectReference obj = values[i].getObjectValue(); result.put(method.getSlotName(i), obj.isNull() ? 0 : ObjectModel.getId(obj)); } } return result; } /** * get the currently executing pseudo-op */ private PseudoOp getCurrentInstr() { return savedCode[savedPc - 1]; } private Set<Integer> getGcMap() { return getCurrentInstr().getGcMap(); } }