/*
* 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.objectmodel.TIBLayoutConstants.*;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BITS_IN_BYTE;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_INT;
import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
import org.jikesrvm.VM;
import org.jikesrvm.architecture.ArchConstants;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.compilers.common.CodeArray;
import org.jikesrvm.compilers.common.LazyCompilationTrampoline;
import org.jikesrvm.runtime.Magic;
import org.vmmagic.Intrinsic;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.NonMoving;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.UninterruptibleNoWarn;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* This class represents an instance of a type information block, at runtime it
* is an array with Object elements.
* @see TIBLayoutConstants
*/
@Uninterruptible
@NonMoving
public final class TIB implements RuntimeTable<Object> {
/**
* @return the number of words required to hold the lazy method invoker trampoline.
*/
public static int lazyMethodInvokerTrampolineWords() {
int codeWords = VM.BuildForIA32 ? (VM.BuildFor32Addr ? 2 : 1) : (VM.BuildFor32Addr ? 3 : 2);
if (VM.VerifyAssertions && VM.runningVM) {
int codeBytes = LazyCompilationTrampoline.getInstructions().length() << ArchConstants.getLogInstructionWidth();
VM._assert(codeWords == ((codeBytes + BYTES_IN_ADDRESS - 1) >>> LOG_BYTES_IN_ADDRESS));
}
return codeWords;
}
/** Alignment encoded data for this TIB - only used at build time */
private int alignData;
/**
* Calculates the size of a TIB.
*
* @param numVirtualMethods the number of virtual methods in the TIB
* @return the size of a TIB with the given number of virtual methods
*/
@NoInline
public static int computeSize(int numVirtualMethods) {
return TIB_FIRST_VIRTUAL_METHOD_INDEX + numVirtualMethods + lazyMethodInvokerTrampolineWords();
}
/**
* Calculate the virtual method offset for the given index.
* @param virtualMethodIndex The index to calculate the offset for
* @return The offset.
*/
public static Offset getVirtualMethodOffset(int virtualMethodIndex) {
return Offset.fromIntZeroExtend((TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex) << LOG_BYTES_IN_ADDRESS);
}
/**
* Calculate the virtual method index for the given offset.
* @param virtualMethodOffset The offset to calculate the index for
* @return The index.
*/
public static int getVirtualMethodIndex(Offset virtualMethodOffset) {
return (virtualMethodOffset.toInt() >>> LOG_BYTES_IN_ADDRESS) - TIB_FIRST_VIRTUAL_METHOD_INDEX;
}
/**
* Calculate the virtual method index for the given raw slot index.
*
* @param slot The raw slot to find the virtual method index for.
* @return The index.
*/
public static int getVirtualMethodIndex(int slot) {
if (VM.VerifyAssertions) VM._assert(slot > TIB_FIRST_VIRTUAL_METHOD_INDEX);
return slot - TIB_FIRST_VIRTUAL_METHOD_INDEX;
}
/**
* The backing data used during boot image writing.
*/
private final Object[] data;
private TIB(int size) {
this.data = new Object[size];
}
@Override
public Object[] getBacking() {
if (VM.VerifyAssertions) VM._assert(!VM.runningVM);
return data;
}
/**
* Create a new TIB of the specified size.
*
* @param size The size of the TIB
* @param alignData Alignment-encoded data for this TIB,
* AlignmentEncoding.ALIGN_CODE_NONE for no alignment encoding.
* @return The created TIB instance.
*/
@NoInline
@Interruptible
public static TIB allocate(int size, int alignData) {
if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
TIB tib = new TIB(size);
tib.setAlignData(alignData);
return tib;
}
/**
* Get a TIB entry.
*
* @param index The index of the entry to get
* @return The value of that entry
*/
@Override
@Intrinsic
public Object get(int index) {
if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
return data[index];
}
/**
* Set a TIB entry.
*
* @param index The index of the entry to set
* @param value The value to set the entry to.
*/
@Override
@Intrinsic
@UninterruptibleNoWarn("Interruptible code not reachable at runtime")
public void set(int index, Object value) {
if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
data[index] = value;
}
/**
* @return the length of the TIB
*/
@Override
@Intrinsic
public int length() {
return data.length;
}
@Inline
public RVMType getType() {
if (VM.runningVM) {
return Magic.objectAsType(get(TIB_TYPE_INDEX));
} else {
return (RVMType)get(TIB_TYPE_INDEX);
}
}
public void setType(RVMType type) {
set(TIB_TYPE_INDEX, type);
}
@Inline
public short[] getSuperclassIds() {
return Magic.objectAsShortArray(get(TIB_SUPERCLASS_IDS_INDEX));
}
public void setSuperclassIds(short[] superclassIds) {
set(TIB_SUPERCLASS_IDS_INDEX, superclassIds);
}
@Interruptible
public ITableArray getITableArray() {
if (VM.VerifyAssertions) VM._assert(getType().isClassType());
return (ITableArray)get(TIB_INTERFACE_DISPATCH_TABLE_INDEX);
}
public void setITableArray(ITableArray iTableArray) {
if (VM.VerifyAssertions) VM._assert(getType().isClassType());
set(TIB_INTERFACE_DISPATCH_TABLE_INDEX, iTableArray);
}
@Inline
public int[] getDoesImplement() {
return Magic.objectAsIntArray(get(TIB_DOES_IMPLEMENT_INDEX));
}
public void setDoesImplement(int[] doesImplement) {
set(TIB_DOES_IMPLEMENT_INDEX, doesImplement);
}
@Interruptible
public IMT getImt() {
if (VM.VerifyAssertions) VM._assert(getType().isClassType());
return (IMT)get(TIB_INTERFACE_DISPATCH_TABLE_INDEX);
}
public void setImt(IMT imt) {
if (VM.VerifyAssertions) VM._assert(imt.length() == IMT_METHOD_SLOTS);
if (VM.VerifyAssertions) VM._assert(getType().isClassType());
set(TIB_INTERFACE_DISPATCH_TABLE_INDEX, imt);
}
public void setArrayElementTib(TIB arrayElementTIB) {
if (VM.VerifyAssertions) VM._assert(getType().isArrayType());
set(TIB_ARRAY_ELEMENT_TIB_INDEX, Magic.tibAsObject(arrayElementTIB));
}
/**
* Gets a virtual method from this TIB.
*
* When running the VM, we must translate requests to return the internal
* lazy compilation trampoline marker.
*
* @param virtualMethodIndex the index of the virtual method
* @return the code for the virtual method or a lazy compilation trampoline
*/
@NoInline
@Interruptible
public CodeArray getVirtualMethod(int virtualMethodIndex) {
int index = TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex;
if (VM.runningVM && isInternalLazyCompilationTrampoline(virtualMethodIndex)) {
return LazyCompilationTrampoline.getInstructions();
}
return (CodeArray) get(index);
}
/**
* @param virtualMethodIndex the index of the virtual method
* @return whether a virtual method is the internal lazy compilation trampoline
*/
@NoInline
public boolean isInternalLazyCompilationTrampoline(int virtualMethodIndex) {
int index = TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex;
Address tibAddress = Magic.objectAsAddress(this);
Address callAddress = tibAddress.loadAddress(Offset.fromIntZeroExtend(index << LOG_BYTES_IN_ADDRESS));
Address maxAddress = tibAddress.plus(Offset.fromIntZeroExtend(length() << LOG_BYTES_IN_ADDRESS));
return callAddress.GE(tibAddress) && callAddress.LT(maxAddress);
}
@Interruptible
public CodeArray getVirtualMethod(Offset virtualMethodOffset) {
return getVirtualMethod(getVirtualMethodIndex(virtualMethodOffset));
}
/**
* Set a virtual method in this TIB.
*
* When running the VM, we must translate requests to use the internal
* lazy compilation trampoline.
*
* @param virtualMethodIndex the index of the virtual metho
* @param code the code for the virtual method
*/
@NoInline
public void setVirtualMethod(int virtualMethodIndex, CodeArray code) {
if (VM.VerifyAssertions) VM._assert(virtualMethodIndex >= 0);
if (VM.runningVM && code == LazyCompilationTrampoline.getInstructions()) {
Address tibAddress = Magic.objectAsAddress(this);
Address callAddress = tibAddress.plus(Offset.fromIntZeroExtend(lazyMethodInvokerTrampolineIndex() << LOG_BYTES_IN_ADDRESS));
set(TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex, callAddress);
} else {
set(TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex, code);
}
}
public void setVirtualMethod(Offset virtualMethodOffset, CodeArray code) {
setVirtualMethod(getVirtualMethodIndex(virtualMethodOffset), code);
}
/**
* Calculate the address that is the call target for the lazy method invoker trampoline.
* @return the offset of the instruction that is the call target
*/
public int lazyMethodInvokerTrampolineIndex() {
return length() - lazyMethodInvokerTrampolineWords();
}
/**
* Initialize the lazy method invoker trampoline for this tib.
*/
@NoInline
public void initializeInternalLazyCompilationTrampoline() {
CodeArray source = LazyCompilationTrampoline.getInstructions();
int targetSlot = lazyMethodInvokerTrampolineIndex();
int logIPW = LOG_BYTES_IN_ADDRESS - ArchConstants.getLogInstructionWidth();
int logIPI = LOG_BYTES_IN_INT - ArchConstants.getLogInstructionWidth();
if (VM.VerifyAssertions) VM._assert(ArchConstants.getLogInstructionWidth() <= LOG_BYTES_IN_INT);
int mask = 0xFFFFFFFF >>> (((1 << logIPI) - 1) << LOG_BITS_IN_BYTE);
for (int i = 0; i < lazyMethodInvokerTrampolineWords(); i++) {
Word currentWord = Word.zero();
int base = i << logIPW;
for (int j = 0; j < (1 << logIPW) && (base + j) < source.length(); j++) {
Word currentEntry = Word.fromIntZeroExtend(source.get(base + j) & mask);
currentEntry = currentEntry.lsh(((VM.LittleEndian ? j : (1 << logIPW) - (j + 1)) << ArchConstants.getLogInstructionWidth()) << LOG_BITS_IN_BYTE);
currentWord = currentWord.or(currentEntry);
}
set(targetSlot + i, currentWord);
}
}
public void setSpecializedMethod(int specializedMethodIndex, CodeArray code) {
if (VM.VerifyAssertions) VM._assert(specializedMethodIndex >= 0);
set(TIB_FIRST_SPECIALIZED_METHOD_INDEX + specializedMethodIndex, code);
}
/**
* @return the number of virtual methods in this TIB.
*/
public int numVirtualMethods() {
return length() - TIB_FIRST_VIRTUAL_METHOD_INDEX - lazyMethodInvokerTrampolineWords();
}
/**
* Does this slot in the TIB hold a TIB entry?
* @param slot the TIB slot
* @return {@code true} if this the array element TIB
*/
public boolean slotContainsTib(int slot) {
if (slot == TIB_ARRAY_ELEMENT_TIB_INDEX && getType().isArrayType()) {
if (VM.VerifyAssertions) VM._assert(get(slot) != null);
return true;
}
return false;
}
/**
* Does this slot in the TIB hold code?
* @param slot the TIB slot
* @return {@code true} if slot is one that holds a code array reference
*/
public boolean slotContainsCode(int slot) {
if (VM.VerifyAssertions) {
VM._assert(slot < length());
}
return slot >= TIB_FIRST_VIRTUAL_METHOD_INDEX;
}
public void setAlignData(int alignData) {
this.alignData = alignData;
}
public int getAlignData() {
return alignData;
}
}