001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.objectmodel;
014
015import static org.jikesrvm.objectmodel.TIBLayoutConstants.*;
016import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BITS_IN_BYTE;
017import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_INT;
018import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
019import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
020
021import org.jikesrvm.VM;
022import org.jikesrvm.architecture.ArchConstants;
023import org.jikesrvm.classloader.RVMType;
024import org.jikesrvm.compilers.common.CodeArray;
025import org.jikesrvm.compilers.common.LazyCompilationTrampoline;
026import org.jikesrvm.runtime.Magic;
027import org.vmmagic.Intrinsic;
028import org.vmmagic.pragma.Inline;
029import org.vmmagic.pragma.Interruptible;
030import org.vmmagic.pragma.NoInline;
031import org.vmmagic.pragma.NonMoving;
032import org.vmmagic.pragma.Uninterruptible;
033import org.vmmagic.pragma.UninterruptibleNoWarn;
034import org.vmmagic.unboxed.Address;
035import org.vmmagic.unboxed.Offset;
036import org.vmmagic.unboxed.Word;
037
038/**
039 * This class represents an instance of a type information block, at runtime it
040 * is an array with Object elements.
041 * @see TIBLayoutConstants
042 */
043@Uninterruptible
044@NonMoving
045public final class TIB implements RuntimeTable<Object> {
046  /**
047   * @return the number of words required to hold the lazy method invoker trampoline.
048   */
049  public static int lazyMethodInvokerTrampolineWords() {
050    int codeWords = VM.BuildForIA32 ? (VM.BuildFor32Addr ? 2 : 1) : (VM.BuildFor32Addr ? 3 : 2);
051    if (VM.VerifyAssertions && VM.runningVM) {
052      int codeBytes = LazyCompilationTrampoline.getInstructions().length() << ArchConstants.getLogInstructionWidth();
053      VM._assert(codeWords == ((codeBytes + BYTES_IN_ADDRESS - 1) >>> LOG_BYTES_IN_ADDRESS));
054    }
055    return codeWords;
056  }
057
058  /** Alignment encoded data for this TIB - only used at build time */
059  private int alignData;
060
061
062  /**
063   * Calculates the size of a TIB.
064   *
065   * @param numVirtualMethods the number of virtual methods in the TIB
066   * @return the size of a TIB with the given number of virtual methods
067   */
068  @NoInline
069  public static int computeSize(int numVirtualMethods) {
070    return TIB_FIRST_VIRTUAL_METHOD_INDEX + numVirtualMethods + lazyMethodInvokerTrampolineWords();
071  }
072
073  /**
074   * Calculate the virtual method offset for the given index.
075   * @param virtualMethodIndex The index to calculate the offset for
076   * @return The offset.
077   */
078  public static Offset getVirtualMethodOffset(int virtualMethodIndex) {
079    return Offset.fromIntZeroExtend((TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex) << LOG_BYTES_IN_ADDRESS);
080  }
081
082  /**
083   * Calculate the virtual method index for the given offset.
084   * @param virtualMethodOffset The offset to calculate the index for
085   * @return The index.
086   */
087  public static int getVirtualMethodIndex(Offset virtualMethodOffset) {
088    return (virtualMethodOffset.toInt() >>> LOG_BYTES_IN_ADDRESS) - TIB_FIRST_VIRTUAL_METHOD_INDEX;
089  }
090
091  /**
092   * Calculate the virtual method index for the given raw slot index.
093   *
094   * @param slot The raw slot to find the virtual method index for.
095   * @return The index.
096   */
097  public static int getVirtualMethodIndex(int slot) {
098    if (VM.VerifyAssertions) VM._assert(slot > TIB_FIRST_VIRTUAL_METHOD_INDEX);
099    return slot - TIB_FIRST_VIRTUAL_METHOD_INDEX;
100  }
101
102  /**
103   * The backing data used during boot image writing.
104   */
105  private final Object[] data;
106
107  private TIB(int size) {
108    this.data = new Object[size];
109  }
110
111  @Override
112  public Object[] getBacking() {
113    if (VM.VerifyAssertions) VM._assert(!VM.runningVM);
114    return data;
115  }
116
117  /**
118   * Create a new TIB of the specified size.
119   *
120   * @param size The size of the TIB
121   * @param alignData Alignment-encoded data for this TIB,
122   *      AlignmentEncoding.ALIGN_CODE_NONE for no alignment encoding.
123   * @return The created TIB instance.
124   */
125  @NoInline
126  @Interruptible
127  public static TIB allocate(int size, int alignData) {
128    if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
129    TIB tib = new TIB(size);
130    tib.setAlignData(alignData);
131    return tib;
132  }
133
134  /**
135   * Get a TIB entry.
136   *
137   * @param index The index of the entry to get
138   * @return The value of that entry
139   */
140  @Override
141  @Intrinsic
142  public Object get(int index) {
143    if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
144    return data[index];
145  }
146
147  /**
148   * Set a TIB entry.
149   *
150   * @param index The index of the entry to set
151   * @param value The value to set the entry to.
152   */
153  @Override
154  @Intrinsic
155  @UninterruptibleNoWarn("Interruptible code not reachable at runtime")
156  public void set(int index, Object value) {
157    if (VM.VerifyAssertions && VM.runningVM) VM._assert(VM.NOT_REACHED);
158    data[index] = value;
159  }
160
161  /**
162   * @return the length of the TIB
163   */
164  @Override
165  @Intrinsic
166  public int length() {
167    return data.length;
168  }
169
170  @Inline
171  public RVMType getType() {
172    if (VM.runningVM) {
173      return Magic.objectAsType(get(TIB_TYPE_INDEX));
174    } else {
175      return (RVMType)get(TIB_TYPE_INDEX);
176    }
177  }
178
179  public void setType(RVMType type) {
180    set(TIB_TYPE_INDEX, type);
181  }
182
183  @Inline
184  public short[] getSuperclassIds() {
185    return Magic.objectAsShortArray(get(TIB_SUPERCLASS_IDS_INDEX));
186  }
187
188  public void setSuperclassIds(short[] superclassIds) {
189    set(TIB_SUPERCLASS_IDS_INDEX, superclassIds);
190  }
191
192  @Interruptible
193  public ITableArray getITableArray() {
194    if (VM.VerifyAssertions) VM._assert(getType().isClassType());
195    return (ITableArray)get(TIB_INTERFACE_DISPATCH_TABLE_INDEX);
196  }
197
198  public void setITableArray(ITableArray iTableArray) {
199    if (VM.VerifyAssertions) VM._assert(getType().isClassType());
200    set(TIB_INTERFACE_DISPATCH_TABLE_INDEX, iTableArray);
201  }
202
203  @Inline
204  public int[] getDoesImplement() {
205    return Magic.objectAsIntArray(get(TIB_DOES_IMPLEMENT_INDEX));
206  }
207
208  public void setDoesImplement(int[] doesImplement) {
209    set(TIB_DOES_IMPLEMENT_INDEX, doesImplement);
210  }
211
212  @Interruptible
213  public IMT getImt() {
214    if (VM.VerifyAssertions) VM._assert(getType().isClassType());
215    return (IMT)get(TIB_INTERFACE_DISPATCH_TABLE_INDEX);
216  }
217
218  public void setImt(IMT imt) {
219    if (VM.VerifyAssertions) VM._assert(imt.length() == IMT_METHOD_SLOTS);
220    if (VM.VerifyAssertions) VM._assert(getType().isClassType());
221    set(TIB_INTERFACE_DISPATCH_TABLE_INDEX, imt);
222  }
223
224  public void setArrayElementTib(TIB arrayElementTIB) {
225    if (VM.VerifyAssertions) VM._assert(getType().isArrayType());
226    set(TIB_ARRAY_ELEMENT_TIB_INDEX, Magic.tibAsObject(arrayElementTIB));
227  }
228
229  /**
230   * Gets a virtual method from this TIB.
231   *
232   * When running the VM, we must translate requests to return the internal
233   * lazy compilation trampoline marker.
234   *
235   * @param virtualMethodIndex the index of the virtual method
236   * @return the code for the virtual method or a lazy compilation trampoline
237   */
238  @NoInline
239  @Interruptible
240  public CodeArray getVirtualMethod(int virtualMethodIndex) {
241    int index = TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex;
242    if (VM.runningVM && isInternalLazyCompilationTrampoline(virtualMethodIndex)) {
243      return LazyCompilationTrampoline.getInstructions();
244    }
245    return (CodeArray) get(index);
246  }
247
248  /**
249   * @param virtualMethodIndex the index of the virtual method
250   * @return whether a virtual method is the internal lazy compilation trampoline
251   */
252  @NoInline
253  public boolean isInternalLazyCompilationTrampoline(int virtualMethodIndex) {
254    int index = TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex;
255    Address tibAddress = Magic.objectAsAddress(this);
256    Address callAddress = tibAddress.loadAddress(Offset.fromIntZeroExtend(index << LOG_BYTES_IN_ADDRESS));
257    Address maxAddress = tibAddress.plus(Offset.fromIntZeroExtend(length() << LOG_BYTES_IN_ADDRESS));
258    return callAddress.GE(tibAddress) && callAddress.LT(maxAddress);
259  }
260
261  @Interruptible
262  public CodeArray getVirtualMethod(Offset virtualMethodOffset) {
263    return getVirtualMethod(getVirtualMethodIndex(virtualMethodOffset));
264  }
265
266  /**
267   * Set a virtual method in this TIB.
268   *
269   * When running the VM, we must translate requests to use the internal
270   * lazy compilation trampoline.
271   *
272   * @param virtualMethodIndex the index of the virtual metho
273   * @param code the code for the virtual method
274   */
275  @NoInline
276  public void setVirtualMethod(int virtualMethodIndex, CodeArray code) {
277    if (VM.VerifyAssertions) VM._assert(virtualMethodIndex >= 0);
278
279    if (VM.runningVM && code == LazyCompilationTrampoline.getInstructions()) {
280      Address tibAddress = Magic.objectAsAddress(this);
281      Address callAddress = tibAddress.plus(Offset.fromIntZeroExtend(lazyMethodInvokerTrampolineIndex() << LOG_BYTES_IN_ADDRESS));
282      set(TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex, callAddress);
283    } else {
284      set(TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethodIndex, code);
285    }
286  }
287
288  public void setVirtualMethod(Offset virtualMethodOffset, CodeArray code) {
289    setVirtualMethod(getVirtualMethodIndex(virtualMethodOffset), code);
290  }
291
292  /**
293   * Calculate the address that is the call target for the lazy method invoker trampoline.
294   * @return the offset of the instruction that is the call target
295   */
296  public int lazyMethodInvokerTrampolineIndex() {
297    return length() - lazyMethodInvokerTrampolineWords();
298  }
299
300  /**
301   * Initialize the lazy method invoker trampoline for this tib.
302   */
303  @NoInline
304  public void initializeInternalLazyCompilationTrampoline() {
305    CodeArray source = LazyCompilationTrampoline.getInstructions();
306    int targetSlot = lazyMethodInvokerTrampolineIndex();
307    int logIPW = LOG_BYTES_IN_ADDRESS - ArchConstants.getLogInstructionWidth();
308    int logIPI = LOG_BYTES_IN_INT - ArchConstants.getLogInstructionWidth();
309    if (VM.VerifyAssertions) VM._assert(ArchConstants.getLogInstructionWidth() <= LOG_BYTES_IN_INT);
310    int mask = 0xFFFFFFFF >>> (((1 << logIPI) - 1) << LOG_BITS_IN_BYTE);
311    for (int i = 0; i < lazyMethodInvokerTrampolineWords(); i++) {
312      Word currentWord = Word.zero();
313      int base = i << logIPW;
314      for (int j = 0; j < (1 << logIPW) && (base + j) < source.length(); j++) {
315        Word currentEntry = Word.fromIntZeroExtend(source.get(base + j) & mask);
316        currentEntry = currentEntry.lsh(((VM.LittleEndian ? j : (1 << logIPW) - (j + 1)) << ArchConstants.getLogInstructionWidth()) << LOG_BITS_IN_BYTE);
317        currentWord = currentWord.or(currentEntry);
318      }
319      set(targetSlot + i, currentWord);
320    }
321  }
322
323
324  public void setSpecializedMethod(int specializedMethodIndex, CodeArray code) {
325    if (VM.VerifyAssertions) VM._assert(specializedMethodIndex >= 0);
326    set(TIB_FIRST_SPECIALIZED_METHOD_INDEX + specializedMethodIndex, code);
327  }
328
329  /**
330   * @return the number of virtual methods in this TIB.
331   */
332  public int numVirtualMethods() {
333    return length() - TIB_FIRST_VIRTUAL_METHOD_INDEX - lazyMethodInvokerTrampolineWords();
334  }
335
336  /**
337   * Does this slot in the TIB hold a TIB entry?
338   * @param slot the TIB slot
339   * @return {@code true} if this the array element TIB
340   */
341  public boolean slotContainsTib(int slot) {
342    if (slot == TIB_ARRAY_ELEMENT_TIB_INDEX && getType().isArrayType()) {
343      if (VM.VerifyAssertions) VM._assert(get(slot) != null);
344      return true;
345    }
346    return false;
347  }
348
349  /**
350   * Does this slot in the TIB hold code?
351   * @param slot the TIB slot
352   * @return {@code true} if slot is one that holds a code array reference
353   */
354  public boolean slotContainsCode(int slot) {
355    if (VM.VerifyAssertions) {
356      VM._assert(slot < length());
357    }
358    return slot >= TIB_FIRST_VIRTUAL_METHOD_INDEX;
359  }
360
361  public void setAlignData(int alignData) {
362    this.alignData = alignData;
363  }
364
365  public int getAlignData() {
366    return alignData;
367  }
368}