/* * 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.jikesrvm.objectmodel; import static org.jikesrvm.mm.mminterface.MemoryManagerConstants.GC_HEADER_BITS; import static org.jikesrvm.mm.mminterface.MemoryManagerConstants.MOVES_OBJECTS; import static org.jikesrvm.objectmodel.JavaHeaderConstants.ADDRESS_BASED_HASHING; import static org.jikesrvm.objectmodel.JavaHeaderConstants.ALIGNMENT_MASK; import static org.jikesrvm.objectmodel.JavaHeaderConstants.ARRAY_LENGTH_BYTES; import static org.jikesrvm.objectmodel.JavaHeaderConstants.DYNAMIC_HASH_OFFSET; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASHCODE_BYTES; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASHCODE_OFFSET; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASH_STATE_HASHED; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASH_STATE_HASHED_AND_MOVED; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASH_STATE_MASK; import static org.jikesrvm.objectmodel.JavaHeaderConstants.HASH_STATE_UNHASHED; import static org.jikesrvm.objectmodel.JavaHeaderConstants.JAVA_HEADER_BYTES; import static org.jikesrvm.objectmodel.JavaHeaderConstants.JAVA_HEADER_OFFSET; import static org.jikesrvm.objectmodel.JavaHeaderConstants.NUM_AVAILABLE_BITS; import static org.jikesrvm.objectmodel.JavaHeaderConstants.OTHER_HEADER_BYTES; import static org.jikesrvm.objectmodel.JavaHeaderConstants.STATUS_BYTES; import static org.jikesrvm.objectmodel.MiscHeader.REQUESTED_BITS; import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_INT; import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS; import org.jikesrvm.VM; import org.jikesrvm.classloader.RVMArray; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.RVMType; import org.jikesrvm.runtime.Magic; import org.jikesrvm.runtime.Memory; import org.jikesrvm.scheduler.Lock; import org.jikesrvm.scheduler.RVMThread; import org.jikesrvm.scheduler.ThinLock; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Interruptible; import org.vmmagic.pragma.NoInline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.pragma.Unpreemptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.Offset; import org.vmmagic.unboxed.Word; /** * Defines the JavaHeader portion of the object header for the * default JikesRVM object model. * The default object model uses a two word header. <p> * * One word holds a TIB pointer. <p> * * The other word ("status word") contains an inline thin lock, * either the hash code or hash code state, and a few unallocated * bits that can be used for other purposes. * If {@link JavaHeaderConstants#ADDRESS_BASED_HASHING} is false, * then to implement default hashcodes, Jikes RVM uses a 10 bit hash code * that is completely stored in the status word, which is laid out as * shown below: * <pre> * TTTT TTTT TTTT TTTT TTTT HHHH HHHH HHAA * T = thin lock bits * H = hash code * A = available for use by GCHeader and/or MiscHeader. * </pre> * * If {@link JavaHeaderConstants#ADDRESS_BASED_HASHING ADDRESS_BASED_HASHING} is true, * then Jikes RVM uses two bits of the status word to record the hash code state in * a typical three state scheme ({@link JavaHeaderConstants#HASH_STATE_UNHASHED}, * {@link JavaHeaderConstants#HASH_STATE_HASHED}, and * {@link JavaHeaderConstants#HASH_STATE_HASHED_AND_MOVED}). In this case, the status * word is laid out as shown below: * <pre> * TTTT TTTT TTTT TTTT TTTT TTHH AAAA AAAA * T = thin lock bits * H = hash code state bits * A = available for use by GCHeader and/or MiscHeader. * </pre> */ @Uninterruptible public class JavaHeader { protected static final int SCALAR_HEADER_SIZE = JAVA_HEADER_BYTES + OTHER_HEADER_BYTES; protected static final int ARRAY_HEADER_SIZE = SCALAR_HEADER_SIZE + ARRAY_LENGTH_BYTES; /** offset of object reference from the lowest memory word */ public static final int OBJECT_REF_OFFSET = ARRAY_HEADER_SIZE; // from start to ref protected static final Offset TIB_OFFSET = JAVA_HEADER_OFFSET; protected static final Offset STATUS_OFFSET = TIB_OFFSET.plus(STATUS_BYTES); protected static final Offset AVAILABLE_BITS_OFFSET = VM.LittleEndian ? (STATUS_OFFSET) : (STATUS_OFFSET.plus(STATUS_BYTES - 1)); /* * Used for 10 bit header hash code in header (!ADDRESS_BASED_HASHING) */ protected static final int HASH_CODE_SHIFT = 2; protected static final Word HASH_CODE_MASK = Word.one().lsh(10).minus(Word.one()).lsh(HASH_CODE_SHIFT); protected static Word hashCodeGenerator; // seed for generating hash codes with copying collectors. /** How many bits are allocated to a thin lock? */ public static final int NUM_THIN_LOCK_BITS = ADDRESS_BASED_HASHING ? 22 : 20; /** How many bits to shift to get the thin lock? */ public static final int THIN_LOCK_SHIFT = ADDRESS_BASED_HASHING ? 10 : 12; /** How many bytes do we have to offset to get to the high locking bits */ public static final int THIN_LOCK_DEDICATED_U16_OFFSET = VM.LittleEndian ? 2 : (VM.BuildFor64Addr ? 4 : 0); /** How many bits do we have to shift to only hold the high locking bits */ public static final int THIN_LOCK_DEDICATED_U16_SHIFT = 16; /** The alignment value **/ public static final int ALIGNMENT_VALUE = JavaHeaderConstants.ALIGNMENT_VALUE; public static final int LOG_MIN_ALIGNMENT = JavaHeaderConstants.LOG_MIN_ALIGNMENT; static { if (VM.VerifyAssertions) { VM._assert(REQUESTED_BITS + GC_HEADER_BITS <= NUM_AVAILABLE_BITS); VM._assert((THIN_LOCK_SHIFT + NUM_THIN_LOCK_BITS - THIN_LOCK_DEDICATED_U16_SHIFT) == 16); } } /** * @return the TIB offset. */ public static Offset getTibOffset() { return TIB_OFFSET; } /** * What is the first word after the class? * * @param obj the object in question * @param type the object's class * @return first word after the scalar object */ public static Address getObjectEndAddress(Object obj, RVMClass type) { int size = type.getInstanceSize(); if (ADDRESS_BASED_HASHING && DYNAMIC_HASH_OFFSET) { Word hashState = Magic.objectAsAddress(obj).loadWord(STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { size += HASHCODE_BYTES; } } return Magic.objectAsAddress(obj).plus(Memory.alignUp(size, BYTES_IN_INT) - OBJECT_REF_OFFSET); } /** * What is the first word after the array? * * @param obj the object in question * @param type the object's class * @param numElements the number of elements in the array * @return the first word after the array */ public static Address getObjectEndAddress(Object obj, RVMArray type, int numElements) { int size = type.getInstanceSize(numElements); if (ADDRESS_BASED_HASHING && DYNAMIC_HASH_OFFSET) { Word hashState = Magic.getWordAtOffset(obj, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { size += HASHCODE_BYTES; } } return Magic.objectAsAddress(obj).plus(Memory.alignUp(size, BYTES_IN_INT) - OBJECT_REF_OFFSET); } /** * What is the offset of the first word of the class? * * @param klass unused * @return offset of the first word of the class from the object * reference */ public static int objectStartOffset(RVMClass klass) { return -OBJECT_REF_OFFSET; } /** * @return offset of the last word of the header from an * out-to-in perspective */ public static int getHeaderEndOffset() { return SCALAR_HEADER_SIZE - OBJECT_REF_OFFSET; } /** * How small is the minimum object header size? * Can be used to pick chunk sizes for allocators. * * @return the minimum object size */ public static int minimumObjectSize() { return SCALAR_HEADER_SIZE; } /** * Given a reference, return an address which is guaranteed to be inside * the memory region allocated to the object. * * @param ref an object reference * @return an address inside the object's memory */ public static Address getPointerInMemoryRegion(ObjectReference ref) { return ref.toAddress().plus(TIB_OFFSET); } /** * @param o an object * @return the TIB for an object. */ public static TIB getTIB(Object o) { return Magic.getTIBAtOffset(o, TIB_OFFSET); } /** * Sets the TIB for an object. * * @param ref the object * @param tib the TIB to set for the object */ public static void setTIB(Object ref, TIB tib) { Magic.setObjectAtOffset(ref, TIB_OFFSET, tib); } /** * Sets the TIB for an object during bootimage writing. * * @param bootImage the bootimage * @param refOffset the object's address in the bootimage * @param tibAddr the TIB's address in the bootimage * @param type the object's type */ @Interruptible public static void setTIB(BootImageInterface bootImage, Address refOffset, Address tibAddr, RVMType type) { bootImage.setAddressWord(refOffset.plus(TIB_OFFSET), tibAddr.toWord(), false, false); } /** * @param fromObj the object to copy * @param type the object's type * @return number of needed bytes when the scalar object is copied by GC */ public static int bytesRequiredWhenCopied(Object fromObj, RVMClass type) { int size = type.getInstanceSize(); if (ADDRESS_BASED_HASHING) { Word hashState = Magic.getWordAtOffset(fromObj, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.NE(HASH_STATE_UNHASHED)) { size += HASHCODE_BYTES; } } return size; } /** * @param obj the object * @param type the object's type * @return number of bytes are used by the scalar object */ public static int bytesUsed(Object obj, RVMClass type) { int size = type.getInstanceSize(); if (MOVES_OBJECTS) { if (ADDRESS_BASED_HASHING) { Word hashState = Magic.getWordAtOffset(obj, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { size += HASHCODE_BYTES; } } } return size; } /** * how many bytes are needed when the array object is copied by GC? * * @param fromObj the object to copy * @param type the object's type * @param numElements the array length * @return the number of bytes that are required for the copy */ public static int bytesRequiredWhenCopied(Object fromObj, RVMArray type, int numElements) { int size = type.getInstanceSize(numElements); if (ADDRESS_BASED_HASHING) { Word hashState = Magic.getWordAtOffset(fromObj, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.NE(HASH_STATE_UNHASHED)) { size += HASHCODE_BYTES; } } return Memory.alignUp(size, BYTES_IN_INT); } /** * how many bytes are used by the array object? * @param obj the object to copy * @param type the object's type * @param numElements the array length * @return the number of bytes that the array uses */ public static int bytesUsed(Object obj, RVMArray type, int numElements) { int size = type.getInstanceSize(numElements); if (MOVES_OBJECTS) { if (ADDRESS_BASED_HASHING) { Word hashState = Magic.getWordAtOffset(obj, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { size += HASHCODE_BYTES; } } } return Memory.alignUp(size, BYTES_IN_INT); } /** * Maps from the object ref to the lowest address of the storage * associated with the object. * * @param obj the object reference * @return the lowest address in the object's memory region */ @Inline public static Address objectStartRef(ObjectReference obj) { if (MOVES_OBJECTS) { if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET) { Word hashState = obj.toAddress().loadWord(STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { return obj.toAddress().minus(OBJECT_REF_OFFSET + HASHCODE_BYTES); } } } return obj.toAddress().minus(OBJECT_REF_OFFSET); } /** * Get an object reference from the address the lowest word of the * object was allocated. In general this required that we are using * a dynamic hash offset or not using address based * hashing. However, the GC algorithm could safely do this in the * nursery so we can't assert DYNAMIC_HASH_OFFSET. * * @param start the lowest word in the storage of an allocated object * @return the object reference for the object */ public static ObjectReference getObjectFromStartAddress(Address start) { /* Skip over any alignment fill */ while ((start.loadInt()) == ALIGNMENT_VALUE) { start = start.plus(BYTES_IN_INT); } return start.plus(OBJECT_REF_OFFSET).toObjectReference(); } /** * Gets an object reference from the address the lowest word of the * object was allocated. * * @param start the lowest word in the storage of an allocated object * @return the object reference for the object */ public static ObjectReference getScalarFromStartAddress(Address start) { return getObjectFromStartAddress(start); } /** * Gets an object reference from the address the lowest word of the * object was allocated. * * @param start the lowest word in the storage of an allocated object * @return the object reference for the object */ public static ObjectReference getArrayFromStartAddress(Address start) { return getObjectFromStartAddress(start); } /** * Get the next object in the heap under contiguous * allocation. Handles alignment issues only when there are no GC or * Misc header words. In the case there are we probably have to ask * MemoryManager to distinguish this for us. * * @param obj the present object * @param size the object's size * @return the next object, provided that the constraints from above are * met */ protected static ObjectReference getNextObject(ObjectReference obj, int size) { if (VM.VerifyAssertions) VM._assert(OTHER_HEADER_BYTES == 0); return getObjectFromStartAddress(obj.toAddress().plus(size).minus(OBJECT_REF_OFFSET)); } /** * Get the next object in the heap under contiguous * allocation. Handles alignment issues. * * @param obj the current object, which must be a scalar * @param type the object's type * @return the next scalar object in the heap */ public static ObjectReference getNextObject(ObjectReference obj, RVMClass type) { return getObjectFromStartAddress(getObjectEndAddress(obj.toObject(), type)); } /** * Get the next array in the heap under contiguous * allocation. Handles alignment issues. * * @param obj the current object, which must be an array * @param type the object's type * @param numElements the length of the array * @return the next scalar object in the heap */ public static ObjectReference getNextObject(ObjectReference obj, RVMArray type, int numElements) { return getObjectFromStartAddress(getObjectEndAddress(obj.toObject(), type, numElements)); } /** * Gets the reference of an array when copied to the specified region. * * @param obj the object to copy * @param to the target address for the copy * @param type the array's type * @return the reference of the copy */ @Inline public static Object getReferenceWhenCopiedTo(Object obj, Address to, RVMArray type) { return getReferenceWhenCopiedTo(obj, to); } /** * Get the reference of a scalar when copied to the specified region. * * @param obj the object to copy * @param to the target address for the copy * @param type the scalar's type * @return the reference of the copy */ @Inline public static Object getReferenceWhenCopiedTo(Object obj, Address to, RVMClass type) { return getReferenceWhenCopiedTo(obj, to); } @Inline protected static Object getReferenceWhenCopiedTo(Object obj, Address to) { if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET) { // Read the hash state (used below) Word statusWord = Magic.getWordAtOffset(obj, STATUS_OFFSET); Word hashState = statusWord.and(HASH_STATE_MASK); if (hashState.NE(HASH_STATE_UNHASHED)) { to = to.plus(HASHCODE_BYTES); } } return Magic.addressAsObject(to.plus(OBJECT_REF_OFFSET)); } /** * Copy a scalar to the given raw storage address. * * @param toAddress the target address * @param fromObj the object to copy * @param numBytes how many bytes to copy * @param type the scalar's type * @return the reference for the object's copy */ @Inline public static Object moveObject(Address toAddress, Object fromObj, int numBytes, RVMClass type) { // We copy arrays and scalars the same way return moveObject(toAddress, fromObj, null, numBytes); } /** * Copies a scalar to the given location. * * @param fromObj the scalar to copy * @param toObj target address for copy * @param numBytes how many bytes to copy * @param type the scalar's type * @return the reference for the object's copy */ @Inline public static Object moveObject(Object fromObj, Object toObj, int numBytes, RVMClass type) { // We copy arrays and scalars the same way return moveObject(Address.zero(), fromObj, toObj, numBytes); } /** * Copies an array to the given raw storage address. * * @param toAddress the target address * @param fromObj the object to copy * @param numBytes how many bytes to copy * @param type the array's type * @return the reference for the object's copy */ @Inline public static Object moveObject(Address toAddress, Object fromObj, int numBytes, RVMArray type) { // We copy arrays and scalars the same way return moveObject(toAddress, fromObj, null, numBytes); } /** * Copies an array to the given location. * * @param fromObj the object to copy * @param toObj the target object * @param numBytes the number of bytes to copy * @param type the array's type * @return the reference for the array's copy */ @Inline public static Object moveObject(Object fromObj, Object toObj, int numBytes, RVMArray type) { // We copy arrays and scalars the same way return moveObject(Address.zero(), fromObj, toObj, numBytes); } /** * Copies an object to the given raw storage address. * * @param toObj the target object. If this is non-{@code null}, the target * address must be {@code Address.zero()}. * @param toAddress the target address. If this is not {@code Address.zero()}, * the target object must be {@code null}. * @param fromObj the object to copy from * @param numBytes the number of bytes to copy * @return the reference of the object's copy */ @Inline public static Object moveObject(Address toAddress, Object fromObj, Object toObj, int numBytes) { if (VM.VerifyAssertions) VM._assert(toAddress.isZero() || toObj == null); // Default values int copyBytes = numBytes; int objRefOffset = OBJECT_REF_OFFSET; Word statusWord = Word.zero(); Word hashState = HASH_STATE_UNHASHED; if (ADDRESS_BASED_HASHING) { // Read the hash state (used below) statusWord = Magic.getWordAtOffset(fromObj, STATUS_OFFSET); hashState = statusWord.and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED)) { // We do not copy the hashcode, but we do allocate it copyBytes -= HASHCODE_BYTES; if (!DYNAMIC_HASH_OFFSET) { // The hashcode is the first word, so we copy to object one word higher if (toObj == null) { toAddress = toAddress.plus(HASHCODE_BYTES); } } } else if (!DYNAMIC_HASH_OFFSET && hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { // Simple operation (no hash state change), but one word larger header objRefOffset += HASHCODE_BYTES; } } if (toObj != null) { toAddress = Magic.objectAsAddress(toObj).minus(objRefOffset); } // Low memory word of source object Address fromAddress = Magic.objectAsAddress(fromObj).minus(objRefOffset); // Do the copy Memory.aligned32Copy(toAddress, fromAddress, copyBytes); if (toObj == null) { toObj = Magic.addressAsObject(toAddress.plus(objRefOffset)); } else { if (VM.VerifyAssertions) VM._assert(toObj == Magic.addressAsObject(toAddress.plus(objRefOffset))); } // Do we need to copy the hash code? if (hashState.EQ(HASH_STATE_HASHED)) { int hashCode = Magic.objectAsAddress(fromObj).toWord().rshl(LOG_BYTES_IN_ADDRESS).toInt(); if (DYNAMIC_HASH_OFFSET) { Magic.setIntAtOffset(toObj, Offset.fromIntSignExtend(numBytes - OBJECT_REF_OFFSET - HASHCODE_BYTES), hashCode); } else { Magic.setIntAtOffset(toObj, HASHCODE_OFFSET, (hashCode << 1) | ALIGNMENT_MASK); } Magic.setWordAtOffset(toObj, STATUS_OFFSET, statusWord.or(HASH_STATE_HASHED_AND_MOVED)); if (ObjectModel.HASH_STATS) ObjectModel.hashTransition2++; } return toObj; } @Inline @Interruptible public static int getObjectHashCode(Object o) { if (ADDRESS_BASED_HASHING) { if (MOVES_OBJECTS) { Word hashState = Magic.getWordAtOffset(o, STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.EQ(HASH_STATE_HASHED)) { // HASHED, NOT MOVED return Magic.objectAsAddress(o).toWord().rshl(LOG_BYTES_IN_ADDRESS).toInt(); } else if (hashState.EQ(HASH_STATE_HASHED_AND_MOVED)) { // HASHED AND MOVED if (DYNAMIC_HASH_OFFSET) { // Read the size of this object. RVMType t = Magic.getObjectType(o); int offset = t.isArrayType() ? t.asArray().getInstanceSize(Magic.getArrayLength(o)) - OBJECT_REF_OFFSET : t.asClass().getInstanceSize() - OBJECT_REF_OFFSET; return Magic.getIntAtOffset(o, Offset.fromIntSignExtend(offset)); } else { return (Magic.getIntAtOffset(o, HASHCODE_OFFSET) >>> 1); } } else { // UNHASHED Word tmp; do { tmp = Magic.prepareWord(o, STATUS_OFFSET); } while (!Magic.attemptWord(o, STATUS_OFFSET, tmp, tmp.or(HASH_STATE_HASHED))); if (ObjectModel.HASH_STATS) ObjectModel.hashTransition1++; return getObjectHashCode(o); } } else { return Magic.objectAsAddress(o).toWord().rshl(LOG_BYTES_IN_ADDRESS).toInt(); } } else { // 10 bit hash code in status word int hashCode = Magic.getWordAtOffset(o, STATUS_OFFSET).and(HASH_CODE_MASK).rshl(HASH_CODE_SHIFT).toInt(); if (hashCode != 0) { return hashCode; } return installHashCode(o); } } @NoInline @Interruptible protected static int installHashCode(Object o) { Word hashCode; do { hashCodeGenerator = hashCodeGenerator.plus(Word.one().lsh(HASH_CODE_SHIFT)); hashCode = hashCodeGenerator.and(HASH_CODE_MASK); } while (hashCode.isZero()); while (true) { Word statusWord = Magic.prepareWord(o, STATUS_OFFSET); if (!(statusWord.and(HASH_CODE_MASK).isZero())) { // some other thread installed a hashcode return statusWord.and(HASH_CODE_MASK).rshl(HASH_CODE_SHIFT).toInt(); } if (Magic.attemptWord(o, STATUS_OFFSET, statusWord, statusWord.or(hashCode))) { // we installed the hash code return hashCode.rshl(HASH_CODE_SHIFT).toInt(); } } } public static Offset getThinLockOffset(Object o) { return STATUS_OFFSET; } public static Offset defaultThinLockOffset() { return STATUS_OFFSET; } /** * Allocates a thin lock word for instances of the type * (if they already have one, then has no effect). * * @param t the type that is supposed to receive a thin * lock word */ public static void allocateThinLock(RVMType t) { // nothing to do (all objects have thin locks in this object model); } @Unpreemptible("Become another thread when lock is contended, don't preempt in other cases") public static void genericLock(Object o) { ThinLock.lock(o, STATUS_OFFSET); } @Unpreemptible("No interruption unless of exceptions") public static void genericUnlock(Object o) { ThinLock.unlock(o, STATUS_OFFSET); } /** * @param obj an object * @param thread a thread * @return <code>true</code> if the lock on obj is currently owned * by thread <code>false</code> if it is not. */ public static boolean holdsLock(Object obj, RVMThread thread) { return ThinLock.holdsLock(obj, STATUS_OFFSET, thread); } /** * Obtains the heavy-weight lock, if there is one, associated with the * indicated object. Returns <code>null</code>, if there is no * heavy-weight lock associated with the object. * * @param o the object from which a lock is desired * @param create if true, create heavy lock if none found * @return the heavy-weight lock on the object (if any) */ @Unpreemptible("May be interrupted for allocations of locks") public static Lock getHeavyLock(Object o, boolean create) { return ThinLock.getHeavyLock(o, STATUS_OFFSET, create); } /** * Non-atomic read of word containing available bits * * @param o the object to read * @return the available bits word */ public static Word readAvailableBitsWord(Object o) { return Magic.getWordAtOffset(o, STATUS_OFFSET); } /** * Non-atomic read of byte containing available bits * @param o the object to read * @return the available bits bytes */ public static byte readAvailableByte(Object o) { return Magic.getByteAtOffset(o, AVAILABLE_BITS_OFFSET); } /** * Non-atomic write of word containing available bits. * * @param o the object whose word will be written * @param val the available bits word */ public static void writeAvailableBitsWord(Object o, Word val) { Magic.setWordAtOffset(o, STATUS_OFFSET, val); } /** * Non-atomic write of word containing available bits * * @param bootImage the bootimage * @param ref an object reference whose word will be written * @param val the available bits word */ @Interruptible public static void writeAvailableByte(BootImageInterface bootImage, Address ref, byte val) { bootImage.setByte(ref.plus(AVAILABLE_BITS_OFFSET), val); } /** * Non-atomic write of byte containing available bits * * @param o the object whose available byte will be written * @param val the value to write to the available byte */ public static void writeAvailableByte(Object o, byte val) { Magic.setByteAtOffset(o, AVAILABLE_BITS_OFFSET, val); } /** * @param o the object whose bit will be tested * @param idx the index in the bits * @return {@code true} if argument bit is 1, {@code false} if it is 0 */ public static boolean testAvailableBit(Object o, int idx) { Word mask = Word.fromIntSignExtend(1 << idx); Word status = Magic.getWordAtOffset(o, STATUS_OFFSET); return mask.and(status).NE(Word.zero()); } /** * Sets argument bit to 1 if value is true, 0 if value is false * * @param o the object whose bit will be set * @param idx the index in the bits * @param flag {@code true} for 1, {@code false} for 0 */ public static void setAvailableBit(Object o, int idx, boolean flag) { Word status = Magic.getWordAtOffset(o, STATUS_OFFSET); if (flag) { Word mask = Word.fromIntSignExtend(1 << idx); Magic.setWordAtOffset(o, STATUS_OFFSET, status.or(mask)); } else { Word mask = Word.fromIntSignExtend(1 << idx).not(); Magic.setWordAtOffset(o, STATUS_OFFSET, status.and(mask)); } } /** * Freezes the other bits in the byte containing the available bits * so that it is safe to update them using setAvailableBits. * * @param o the object whose available bytes will be initialized */ @Interruptible public static void initializeAvailableByte(Object o) { if (!ADDRESS_BASED_HASHING) getObjectHashCode(o); } /** * A prepare on the word containing the available bits. * <p> * Note: this method is intended to be used in conjunction * with the attempt method. * * @param o the object which has the available bits * @return the current value of the word * @see #attemptAvailableBits(Object, Word, Word) */ public static Word prepareAvailableBits(Object o) { return Magic.prepareWord(o, STATUS_OFFSET); } /** * An attempt on the word containing the available bits. * <p> * Note: this method is intended to be used in conjunction * with the prepare method. If the method returns {@code false}, * callers must update their information about the old value of * the available bits word before retrying again. * * @param o the object which has the available bits * @param oldVal the old value that the word is expected to have * @param newVal the new value that will be written, if possible * @return whether the write occurred */ public static boolean attemptAvailableBits(Object o, Word oldVal, Word newVal) { return Magic.attemptWord(o, STATUS_OFFSET, oldVal, newVal); } /** * Given the smallest base address in a region, return the smallest * object reference that could refer to an object in the region. * * @param regionBaseAddr the smallest base address in the region * @return the smallest address in the region that could possibly * refer to an object in the region */ public static Address minimumObjectRef(Address regionBaseAddr) { return regionBaseAddr.plus(OBJECT_REF_OFFSET); } /** * Given the largest base address in a region, return the largest * object reference that could refer to an object in the region. * * @param regionHighAddr the highest base address in the region * @return the largest address in the region that could possibly * refer to an object in the region */ public static Address maximumObjectRef(Address regionHighAddr) { return regionHighAddr.plus(OBJECT_REF_OFFSET - SCALAR_HEADER_SIZE); } /** * Computes the header size of an instance of the given type. * * @param type the instance's type * @return size of the head in bytes */ public static int computeScalarHeaderSize(RVMClass type) { return SCALAR_HEADER_SIZE; } /** * Computes the header size of an instance of the given type. * * @param type the instance's type * @return size of the head in bytes */ public static int computeArrayHeaderSize(RVMArray type) { return ARRAY_HEADER_SIZE; } /** * @param t RVMClass instance being created * @return the desired alignment of the alignment point returned by * getOffsetForAlignment in instances of the argument RVMClass. */ public static int getAlignment(RVMClass t) { return t.getAlignment(); } /** * @param t RVMClass instance being copied * @param obj the object being copied * @return the desired alignment of the alignment point returned by * getOffsetForAlignment in instances of the argument RVMClass. */ public static int getAlignment(RVMClass t, Object obj) { return t.getAlignment(); } /** * @param t RVMArray instance being created * @return the desired alignment of the alignment point returned by * getOffsetForAlignment in instances of the argument RVMArray. */ public static int getAlignment(RVMArray t) { return t.getAlignment(); } /** * @param t RVMArray instance being copied * @param obj the object being copied * @return the desired alignment of the alignment point returned by * getOffsetForAlignment in instances of the argument RVMArray. */ public static int getAlignment(RVMArray t, Object obj) { return t.getAlignment(); } /** * @param t RVMClass instance being created * @param needsIdentityHash TODO document this parameter. Is it still needed? * It's never set to true. * @return the offset relative to physical beginning of object * that must be aligned. */ public static int getOffsetForAlignment(RVMClass t, boolean needsIdentityHash) { /* Align the first field - note that this is one word off from the reference. */ if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET && needsIdentityHash) { return SCALAR_HEADER_SIZE + HASHCODE_BYTES; } return SCALAR_HEADER_SIZE; } /** * @param t RVMClass instance being copied * @param obj the object being copied * @return the offset relative to physical beginning of object * that must be aligned. */ public static int getOffsetForAlignment(RVMClass t, ObjectReference obj) { if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET) { Word hashState = obj.toAddress().loadWord(STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.NE(HASH_STATE_UNHASHED)) { return SCALAR_HEADER_SIZE + HASHCODE_BYTES; } } return SCALAR_HEADER_SIZE; } /** * @param t RVMArray instance being created * @param needsIdentityHash TODO document this parameter. Is it still needed? * It's never set to true. * @return the offset relative to physical beginning of object that must * be aligned. */ public static int getOffsetForAlignment(RVMArray t, boolean needsIdentityHash) { /* although array_header_size == object_ref_offset we say this because the whole point is to align the object ref */ if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET && needsIdentityHash) { return OBJECT_REF_OFFSET + HASHCODE_BYTES; } return OBJECT_REF_OFFSET; } /** * @param t RVMArray instance being copied * @param obj the object being copied * @return the offset relative to physical beginning of object that must * be aligned. */ public static int getOffsetForAlignment(RVMArray t, ObjectReference obj) { /* although array_header_size == object_ref_offset we say this because the whole point is to align the object ref */ if (ADDRESS_BASED_HASHING && !DYNAMIC_HASH_OFFSET) { Word hashState = obj.toAddress().loadWord(STATUS_OFFSET).and(HASH_STATE_MASK); if (hashState.NE(HASH_STATE_UNHASHED)) { return OBJECT_REF_OFFSET + HASHCODE_BYTES; } } return OBJECT_REF_OFFSET; } /** * Perform any required initialization of the JAVA portion of the header. * @param ptr the raw storage to be initialized * @param tib the TIB of the instance being created * @param size the number of bytes allocated by the GC system for this object. * @return the object whose header was initialized */ public static Object initializeScalarHeader(Address ptr, TIB tib, int size) { // (TIB set by ObjectModel) Object ref = Magic.addressAsObject(ptr.plus(OBJECT_REF_OFFSET)); return ref; } /** * Perform any required initialization of the JAVA portion of the header. * @param bootImage The bootimage being written * @param ptr The object ref to the storage to be initialized * @param tib The TIB of the instance being created * @param size The number of bytes allocated by the GC system for this object. * @param needsIdentityHash needs an identity hash value * @param identityHashValue the value for the identity hash * @return the address used for a reference to this object */ @Interruptible public static Address initializeScalarHeader(BootImageInterface bootImage, Address ptr, TIB tib, int size, boolean needsIdentityHash, int identityHashValue) { Address ref = ptr.plus(OBJECT_REF_OFFSET); if (needsIdentityHash) { bootImage.setFullWord(ref.plus(STATUS_OFFSET), HASH_STATE_HASHED_AND_MOVED.toInt()); if (DYNAMIC_HASH_OFFSET) { // Read the size of this object. RVMType t = tib.getType(); bootImage.setFullWord(ptr.plus(t.asClass().getInstanceSize()), identityHashValue); } else { ref = ref.plus(HASHCODE_BYTES); bootImage.setFullWord(ref.plus(HASHCODE_OFFSET), (identityHashValue << 1) | ALIGNMENT_MASK); } } else { // As boot image objects can't move there is no benefit in lazily setting them to hashed bootImage.setFullWord(ref.plus(STATUS_OFFSET), HASH_STATE_HASHED.toInt()); } return ref; } /** * Perform any required initialization of the JAVA portion of the header. * @param ptr the raw storage to be initialized * @param tib the TIB of the instance being created * @param size the number of bytes allocated by the GC system for this object. * @return the array whose header was initialized */ public static Object initializeArrayHeader(Address ptr, TIB tib, int size) { Object ref = Magic.addressAsObject(ptr.plus(OBJECT_REF_OFFSET)); // (TIB and array length set by ObjectModel) return ref; } /** * Perform any required initialization of the JAVA portion of the header. * * @param bootImage the bootimage being written * @param ptr the object ref to the storage to be initialized * @param tib the TIB of the instance being created * @param size the number of bytes allocated by the GC system for this object. * @param numElements the number of elements in the array * @param needsIdentityHash needs an identity hash value * @param identityHashValue the value for the identity hash * @return the address used for a reference to this object */ @Interruptible public static Address initializeArrayHeader(BootImageInterface bootImage, Address ptr, TIB tib, int size, int numElements, boolean needsIdentityHash, int identityHashValue) { Address ref = ptr.plus(OBJECT_REF_OFFSET); // (TIB set by BootImageWriter; array length set by ObjectModel) if (needsIdentityHash) { bootImage.setFullWord(ref.plus(STATUS_OFFSET), HASH_STATE_HASHED_AND_MOVED.toInt()); if (DYNAMIC_HASH_OFFSET) { // Read the size of this object. RVMType t = tib.getType(); bootImage.setFullWord(ptr.plus(t.asArray().getInstanceSize(numElements)), identityHashValue); } else { ref = ref.plus(HASHCODE_BYTES); bootImage.setFullWord(ref.plus(HASHCODE_OFFSET), (identityHashValue << 1) | ALIGNMENT_MASK); } } else { // As boot image objects can't move there is no benefit in lazily setting them to hashed bootImage.setFullWord(ref.plus(STATUS_OFFSET), HASH_STATE_HASHED.toInt()); } return ref; } /** * For low level debugging of GC subsystem. * Dump the header word(s) of the given object reference. * @param ref the object reference whose header should be dumped */ public static void dumpHeader(Object ref) { // TIB dumped in ObjectModel VM.sysWrite(" STATUS="); VM.sysWriteHex(Magic.getWordAtOffset(ref, STATUS_OFFSET).toAddress()); } }