/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.runtime;
import org.jikesrvm.SubordinateArchitecture;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.ArchitectureSpecific.VM_CodeArray;
import org.jikesrvm.cellspu.VM_SubArchBootRecord;
import org.jikesrvm.classloader.VM_Type;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.memorymanagers.mminterface.MM_Constants;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.util.VM_HashMap;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.UninterruptibleNoWarn;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.AddressArray;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* This class is an implementation of VM_Statics for the subordinate architecture.
* It is laid out in the same manner, however, contains only constants required
* by the methods compiled for the subarch. Static fields and methods are kept seperatly.
*/
public class VM_SubArchStatics implements VM_Constants {
/**
* Static data values (pointed to by subarch's jtoc register).
* This is currently fixed-size, although at one point the system's plans
* called for making it dynamically growable. We could also make it
* non-contiguous.
*/
public static final int NO_SLOTS = 0x400; // 2kB (512 entries)
public static final int SIZE_OF_TABLE = NO_SLOTS << LOG_BYTES_IN_INT;
private static final int[] slots = new int[NO_SLOTS];
/**
* Object version of the slots used during boot image creation and
* destroyed shortly after. This is required to support conversion
* of a slot address to its associated object during boot image
* creation.
*/
private static Object[] objectSlots = new Object[0x400];
/** Pointers to per-class numeric static blocks */
private static final AddressArray staticsTOC = AddressArray.create(0x400);
/** Pointers to per-class reference static blocks */
private static final AddressArray methodTIB = AddressArray.create(0x200);
/** Each slot holds the size of the num,ref and method TOCs */
private static final int[] sizeStaticsTable = new int [0x200];
private static int currTOCidx = 0;
/**
* The middle of the table, references are slots above this and
* numeric values below this. The JTOC points to the middle of the
* table.
*/
public static final int middleOfTable = slots.length / 2;
/** Next available numeric slot number */
private static int nextNumericSlot = middleOfTable - 1;
/**
* Numeric slot hole. Holes are created to align 8byte values. We
* allocate into a hole rather than consume another numeric slot.
*/
private static int numericSlotHole = middleOfTable;
/** Next available reference slot number */
private static int nextReferenceSlot = middleOfTable + 1;
public static Offset mainStaticTocAddrOff = Offset.fromIntZeroExtend(0xdeadbeef);
public static Offset mainJTOCAddrOff = Offset.fromIntZeroExtend(0xdeadbeef);
/**
* Mapping from int like literals (ints and floats) to the jtoc slot
* that contains them.
*/
private static final VM_HashMap<Integer, Integer> intSizeLiterals = new VM_HashMap<Integer, Integer>();
/**
* Mapping from long like literals (longs and doubles) to the jtoc
* slot that contains them.
*/
private static final VM_HashMap<Long, Integer> longSizeLiterals = new VM_HashMap<Long, Integer>();
/**
* Mapping from object literals to the jtoc slot that contains them.
*/
private static final VM_HashMap<Object, Integer> objectLiterals = new VM_HashMap<Object, Integer>();
/** Initialize some static values which are always available to the subarch */
public static void init() {
// Stick this in a numeric slot to save messing up things (e.g. in bootImageWriter and GC)
mainJTOCAddrOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT);
VM_SubArchStatics.setSlotContents(mainJTOCAddrOff, VM_Magic.getTocPointer().toInt());
mainStaticTocAddrOff = VM_SubArchStatics.allocateReferenceSlot();
VM_SubArchStatics.setSlotContents(mainStaticTocAddrOff, staticsTOC);
// set all Class TOC pointers to Address.max to signal that they are invalid at present
if (VM.VerifyAssertions) VM._assert(currTOCidx == 0);
for (int i=0; i<methodTIB.length(); i++) {
methodTIB.set(i, Address.max());
}
}
/**
* Conversion from JTOC slot index to JTOC offset.
*/
@Uninterruptible
public static Offset slotAsOffset(int slot) {
return Offset.fromIntSignExtend((slot - middleOfTable) << LOG_BYTES_IN_INT);
}
/**
* Conversion from JTOC offset to JTOC slot index.
*/
@Uninterruptible
public static int offsetAsSlot(Offset offset) {
if (VM.VerifyAssertions) VM._assert((offset.toInt() & 3) == 0);
return middleOfTable + (offset.toInt() >> LOG_BYTES_IN_INT);
}
/**
* Return the lowest slot number in use
*/
public static int getLowestInUseSlot() {
return nextNumericSlot + 1;
}
/**
* Return the highest slot number in use
*/
public static int getHighestInUseSlot() {
return nextReferenceSlot - (VM.BuildFor32Addr ? 1 : 2);
}
/**
* Find the given literal in the int like literal map, if not found
* create a slot for the literal and place an entry in the map
* @param literal the literal value to find or create
* @return the offset in the JTOC of the literal
*/
public static int findOrCreateIntSizeLiteral(int literal) {
Integer offsetAsInt;
synchronized (intSizeLiterals) {
offsetAsInt = intSizeLiterals.get(literal);
if (offsetAsInt != null) {
return offsetAsInt;
} else {
Offset newOff = allocateNumericSlot(BYTES_IN_INT);
intSizeLiterals.put(literal, newOff.toInt());
setSlotContents(newOff, literal);
VM_SubArchBootRecord.updateJtocNumeric();
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
return newOff.toInt();
}
}
}
/**
* Find the given literal in the long like literal map, if not found
* create a slot for the literal and place an entry in the map
* @param literal the literal value to find or create
* @return the offset in the JTOC of the literal
*/
public static int findOrCreateLongSizeLiteral(long literal) {
Integer offsetAsInt;
synchronized (longSizeLiterals) {
offsetAsInt = longSizeLiterals.get(literal);
if (offsetAsInt != null) {
return offsetAsInt;
} else {
Offset newOff = allocateNumericSlot(BYTES_IN_LONG);
longSizeLiterals.put(literal, newOff.toInt());
setSlotContents(newOff, literal);
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
return newOff.toInt();
}
}
}
/**
* Find or allocate a slot in the jtoc for a class literal
* @param typeReferenceID the type reference ID for the class
* @return the offset of slot that was allocated
*/
public static int findOrCreateClassLiteral(int typeReferenceID) {
Class<?> literalAsClass = VM_TypeReference.getTypeRef(typeReferenceID).resolve(true).getClassForType();
Integer offAsInt;
synchronized (objectLiterals) {
offAsInt = objectLiterals.get(literalAsClass);
if (offAsInt != null) {
return offAsInt;
} else {
Offset newOff = allocateReferenceSlot();
objectLiterals.put(literalAsClass, newOff.toInt());
setSlotContents(newOff, literalAsClass);
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
return newOff.toInt();
}
}
}
/**
* Find or allocate a slot in the jtoc for an object literal.
* @param literal value
* @return offset of slot that was allocated
* Side effect: literal value is stored into jtoc
*/
public static int findOrCreateObjectLiteral(Object literal) {
Integer offAsInt;
synchronized (objectLiterals) {
offAsInt = objectLiterals.get(literal);
if (offAsInt != null) {
return offAsInt;
} else {
Offset newOff = allocateReferenceSlot();
objectLiterals.put(literal, newOff.toInt());
setSlotContents(newOff, literal);
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
return newOff.toInt();
}
}
}
/**
* Find a slot in the jtoc with this object literal in else return 0
* @param literal value
* @return offset containing literal or 0
*/
public static int findObjectLiteral(Object literal) {
Integer offAsInt;
synchronized (objectLiterals) {
offAsInt = objectLiterals.get(literal);
}
if (offAsInt != null) {
return offAsInt;
} else {
return 0;
}
}
/**
* Allocate a numeric slot in the jtoc.
* @param size of slot
* @return offset of slot that was allocated as int
* (two slots are allocated for longs and doubles)
*/
public static synchronized Offset allocateNumericSlot(int size) {
// Result slot
int slot;
// Allocate two slots for wide items after possibly blowing
// another slot for alignment. Wide things are longs or doubles
if (size == BYTES_IN_LONG) {
// widen for a wide
nextNumericSlot--;
// check alignment
if ((nextNumericSlot & 1) != 0) {
// slot isn't 8byte aligned so increase by 1 and record hole
nextNumericSlot--;
numericSlotHole = nextNumericSlot + 2;
}
// Remember the slot and adjust the next available slot
slot = nextNumericSlot;
nextNumericSlot--;
VM_SubArchBootRecord.updateJtocNumeric();
} else {
// 4byte quantity, try to reuse hole if one is available
if ((numericSlotHole != middleOfTable) && (VM_SubArchBootRecord.verifyUpdate(slotAsOffset(numericSlotHole)))) {
slot = numericSlotHole;
numericSlotHole = middleOfTable;
} else {
slot = nextNumericSlot;
nextNumericSlot--;
numericSlotHole = middleOfTable; // scrap hole as it is now in the cached JTOC part
VM_SubArchBootRecord.updateJtocNumeric();
}
}
if (nextNumericSlot < 0) {
enlargeTable();
}
return slotAsOffset(slot);
}
/**
* Allocate a reference slot in the jtoc.
* @return offset of slot that was allocated as int
* (two slots are allocated on 64bit architectures)
*/
public static synchronized Offset allocateReferenceSlot() {
int slot = nextReferenceSlot;
if (VM.BuildFor64Addr) {
nextReferenceSlot += 2;
} else {
nextReferenceSlot++;
}
if (nextReferenceSlot >= slots.length) {
enlargeTable();
}
VM_SubArchBootRecord.updateJtocReference();
return slotAsOffset(slot);
}
// /**
// * Allocate a method reference slot in the jtoc.
// * Two words are used - one for address, one for length to
// * bring in code to local memory
// * @return offset of slot that was allocated as int
// */
// public static synchronized Offset allocateMethodSlot() {
// int slot = nextReferenceSlot;
// nextReferenceSlot += 2;
//
// if (nextReferenceSlot >= slots.length) {
// enlargeTable();
// }
// return slotAsOffset(slot);
// }
/**
* Grow the statics table
*/
private static void enlargeTable() {
// !!TODO: enlarge slots[] and descriptions[], and modify jtoc register to
// point to newly enlarged slots[]
// NOTE: very tricky on IA32 because opt uses 32 bit literal address to access jtoc.
VM.sysFail("VM_SubArchStatics.enlargeTable: jtoc is full");
}
/**
* Fetch number of numeric jtoc slots currently allocated.
*/
@Uninterruptible
public static int getNumberOfNumericSlots() {
return middleOfTable - nextNumericSlot;
}
/**
* Fetch number of reference jtoc slots currently allocated.
*/
@Uninterruptible
public static int getNumberOfReferenceSlots() {
return nextReferenceSlot - middleOfTable;
}
/**
* Fetch total number of slots comprising the jtoc.
*/
@Uninterruptible
public static int getTotalNumberOfSlots() {
return slots.length;
}
/**
* Does specified jtoc slot contain a reference?
* @param slot obtained from offsetAsSlot()
* @return true --> slot contains a reference
*/
@Uninterruptible
public static boolean isReference(int slot) {
return slot > middleOfTable;
}
/**
* Does specified jtoc slot contain an int sized literal?
* @param slot obtained from offsetAsSlot()
* @return true --> slot contains a reference
*/
public static boolean isIntSizeLiteral(int slot) {
if (isReference(slot)) {
return false;
} else {
int ival = getSlotContentsAsInt(slotAsOffset(slot));
Integer offsetAsInt;
synchronized (intSizeLiterals) {
offsetAsInt = intSizeLiterals.get(ival);
}
if (offsetAsInt == null) {
return false;
} else {
return slotAsOffset(slot).toInt() == offsetAsInt;
}
}
}
/**
* Does specified jtoc slot contain a long sized literal?
* @param slot obtained from offsetAsSlot()
* @return true --> slot contains a reference
*/
public static boolean isLongSizeLiteral(int slot) {
if (isReference(slot)) {
return false;
} else {
long lval = getSlotContentsAsLong(slotAsOffset(slot));
Integer offsetAsInt;
synchronized (longSizeLiterals) {
offsetAsInt = longSizeLiterals.get(lval);
}
if (offsetAsInt == null) {
return false;
} else {
return slotAsOffset(slot).toInt() == offsetAsInt;
}
}
}
/**
* Get size occupied by a reference
*/
@Uninterruptible
public static int getReferenceSlotSize() {
return VM.BuildFor64Addr ? 2 : 1;
}
/**
* Fetch jtoc object (for JNI environment and GC).
*/
@Uninterruptible
public static Address getSlots() {
return VM_Magic.objectAsAddress(slots).plus(middleOfTable << LOG_BYTES_IN_INT);
}
/**
* Fetch jtoc object (for JNI environment and GC).
*/
@Uninterruptible
public static int[] getSlotsAsIntArray() {
return slots;
}
/**
* Fetch contents of a slot, as an integer
*/
@Uninterruptible
public static int getSlotContentsAsInt(Offset offset) {
if (VM.runningVM) {
return VM_Magic.getIntAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT));
} else {
int slot = offsetAsSlot(offset);
return slots[slot];
}
}
/**
* Fetch contents of a slot-pair, as a long integer.
*/
@Uninterruptible
public static long getSlotContentsAsLong(Offset offset) {
if (VM.runningVM) {
long ret = VM_Magic.getLongAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT));
return ret;
} else {
int slot = offsetAsSlot(offset);
long result;
if (VM.LittleEndian) {
result = (((long) slots[slot + 1]) << BITS_IN_INT); // hi
result |= ((long) slots[slot]) & 0xFFFFFFFFL; // lo
} else {
result = (((long) slots[slot]) << BITS_IN_INT); // hi
result |= ((long) slots[slot + 1]) & 0xFFFFFFFFL; // lo
}
return result;
}
}
/**
* Fetch contents of a slot, as an object.
*/
@Uninterruptible
public static Object getSlotContentsAsObject(Offset offset) {
if (VM.runningVM) {
return VM_Magic.getObjectAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT));
} else {
return objectSlots[offsetAsSlot(offset)];
}
}
/**
* Fetch contents of a slot, as an Address.
*/
@UninterruptibleNoWarn
public static Address getSlotContentsAsAddress(Offset offset) {
if (VM.runningVM) {
if (VM.BuildFor32Addr) {
return Address.fromIntSignExtend(getSlotContentsAsInt(offset));
} else {
return Address.fromLong(getSlotContentsAsLong(offset));
}
} else {
// Addresses are represented by objects in the tools building the VM
Object unboxed = objectSlots[offsetAsSlot(offset)];
if (unboxed instanceof Address) {
return (Address) unboxed;
} else if (unboxed instanceof Word) {
return ((Word) unboxed).toAddress();
} else if (unboxed instanceof Extent) {
return ((Extent) unboxed).toWord().toAddress();
} else if (unboxed instanceof Offset) {
return ((Offset) unboxed).toWord().toAddress();
} else {
if (VM.VerifyAssertions) VM._assert(false);
return Address.zero();
}
}
}
/**
* Set contents of a slot, as an integer.
*/
@Uninterruptible
public static void setSlotContents(Offset offset, int value) {
if (VM.runningVM) {
if (VM.VerifyAssertions) VM._assert(VM_SubArchBootRecord.verifyUpdate(offset));
VM_Magic.setIntAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT), value);
} else {
slots[offsetAsSlot(offset)] = value;
}
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
}
/**
* Set contents of a slot, as a long integer.
*/
@UninterruptibleNoWarn
public static void setSlotContents(Offset offset, long value) {
if (VM.runningVM) {
if (VM.VerifyAssertions) VM._assert(VM_SubArchBootRecord.verifyUpdate(offset));
VM_Magic.setLongAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT), value);
} else {
int slot = offsetAsSlot(offset);
if (VM.LittleEndian) {
slots[slot + 1] = (int) (value >>> BITS_IN_INT); // hi
slots[slot] = (int) (value); // lo
} else {
slots[slot] = (int) (value >>> BITS_IN_INT); // hi
slots[slot + 1] = (int) (value); // lo
}
}
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
}
/**
* Set contents of a slot, as an object.
*/
@UninterruptibleNoWarn
public static void setSlotContents(Offset offset, Object object) {
// NB uninterruptible warnings are disabled for this method due to
// the array store which could cause a fault - this can't actually
// happen as the fault would only ever occur when not running the
// VM. We suppress the warning as we know the error can't happen.
if (VM.runningVM && MM_Constants.NEEDS_PUTSTATIC_WRITE_BARRIER) {
if (VM.VerifyAssertions) VM._assert(VM_SubArchBootRecord.verifyUpdate(offset));
MM_Interface.putstaticWriteBarrier(offset, object, 0);
} else {
setSlotContents(offset, VM_Magic.objectAsAddress(object).toWord());
}
if (VM.VerifyAssertions) VM._assert(offset.toInt() > 0);
if (!VM.runningVM && objectSlots != null) {
// When creating the boot image objectSlots is populated as
// VM_Magic won't work in the bootstrap JVM.
objectSlots[offsetAsSlot(offset)] = object;
}
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
}
/**
* Set contents of a slot, as a VM_CodeArray.
*/
@Uninterruptible
public static void setSlotContents(Offset offset, VM_CodeArray code) {
if (VM.VerifyAssertions) VM._assert(VM_SubArchBootRecord.verifyUpdate(offset));
setSlotContents(offset, VM_Magic.codeArrayAsObject(code));
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
}
/**
* Set contents of a slot, as a Word.
*/
@Uninterruptible
public static void setSlotContents(Offset offset, Word word) {
if (VM.runningVM) {
if (VM.VerifyAssertions) VM._assert(VM_SubArchBootRecord.verifyUpdate(offset));
VM_Magic.setWordAtOffset(slots, offset.plus(middleOfTable << LOG_BYTES_IN_INT), word);
} else {
if (VM.BuildFor32Addr) {
setSlotContents(offset, word.toInt());
} else {
setSlotContents(offset, word.toLong());
}
}
SubordinateArchitecture.VM_SubArchBootRecord.setJtocDirty();
}
/**
* Inform VM_Statics that boot image instantiation is over and that
* unnecessary data structures, for runtime, can be released
*/
public static void bootImageInstantiationFinished() {
objectSlots = null;
}
/**
* Search for a type that this TIB
* @param tibOff offset of TIB in JTOC
* @return type of TIB or null
*/
public static VM_Type findTypeOfTIBSlot(Offset tibOff) {
for (VM_Type type : VM_Type.getTypes()) {
if (type != null && type.getTibOffset().EQ(tibOff)) {
return type;
}
}
return null;
}
/**
* Store details of a new type in the subarch static tables
*/
public static synchronized Offset addNewType(Address numStaticsBlock, int numStaticsLen,
Address refStaticsBlock, int refStaticsLen,
Address methodTIBBlock, int methodTIBLen) {
if (VM.runningVM) {
staticsTOC.set((2*currTOCidx), numStaticsBlock);
staticsTOC.set((2*currTOCidx) + 1, refStaticsBlock);
} else {
// need to do this later as a fixup otherwise the value will be overwritten
VM_Magic.bootWriterFixup(staticsTOC.getBacking(), (2*currTOCidx) << LOG_BYTES_IN_ADDRESS, numStaticsBlock.toInt());
VM_Magic.bootWriterFixup(staticsTOC.getBacking(), ((2*currTOCidx) + 1) << LOG_BYTES_IN_ADDRESS, refStaticsBlock.toInt());
}
methodTIB.set(currTOCidx, methodTIBBlock);
// compress the three sizes into a single entry
numStaticsLen = VM_Memory.alignUp(numStaticsLen, BYTES_IN_QUAD);
numStaticsLen = numStaticsLen >> LOG_BYTES_IN_QUAD;
refStaticsLen = VM_Memory.alignUp(refStaticsLen, BYTES_IN_QUAD);
refStaticsLen = refStaticsLen >> LOG_BYTES_IN_QUAD;
methodTIBLen = VM_Memory.alignUp(methodTIBLen, BYTES_IN_QUAD);
methodTIBLen = methodTIBLen >> LOG_BYTES_IN_QUAD;
if (VM.VerifyAssertions) VM._assert((numStaticsLen < (1 << 11)) &&
(refStaticsLen < (1 << 11)) &&
(methodTIBLen < (1 << 11)));
sizeStaticsTable[currTOCidx] = (methodTIBLen << 20) | (refStaticsLen << 10) | numStaticsLen;
Offset ret = Offset.fromIntSignExtend(currTOCidx << LOG_BYTES_IN_ADDRESS);
currTOCidx++;
return ret;
}
public static Object getMethodTIB() {
return methodTIB;
}
public static Object getSizeStaticsTable() {
return sizeStaticsTable;
}
}