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.runtime;
014
015import static org.jikesrvm.VM.NOT_REACHED;
016import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_BYTE;
017import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_CHAR;
018import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_INT;
019import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_LONG;
020import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
021
022import org.jikesrvm.VM;
023import org.jikesrvm.architecture.AbstractRegisters;
024import org.jikesrvm.architecture.StackFrameLayout;
025import org.jikesrvm.classloader.DynamicTypeCheck;
026import org.jikesrvm.classloader.MemberReference;
027import org.jikesrvm.classloader.RVMArray;
028import org.jikesrvm.classloader.RVMClass;
029import org.jikesrvm.classloader.RVMField;
030import org.jikesrvm.classloader.RVMMethod;
031import org.jikesrvm.classloader.RVMType;
032import org.jikesrvm.classloader.TypeReference;
033import org.jikesrvm.compilers.common.CompiledMethod;
034import org.jikesrvm.compilers.common.CompiledMethods;
035import org.jikesrvm.mm.mminterface.Barriers;
036import org.jikesrvm.mm.mminterface.MemoryManager;
037import org.jikesrvm.objectmodel.ObjectModel;
038import org.jikesrvm.objectmodel.TIB;
039import org.jikesrvm.scheduler.RVMThread;
040import org.jikesrvm.util.Services;
041import org.vmmagic.pragma.Entrypoint;
042import org.vmmagic.pragma.Inline;
043import org.vmmagic.pragma.NoInline;
044import org.vmmagic.pragma.Pure;
045import org.vmmagic.pragma.Uninterruptible;
046import org.vmmagic.pragma.Unpreemptible;
047import org.vmmagic.pragma.UnpreemptibleNoWarn;
048import org.vmmagic.unboxed.Address;
049import org.vmmagic.unboxed.Offset;
050import org.vmmagic.unboxed.Word;
051
052/**
053 * Entrypoints into the runtime of the virtual machine.
054 *
055 * <p> These are "helper functions" called from machine code
056 * emitted by BaselineCompilerImpl.
057 * They implement functionality that cannot be mapped directly
058 * into a small inline
059 * sequence of machine instructions. See also: Linker.
060 *
061 * <p> Note #1: If you add, remove, or change the signature of
062 * any of these methods you may need to change Entrypoints to match.
063 *
064 * <p> Note #2: Code here must be carefully written to be gc-safe
065 * while manipulating
066 * stackframe and instruction addresses.
067 *
068 * <p> Any time we are holding interior pointers to objects that
069 * could be moved by a garbage
070 * collection cycle we must either avoid passing through gc-sites
071 * (by writing
072 * straight line code with no "non-magic" method invocations) or we
073 * must turn off the
074 * collector (so that a gc request initiated by another thread will
075 * not run until we're
076 * done manipulating the bare pointers). Furthermore, while
077 * the collector is turned off,
078 * we must be careful not to make any allocation requests ("new").
079 *
080 * <p> The interior pointers that we must worry about are:
081 * <ul>
082 *   <li> "ip" values that point to interiors of "code" objects
083 *   <li> "fp" values that point to interior of "stack" objects
084 * </ul>
085 */
086public class RuntimeEntrypoints {
087
088  private static final boolean traceAthrow = false;
089  // Trap codes for communication with C trap handler.
090  //
091  public static final int TRAP_UNKNOWN = -1;
092  public static final int TRAP_NULL_POINTER = 0;
093  public static final int TRAP_ARRAY_BOUNDS = 1;
094  public static final int TRAP_DIVIDE_BY_ZERO = 2;
095  public static final int TRAP_STACK_OVERFLOW = 3;
096  public static final int TRAP_CHECKCAST = 4; // opt-compiler
097  public static final int TRAP_REGENERATE = 5; // opt-compiler
098  public static final int TRAP_JNI_STACK = 6; // jni
099  public static final int TRAP_MUST_IMPLEMENT = 7;
100  public static final int TRAP_STORE_CHECK = 8; // opt-compiler
101  public static final int TRAP_STACK_OVERFLOW_FATAL = 9; // assertion checking
102  public static final int TRAP_UNREACHABLE_BYTECODE = 10; // IA32 baseline compiler assertion
103
104  private static final String UNREACHABLE_BC_MESSAGE = "Attempted to execute " +
105  "a bytecode that was determined to be unreachable!";
106
107  //---------------------------------------------------------------//
108  //                     Type Checking.                            //
109  //---------------------------------------------------------------//
110
111  /**
112   * Test if object is instance of target class/array or
113   * implements target interface.
114   * @param object object to be tested
115   * @param targetID type reference id corresponding to target
116   *                 class/array/interface
117   * @return true iff is object instance of target type?
118   */
119  @Entrypoint
120  static boolean instanceOf(Object object, int targetID) throws NoClassDefFoundError {
121
122    /*  Here, LHS and RHS refer to the way we would treat these if they were
123        arguments to an assignment operator and we were testing for
124        assignment-compatibility.  In Java, "rhs instanceof lhs" means that
125        the operation "lhs = rhs" would succeed.   This of course is backwards
126        if one is looking at it from the point of view of the "instanceof"
127        operator.  */
128    TypeReference tRef = TypeReference.getTypeRef(targetID);
129    RVMType lhsType = tRef.peekType();
130    if (lhsType == null) {
131      lhsType = tRef.resolve();
132    }
133    if (!lhsType.isResolved()) {
134      lhsType.resolve(); // forces loading/resolution of super class/interfaces
135    }
136
137    /* Test for null only AFTER we have resolved the type of targetID. */
138    if (object == null) {
139      return false; // null is not an instance of any type
140    }
141
142    RVMType rhsType = ObjectModel.getObjectType(object);
143    /* RHS must already be resolved, since we have a non-null object that is
144       an instance of RHS  */
145    if (VM.VerifyAssertions) VM._assert(rhsType.isResolved());
146    if (VM.VerifyAssertions) VM._assert(lhsType.isResolved());
147
148    return lhsType == rhsType || DynamicTypeCheck.instanceOfResolved(lhsType, rhsType);
149  }
150
151  /**
152   * Throw exception unless object is instance of target
153   * class/array or implements target interface.
154   * @param object object to be tested
155   * @param id of type reference corresponding to target class/array/interface
156   */
157  @Entrypoint
158  static void checkcast(Object object, int id) throws ClassCastException, NoClassDefFoundError {
159    if (object == null) {
160      return; // null may be cast to any type
161    }
162
163    TypeReference tRef = TypeReference.getTypeRef(id);
164    RVMType lhsType = tRef.peekType();
165    if (lhsType == null) {
166      lhsType = tRef.resolve();
167    }
168    RVMType rhsType = ObjectModel.getObjectType(object);
169    if (lhsType == rhsType) {
170      return; // exact match
171    }
172
173    // not an exact match, do more involved lookups
174    //
175    if (!isAssignableWith(lhsType, rhsType)) {
176      throw new ClassCastException("Cannot cast a(n) " + rhsType + " to a(n) " + lhsType);
177    }
178  }
179
180  @Entrypoint
181  static void aastore(Object[] arrayRef, int index, Object value) throws ArrayStoreException, ArrayIndexOutOfBoundsException {
182    checkstore(arrayRef, value);
183    int nelts = ObjectModel.getArrayLength(arrayRef);
184    if (index >= 0 && index < nelts) {
185      Services.setArrayUninterruptible(arrayRef, index, value);
186    } else {
187      throw new ArrayIndexOutOfBoundsException(index);
188    }
189  }
190
191  @Entrypoint
192  @Uninterruptible
193  static void aastoreUninterruptible(Object[] arrayRef, int index, Object value) {
194    if (VM.VerifyAssertions) {
195      int nelts = ObjectModel.getArrayLength(arrayRef);
196      VM._assert(index >= 0 && index < nelts);
197    }
198    Services.setArrayUninterruptible(arrayRef, index, value);
199  }
200
201  @Entrypoint
202  @Inline
203  static void checkstore(Object array, Object arrayElement) throws ArrayStoreException {
204    if (arrayElement == null) {
205      return; // null may be assigned to any type
206    }
207
208    RVMType lhsType = Magic.getObjectType(array);
209    RVMType elmType = lhsType.asArray().getElementType();
210
211    if (elmType == RVMType.JavaLangObjectType) {
212      return; // array of Object can receive anything
213    }
214
215    RVMType rhsType = Magic.getObjectType(arrayElement);
216
217    if (elmType == rhsType) {
218      return; // exact type match
219    }
220
221    if (isAssignableWith(elmType, rhsType)) {
222      return;
223    }
224
225    throw new ArrayStoreException();
226  }
227
228  /**
229   * May a variable of type "lhs" be assigned a value of type "rhs"?
230   * @param lhs type of variable
231   * @param rhs type of value
232   * @return true  --&gt; assignment is legal
233   *           false --&gt; assignment is illegal
234   * <strong>Assumption</strong>: caller has already tested "trivial" case
235   * (exact type match)
236   *             so we need not repeat it here
237   */
238  @Pure
239  @Inline(value = Inline.When.AllArgumentsAreConstant)
240  public static boolean isAssignableWith(RVMType lhs, RVMType rhs) {
241    if (!lhs.isResolved()) {
242      lhs.resolve();
243    }
244    if (!rhs.isResolved()) {
245      rhs.resolve();
246    }
247    return DynamicTypeCheck.instanceOfResolved(lhs, rhs);
248  }
249
250  //---------------------------------------------------------------//
251  //                     Object Allocation.                        //
252  //---------------------------------------------------------------//
253
254  /**
255   * Allocate something like "new Foo()".
256   * @param id id of type reference of class to create
257   * @param site the site id of the calling allocation site
258   * @return object with header installed and all fields set to zero/null
259   *           (ready for initializer to be run on it)
260   * See also: bytecode 0xbb ("new")
261   */
262  @Entrypoint
263  static Object unresolvedNewScalar(int id, int site) throws NoClassDefFoundError, OutOfMemoryError {
264    TypeReference tRef = TypeReference.getTypeRef(id);
265    RVMType t = tRef.peekType();
266    if (t == null) {
267      t = tRef.resolve();
268    }
269    RVMClass cls = t.asClass();
270    if (!cls.isInitialized()) {
271      initializeClassForDynamicLink(cls);
272    }
273
274    int allocator = MemoryManager.pickAllocator(cls);
275    int align = ObjectModel.getAlignment(cls);
276    int offset = ObjectModel.getOffsetForAlignment(cls, false);
277    return resolvedNewScalar(cls.getInstanceSize(),
278                             cls.getTypeInformationBlock(),
279                             cls.hasFinalizer(),
280                             allocator,
281                             align,
282                             offset,
283                             site);
284  }
285
286  /**
287   * Allocate something like "new Foo()".
288   * @param cls RVMClass of array to create
289   * @return object with header installed and all fields set to zero/null
290   *           (ready for initializer to be run on it)
291   * See also: bytecode 0xbb ("new")
292   */
293  public static Object resolvedNewScalar(RVMClass cls) {
294
295    int allocator = MemoryManager.pickAllocator(cls);
296    int site = MemoryManager.getAllocationSite(false);
297    int align = ObjectModel.getAlignment(cls);
298    int offset = ObjectModel.getOffsetForAlignment(cls, false);
299    return resolvedNewScalar(cls.getInstanceSize(),
300                             cls.getTypeInformationBlock(),
301                             cls.hasFinalizer(),
302                             allocator,
303                             align,
304                             offset,
305                             site);
306  }
307
308  /**
309   * Allocate something like "new Foo()".
310   * @param size size of object (including header), in bytes
311   * @param tib  type information block for object
312   * @param hasFinalizer does this type have a finalizer?
313   * @param allocator int that encodes which allocator should be used
314   * @param align the alignment requested; must be a power of 2.
315   * @param offset the offset at which the alignment is desired.
316   * @param site the site id of the calling allocation site
317   * @return object with header installed and all fields set to zero/null
318   *           (ready for initializer to be run on it)
319   * See also: bytecode 0xbb ("new")
320   */
321  @Entrypoint
322  public static Object resolvedNewScalar(int size, TIB tib, boolean hasFinalizer, int allocator, int align,
323                                         int offset, int site) throws OutOfMemoryError {
324
325    // GC stress testing
326    if (VM.ForceFrequentGC) checkAllocationCountDownToGC();
327
328    // Allocate the object and initialize its header
329    Object newObj = MemoryManager.allocateScalar(size, tib, allocator, align, offset, site);
330
331    // Deal with finalization
332    if (hasFinalizer) MemoryManager.addFinalizer(newObj);
333
334    return newObj;
335  }
336
337  /**
338   * Allocate something like "new Foo[]".
339   * @param numElements number of array elements
340   * @param id id of type reference of array to create.
341   * @param site the site id of the calling allocation site
342   * @return array with header installed and all fields set to zero/null
343   * See also: bytecode 0xbc ("anewarray")
344   */
345  @Entrypoint
346  public static Object unresolvedNewArray(int numElements, int id, int site)
347      throws NoClassDefFoundError, OutOfMemoryError, NegativeArraySizeException {
348    TypeReference tRef = TypeReference.getTypeRef(id);
349    RVMType t = tRef.peekType();
350    if (t == null) {
351      t = tRef.resolve();
352    }
353    RVMArray array = t.asArray();
354    if (!array.isInitialized()) {
355      array.resolve();
356      array.instantiate();
357    }
358
359    return resolvedNewArray(numElements, array, site);
360  }
361
362  /**
363   * Allocate something like "new Foo[]".
364   * @param numElements number of array elements
365   * @param array RVMArray of array to create
366   * @return array with header installed and all fields set to zero/null
367   * See also: bytecode 0xbc ("anewarray")
368   */
369  public static Object resolvedNewArray(int numElements, RVMArray array)
370      throws OutOfMemoryError, NegativeArraySizeException {
371    return resolvedNewArray(numElements, array, MemoryManager.getAllocationSite(false));
372  }
373
374  public static Object resolvedNewArray(int numElements, RVMArray array, int site)
375      throws OutOfMemoryError, NegativeArraySizeException {
376
377    return resolvedNewArray(numElements,
378                            array.getLogElementSize(),
379                            ObjectModel.computeArrayHeaderSize(array),
380                            array.getTypeInformationBlock(),
381                            MemoryManager.pickAllocator(array),
382                            ObjectModel.getAlignment(array),
383                            ObjectModel.getOffsetForAlignment(array, false),
384                            site);
385  }
386
387  /**
388   * Allocate something like "new int[cnt]" or "new Foo[cnt]".
389   * @param numElements number of array elements
390   * @param logElementSize size in bytes of an array element, log base 2.
391   * @param headerSize size in bytes of array header
392   * @param tib type information block for array object
393   * @param allocator int that encodes which allocator should be used
394   * @param align the alignment requested; must be a power of 2.
395   * @param offset the offset at which the alignment is desired.
396   * @param site the site id of the calling allocation site
397   * @return array object with header installed and all elements set
398   *         to zero/null
399   * See also: bytecode 0xbc ("newarray") and 0xbd ("anewarray")
400   */
401  @Entrypoint
402  public static Object resolvedNewArray(int numElements, int logElementSize, int headerSize, TIB tib,
403                                        int allocator, int align, int offset, int site)
404      throws OutOfMemoryError, NegativeArraySizeException {
405
406    if (numElements < 0) raiseNegativeArraySizeException();
407
408    // GC stress testing
409    if (VM.ForceFrequentGC) checkAllocationCountDownToGC();
410
411    // Allocate the array and initialize its header
412    return MemoryManager.allocateArray(numElements, logElementSize, headerSize, tib, allocator, align, offset, site);
413  }
414
415  /**
416   * Clone a Scalar or Array Object.
417   * called from java/lang/Object.clone().
418   * <p>
419   * For simplicity, we just code this more or less in Java using
420   * internal reflective operations and some magic.
421   * This is inefficient for large scalar objects, but until that
422   * is proven to be a  performance problem, we won't worry about it.
423   * By keeping this in Java instead of dropping into Memory.copy,
424   * we avoid having to add special case code to deal with write barriers,
425   * and other such things.
426   * <p>
427   * This method calls specific cloning routines based on type to help
428   * guide the inliner (which won't inline a single large method).
429   *
430   * @param obj the object to clone
431   * @return the cloned object
432   * @throws CloneNotSupportedException when the object does not support cloning
433   */
434  public static Object clone(Object obj) throws OutOfMemoryError, CloneNotSupportedException {
435    RVMType type = Magic.getObjectType(obj);
436    if (type.isArrayType()) {
437      return cloneArray(obj, type);
438    } else {
439      return cloneClass(obj, type);
440    }
441  }
442
443  /**
444   * Clone an array
445   *
446   * @param obj the array to clone
447   * @param type the type information for the array
448   * @return the cloned object
449   */
450  private static Object cloneArray(Object obj, RVMType type) throws OutOfMemoryError {
451    RVMArray ary = type.asArray();
452    int nelts = ObjectModel.getArrayLength(obj);
453    Object newObj = resolvedNewArray(nelts, ary);
454    System.arraycopy(obj, 0, newObj, 0, nelts);
455    return newObj;
456  }
457
458  /**
459   * Clone an object implementing a class - check that the class is cloneable
460   * (we make this a small method with just a test so that the inliner will
461   * inline it and hopefully eliminate the instanceof test).
462   *
463   * @param obj the object to clone
464   * @param type the type information for the class
465   * @return the cloned object
466   * @throws CloneNotSupportedException when the object does not support cloning
467   */
468  private static Object cloneClass(Object obj, RVMType type) throws OutOfMemoryError, CloneNotSupportedException {
469    if (!(obj instanceof Cloneable)) {
470      throw new CloneNotSupportedException();
471    } else {
472      return cloneClass2(obj, type);
473    }
474  }
475
476  /**
477   * Clone an object implementing a class - the actual clone
478   *
479   * @param obj the object to clone
480   * @param type the type information for the class
481   * @return the cloned object
482   */
483  private static Object cloneClass2(Object obj, RVMType type) throws OutOfMemoryError {
484    RVMClass cls = type.asClass();
485    Object newObj = resolvedNewScalar(cls);
486    for (RVMField f : cls.getInstanceFields()) {
487      if (f.isReferenceType()) {
488        // Writing a reference
489        // Do via slower "VM-internal reflection" to enable
490        // collectors to do the right thing wrt reference counting
491        // and write barriers.
492        f.setObjectValueUnchecked(newObj, f.getObjectValueUnchecked(obj));
493      } else {
494        // Primitive type
495        // Check if we need to go via the slower barried path
496        TypeReference fieldType = f.getType();
497        if (Barriers.NEEDS_BOOLEAN_PUTFIELD_BARRIER && fieldType.isBooleanType()) {
498          f.setBooleanValueUnchecked(newObj, f.getBooleanValueUnchecked(obj));
499          continue;
500        } else if (Barriers.NEEDS_BYTE_PUTFIELD_BARRIER && fieldType.isByteType()) {
501          f.setByteValueUnchecked(newObj, f.getByteValueUnchecked(obj));
502          continue;
503        } else if (Barriers.NEEDS_CHAR_PUTFIELD_BARRIER && fieldType.isCharType()) {
504          f.setCharValueUnchecked(newObj, f.getCharValueUnchecked(obj));
505          continue;
506        } else if (Barriers.NEEDS_DOUBLE_PUTFIELD_BARRIER && fieldType.isDoubleType()) {
507          f.setDoubleValueUnchecked(newObj, f.getDoubleValueUnchecked(obj));
508          continue;
509        } else if (Barriers.NEEDS_FLOAT_PUTFIELD_BARRIER && fieldType.isFloatType()) {
510          f.setFloatValueUnchecked(newObj, f.getFloatValueUnchecked(obj));
511          continue;
512        } else if (Barriers.NEEDS_INT_PUTFIELD_BARRIER && fieldType.isIntType()) {
513          f.setIntValueUnchecked(newObj, f.getIntValueUnchecked(obj));
514          continue;
515        } else if (Barriers.NEEDS_LONG_PUTFIELD_BARRIER && fieldType.isLongType()) {
516          f.setLongValueUnchecked(newObj, f.getLongValueUnchecked(obj));
517          continue;
518        } else if (Barriers.NEEDS_SHORT_PUTFIELD_BARRIER && fieldType.isShortType()) {
519          f.setShortValueUnchecked(newObj, f.getShortValueUnchecked(obj));
520          continue;
521        } else if (Barriers.NEEDS_WORD_PUTFIELD_BARRIER && fieldType.isWordType()) {
522          f.setWordValueUnchecked(newObj, f.getWordValueUnchecked(obj));
523          continue;
524        } else if (Barriers.NEEDS_ADDRESS_PUTFIELD_BARRIER && fieldType.isAddressType()) {
525          f.setAddressValueUnchecked(newObj, f.getAddressValueUnchecked(obj));
526          continue;
527        } else if (Barriers.NEEDS_EXTENT_PUTFIELD_BARRIER && fieldType.isExtentType()) {
528          f.setExtentValueUnchecked(newObj, f.getExtentValueUnchecked(obj));
529          continue;
530        } else if (Barriers.NEEDS_OFFSET_PUTFIELD_BARRIER && fieldType.isOffsetType()) {
531          f.setOffsetValueUnchecked(newObj, f.getOffsetValueUnchecked(obj));
532          continue;
533        } else {
534          // Can perform raw copy of field
535          int size = f.getSize();
536          Offset offset = f.getOffset();
537          if (VM.BuildFor32Addr) {
538            // As per pre primitive write barrier code we test the most likely
539            // case first
540            if (size == BYTES_IN_INT) {
541              int bits = Magic.getIntAtOffset(obj, offset);
542              Magic.setIntAtOffset(newObj, offset, bits);
543              continue;
544            } else if (size == BYTES_IN_LONG) {
545              long bits = Magic.getLongAtOffset(obj, offset);
546              Magic.setLongAtOffset(newObj, offset, bits);
547              continue;
548            }
549          } else {
550            // BuildFor64Addr
551            // As per pre primitive write barrier code we test the most likely
552            // case first
553            if (size == BYTES_IN_LONG) {
554              long bits = Magic.getLongAtOffset(obj, offset);
555              Magic.setLongAtOffset(newObj, offset, bits);
556              continue;
557            } else if (size == BYTES_IN_INT) {
558              int bits = Magic.getIntAtOffset(obj, offset);
559              Magic.setIntAtOffset(newObj, offset, bits);
560              continue;
561            }
562          }
563          if (size == BYTES_IN_CHAR) {
564            char bits = Magic.getCharAtOffset(obj, offset);
565            Magic.setCharAtOffset(newObj, offset, bits);
566          } else {
567            if (VM.VerifyAssertions) VM._assert(size == BYTES_IN_BYTE);
568            byte bits = Magic.getByteAtOffset(obj, offset);
569            Magic.setByteAtOffset(newObj, offset, bits);
570          }
571        }
572      }
573    }
574    return newObj;
575  }
576
577  /**
578   * Helper function to actually throw the required exception.
579   * Keep out of line to mitigate code space when quickNewArray is inlined.
580   */
581  @NoInline
582  private static void raiseNegativeArraySizeException() throws NegativeArraySizeException {
583    throw new NegativeArraySizeException();
584  }
585
586  /**
587   * Get an object's "hashcode" value.
588   *
589   * Side effect: hash value is generated and stored into object's
590   * status word.
591   * @param object the object to hash
592   * @return object's hashcode.
593   * @see java.lang.Object#hashCode
594   */
595  public static int getObjectHashCode(Object object) {
596    return ObjectModel.getObjectHashCode(object);
597  }
598
599  //---------------------------------------------------------------//
600  //                        Dynamic linking.                       //
601  //---------------------------------------------------------------//
602
603  /**
604   * Prepare a class for use prior to first allocation,
605   * field access, or method invocation.
606   * Made public so that it is accessible from java.lang.reflect.*.
607   *
608   * @param cls the class to prepare for dynamic link
609   * @see MemberReference#needsDynamicLink
610   */
611  public static void initializeClassForDynamicLink(RVMClass cls) {
612    if (VM.TraceClassLoading) {
613      VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (begin) " + cls + "\n");
614    }
615
616    cls.resolve();
617    cls.instantiate();
618    cls.initialize();   // throws ExceptionInInitializerError
619
620    if (VM.TraceClassLoading) {
621      VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (end)   " + cls + "\n");
622    }
623  }
624
625  //---------------------------------------------------------------//
626  //                    Implementation Errors.                     //
627  //---------------------------------------------------------------//
628
629  /**
630   * Report unexpected method call: interface method
631   * (virtual machine dispatching error, shouldn't happen).
632   */
633  static void unexpectedInterfaceMethodCall() {
634    VM.sysFail("interface method dispatching error");
635  }
636
637  /**
638   * Report unexpected method call: abstract method (verification error).
639   */
640  @Entrypoint
641  static void unexpectedAbstractMethodCall() {
642    VM.sysWrite("RuntimeEntrypoints.unexpectedAbstractMethodCall\n");
643    throw new AbstractMethodError();
644  }
645
646  //---------------------------------------------------------------//
647  //                    Exception Handling.                        //
648  //---------------------------------------------------------------//
649
650  /**
651   * Deliver a software exception to current java thread.
652   * @param exceptionObject exception object to deliver
653   * (null --&gt; deliver NullPointerException).
654   * does not return
655   * (stack is unwound and execution resumes in a catch block)
656   *
657   * This method is public so that it can be invoked by java.lang.VMClass.
658   */
659  @NoInline
660  @Entrypoint
661  @Unpreemptible("Deliver exception possibly from unpreemptible code")
662  public static void athrow(Throwable exceptionObject) {
663    if (traceAthrow) {
664      VM.sysWriteln("in athrow.");
665      RVMThread.dumpStack();
666    }
667    RVMThread myThread = RVMThread.getCurrentThread();
668    AbstractRegisters exceptionRegisters = myThread.getExceptionRegisters();
669    VM.disableGC();              // VM.enableGC() is called when the exception is delivered.
670    Magic.saveThreadState(exceptionRegisters);
671    exceptionRegisters.setInUse(true);
672    deliverException(exceptionObject, exceptionRegisters);
673  }
674
675  /**
676   * Deliver a hardware exception to current java thread.
677   * <p>
678   * Does not return.
679   * (stack is unwound, starting at trap site, and
680   *           execution resumes in a catch block somewhere up the stack)
681   *     /or/  execution resumes at instruction following trap
682   *     (for TRAP_STACK_OVERFLOW)
683   *
684   * <p> Note:     Control reaches here by the actions of an
685   *           external "C" signal handler
686   *           which saves the register state of the trap site into the
687   *           "exceptionRegisters" field of the current
688   *           Thread object.
689   *           The signal handler also inserts a &lt;hardware trap&gt; frame
690   *           onto the stack immediately above this frame, for use by
691   *           HardwareTrapGCMapIterator during garbage collection.
692   *
693   * @param trapCode code indicating kind of exception that was trapped
694   * (see TRAP_xxx, above)
695   * @param trapInfo array subscript (for array bounds trap, only), marker
696   * (for stack overflow traps on PPC) or
697   */
698  @Entrypoint
699  @UnpreemptibleNoWarn
700  static void deliverHardwareException(int trapCode, Word trapInfo) {
701    if (VM.verboseSignalHandling) VM.sysWriteln("delivering hardware exception");
702    RVMThread myThread = RVMThread.getCurrentThread();
703    if (VM.verboseSignalHandling) VM.sysWriteln("we have a thread = ",Magic.objectAsAddress(myThread));
704    if (VM.verboseSignalHandling) VM.sysWriteln("it's in state = ",myThread.getExecStatus());
705    AbstractRegisters exceptionRegisters = myThread.getExceptionRegisters();
706    if (VM.verboseSignalHandling) VM.sysWriteln("we have exception registers = ",Magic.objectAsAddress(exceptionRegisters));
707
708    if ((trapCode == TRAP_STACK_OVERFLOW || trapCode == TRAP_JNI_STACK) &&
709        myThread.getStack().length < (StackFrameLayout.getMaxStackSize() >> LOG_BYTES_IN_ADDRESS) &&
710        !myThread.hasNativeStackFrame()) {
711      // expand stack by the size appropriate for normal or native frame
712      // and resume execution at successor to trap instruction
713      // (C trap handler has set register.ip to the instruction following the trap).
714      if (trapCode == TRAP_JNI_STACK) {
715        RVMThread.resizeCurrentStack(myThread.getStackLength() + StackFrameLayout.getJNIStackGrowthSize(), exceptionRegisters);
716      } else {
717        RVMThread.resizeCurrentStack(myThread.getStackLength() + StackFrameLayout.getStackGrowthSize(), exceptionRegisters);
718      }
719      if (VM.VerifyAssertions) VM._assert(exceptionRegisters.getInUse());
720      exceptionRegisters.setInUse(false);
721      Magic.restoreHardwareExceptionState(exceptionRegisters);
722
723      if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
724    }
725
726    // GC stress testing
727    if (canForceGC()) {
728      //VM.sysWrite("FORCING GC: in deliverHardwareException\n");
729      System.gc();
730    }
731
732    // Sanity checking.
733    // Hardware traps in uninterruptible code should be considered hard failures.
734    if (!VM.sysFailInProgress()) {
735      Address fp = exceptionRegisters.getInnermostFramePointer();
736      int compiledMethodId = Magic.getCompiledMethodID(fp);
737      if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) {
738        CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
739        Address ip = exceptionRegisters.getInnermostInstructionAddress();
740        Offset instructionOffset = compiledMethod.getInstructionOffset(ip);
741        if (compiledMethod.isWithinUninterruptibleCode(instructionOffset)) {
742          switch (trapCode) {
743          case TRAP_NULL_POINTER:
744            VM.sysWriteln("\nFatal error: NullPointerException within uninterruptible region.");
745            break;
746          case TRAP_ARRAY_BOUNDS:
747            VM.sysWriteln("\nFatal error: ArrayIndexOutOfBoundsException within uninterruptible region (index was ", trapInfo.toInt(), ").");
748            break;
749          case TRAP_DIVIDE_BY_ZERO:
750            VM.sysWriteln("\nFatal error: DivideByZero within uninterruptible region.");
751            break;
752          case TRAP_STACK_OVERFLOW:
753          case TRAP_JNI_STACK:
754            VM.sysWriteln("\nFatal error: StackOverflowError within uninterruptible region.");
755            break;
756          case TRAP_CHECKCAST:
757            VM.sysWriteln("\nFatal error: ClassCastException within uninterruptible region.");
758            break;
759          case TRAP_MUST_IMPLEMENT:
760            VM.sysWriteln("\nFatal error: IncompatibleClassChangeError within uninterruptible region.");
761            break;
762          case TRAP_STORE_CHECK:
763            VM.sysWriteln("\nFatal error: ArrayStoreException within uninterruptible region.");
764            break;
765          case TRAP_UNREACHABLE_BYTECODE:
766            VM.sysWriteln("\nFatal error: Reached a bytecode that was determined to be unreachable within uninterruptible region.");
767            break;
768          default:
769            VM.sysWriteln("\nFatal error: Unknown hardware trap within uninterruptible region.");
770          break;
771          }
772          VM.sysWriteln("trapCode = ", trapCode);
773          VM.sysWriteln("trapInfo = ", trapInfo.toAddress());
774          VM.sysFail("Exiting virtual machine due to uninterruptibility violation.");
775        }
776      }
777    }
778
779    Throwable exceptionObject;
780    switch (trapCode) {
781      case TRAP_NULL_POINTER:
782        exceptionObject = new java.lang.NullPointerException();
783        break;
784      case TRAP_ARRAY_BOUNDS:
785        exceptionObject = new java.lang.ArrayIndexOutOfBoundsException(trapInfo.toInt());
786        break;
787      case TRAP_DIVIDE_BY_ZERO:
788        exceptionObject = new java.lang.ArithmeticException();
789        break;
790      case TRAP_STACK_OVERFLOW:
791      case TRAP_JNI_STACK:
792        exceptionObject = new java.lang.StackOverflowError();
793        break;
794      case TRAP_CHECKCAST:
795        exceptionObject = new java.lang.ClassCastException();
796        break;
797      case TRAP_MUST_IMPLEMENT:
798        exceptionObject = new java.lang.IncompatibleClassChangeError();
799        break;
800      case TRAP_STORE_CHECK:
801        exceptionObject = new java.lang.ArrayStoreException();
802        break;
803      case TRAP_UNREACHABLE_BYTECODE:
804        exceptionObject = new java.lang.InternalError(UNREACHABLE_BC_MESSAGE);
805        break;
806      default:
807        exceptionObject = new java.lang.UnknownError();
808        RVMThread.traceback("UNKNOWN ERROR");
809        break;
810    }
811
812    VM.disableGC();  // VM.enableGC() is called when the exception is delivered.
813    deliverException(exceptionObject, exceptionRegisters);
814  }
815
816  /**
817   * Unlock an object and then deliver a software exception
818   * to current java thread.
819   * <p>
820   * Does not return (stack is unwound and execution resumes in a catch block).
821   *
822   * @param objToUnlock object to unlock
823   * @param objToThrow exception object to deliver
824   * ({@code null} --&gt; deliver NullPointerException).
825   */
826  @NoInline
827  @Entrypoint
828  static void unlockAndThrow(Object objToUnlock, Throwable objToThrow) {
829    ObjectModel.genericUnlock(objToUnlock);
830    athrow(objToThrow);
831  }
832
833  /**
834   * Create and throw a java.lang.ArrayIndexOutOfBoundsException.
835   * Only used in some configurations where it is easier to make a call
836   * then recover the array index from a trap instruction.
837   *
838   * @param index the failing index
839   */
840  @NoInline
841  @Entrypoint
842  static void raiseArrayIndexOutOfBoundsException(int index) {
843    throw new java.lang.ArrayIndexOutOfBoundsException(index);
844  }
845
846  /**
847   * Create and throw a java.lang.ArrayIndexOutOfBoundsException.
848   * Used (rarely) by the opt compiler when it has determined that
849   * an array access will unconditionally raise an array bounds check
850   * error, but it has lost track of exactly what the index is going to be.
851   */
852  @NoInline
853  static void raiseArrayIndexOutOfBoundsException() {
854    throw new java.lang.ArrayIndexOutOfBoundsException();
855  }
856
857  /**
858   * Create and throw a java.lang.NullPointerException.
859   * Used in a few circumstances to reduce code space costs
860   * of inlining (see java.lang.System.arraycopy()).  Could also
861   * be used to raise a null pointer exception without going through
862   * the hardware trap handler; currently this is only done when the
863   * opt compiler has determined that an instruction will unconditionally
864   * raise a null pointer exception.
865   */
866  @NoInline
867  @Entrypoint
868  public static void raiseNullPointerException() {
869    throw new java.lang.NullPointerException();
870  }
871
872  /**
873   * Create and throw a java.lang.ArrayStoreException.
874   * Used in a few circumstances to reduce code space costs
875   * of inlining (see java.lang.System.arraycopy()).
876   */
877  @NoInline
878  public static void raiseArrayStoreException() {
879    throw new java.lang.ArrayStoreException();
880  }
881
882  /**
883   * Create and throw a java.lang.ArithmeticException.
884   * Used to raise an arithmetic exception without going through
885   * the hardware trap handler; currently this is only done when the
886   * opt compiler has determined that an instruction will unconditionally
887   * raise an arithmetic exception.
888   */
889  @NoInline
890  @Entrypoint
891  static void raiseArithmeticException() {
892    throw new java.lang.ArithmeticException();
893  }
894
895  /**
896   * Create and throw a java.lang.AbstractMethodError.
897   * Used to handle error cases in invokeinterface dispatching.
898   */
899  @NoInline
900  @Entrypoint
901  static void raiseAbstractMethodError() {
902    throw new java.lang.AbstractMethodError();
903  }
904
905  /**
906   * Create and throw a java.lang.IllegalAccessError.
907   * Used to handle error cases in invokeinterface dispatching.
908   */
909  @NoInline
910  @Entrypoint
911  static void raiseIllegalAccessError() {
912    throw new java.lang.IllegalAccessError();
913  }
914
915  //----------------//
916  // implementation //
917  //----------------//
918
919  public static void init() {
920    // tell the bootloader (sysSignal*.c) to pass control to
921    // "RuntimeEntrypoints.deliverHardwareException()"
922    // whenever the host operating system detects a hardware trap.
923    //
924    BootRecord.the_boot_record.hardwareTrapMethodId = CompiledMethods.createHardwareTrapCompiledMethod().getId();
925    BootRecord.the_boot_record.deliverHardwareExceptionOffset =
926        Entrypoints.deliverHardwareExceptionMethod.getOffset();
927
928    // tell the bootloader (sysSignal.c) to set "RVMThread.debugRequested" flag
929    // whenever the host operating system detects a debug request signal
930    //
931    BootRecord.the_boot_record.debugRequestedOffset = Entrypoints.debugRequestedField.getOffset();
932  }
933
934  /**
935   * Build a multi-dimensional array.
936   * @param methodId  TODO document me
937   * @param numElements number of elements to allocate for each dimension
938   * @param arrayType type of array that will result
939   * @return array object
940   */
941  public static Object buildMultiDimensionalArray(int methodId, int[] numElements, RVMArray arrayType) {
942    RVMMethod method = MemberReference.getMethodRef(methodId).peekResolvedMethod();
943    if (VM.VerifyAssertions) VM._assert(method != null);
944    return buildMDAHelper(method, numElements, 0, arrayType);
945  }
946
947  /**
948   * Build a two-dimensional array.
949   * @param methodId  TODO document me
950   * @param dim0 the arraylength for arrays in dimension 0
951   * @param dim1 the arraylength for arrays in dimension 1
952   * @param arrayType type of array that will result
953   * @return array object
954   */
955  public static Object buildTwoDimensionalArray(int methodId, int dim0, int dim1, RVMArray arrayType) {
956    RVMMethod method = MemberReference.getMethodRef(methodId).peekResolvedMethod();
957    if (VM.VerifyAssertions) VM._assert(method != null);
958
959    if (!arrayType.isInstantiated()) {
960      arrayType.resolve();
961      arrayType.instantiate();
962    }
963
964    Object[] newArray = (Object[])resolvedNewArray(dim0, arrayType);
965
966    RVMArray innerArrayType = arrayType.getElementType().asArray();
967    if (!innerArrayType.isInstantiated()) {
968      innerArrayType.resolve();
969      innerArrayType.instantiate();
970    }
971
972    for (int i = 0; i < dim0; i++) {
973      newArray[i] = resolvedNewArray(dim1, innerArrayType);
974    }
975
976    return newArray;
977  }
978
979  /**
980   * @param method Apparently unused (!)
981   * @param numElements Number of elements to allocate for each dimension
982   * @param dimIndex Current dimension to build
983   * @param arrayType type of array that will result
984   * @return a multi-dimensional array
985   */
986  public static Object buildMDAHelper(RVMMethod method, int[] numElements, int dimIndex, RVMArray arrayType) {
987
988    if (!arrayType.isInstantiated()) {
989      arrayType.resolve();
990      arrayType.instantiate();
991    }
992
993    int nelts = numElements[dimIndex];
994    Object newObject = resolvedNewArray(nelts, arrayType);
995
996    if (++dimIndex == numElements.length) {
997      return newObject; // all dimensions have been built
998    }
999
1000    Object[] newArray = (Object[]) newObject;
1001    RVMArray newArrayType = arrayType.getElementType().asArray();
1002
1003    for (int i = 0; i < nelts; ++i) {
1004      newArray[i] = buildMDAHelper(method, numElements, dimIndex, newArrayType);
1005    }
1006
1007    return newArray;
1008  }
1009
1010  /**
1011   * Deliver an exception to current java thread.
1012   * <STRONG> Precondition: </STRONG> VM.disableGC has already been called.
1013   *  <ol>
1014   *   <li> exceptionRegisters may not match any reasonable stack
1015   *          frame at this point.
1016   *   <li> we're going to be playing with raw addresses (fp, ip).
1017   *  </ol>
1018   * <p>
1019   * Does not return:
1020   * <ul>
1021   *  <li> stack is unwound and execution resumes in a catch block
1022   *  <li> <em> or </em> current thread is terminated if no catch block is found
1023   * </ul>
1024
1025   * @param exceptionObject exception object to deliver
1026   * @param exceptionRegisters register state corresponding to exception site
1027   */
1028  @Unpreemptible("Deliver exception trying to avoid preemption")
1029  private static void deliverException(Throwable exceptionObject, AbstractRegisters exceptionRegisters) {
1030    if (VM.TraceExceptionDelivery) {
1031      VM.sysWriteln("RuntimeEntrypoints.deliverException() entered; just got an exception object.");
1032    }
1033    //VM.sysWriteln("throwing exception!");
1034    //RVMThread.dumpStack();
1035
1036    // walk stack and look for a catch block
1037    //
1038    if (VM.TraceExceptionDelivery) {
1039      VM.sysWrite("Hunting for a catch block...");
1040    }
1041    RVMType exceptionType = Magic.getObjectType(exceptionObject);
1042    Address fp = exceptionRegisters.getInnermostFramePointer();
1043    Address hijackedCalleeFp = RVMThread.getCurrentThread().getHijackedReturnCalleeFp();
1044    boolean leapfroggedReturnBarrier = false;
1045    if (VM.VerifyAssertions) VM._assert(hijackedCalleeFp.isZero() || hijackedCalleeFp.GE(fp));
1046    while (Magic.getCallerFramePointer(fp).NE(StackFrameLayout.getStackFrameSentinelFP())) {
1047      if (!hijackedCalleeFp.isZero() && hijackedCalleeFp.LE(fp)) {
1048        leapfroggedReturnBarrier = true;
1049      }
1050      int compiledMethodId = Magic.getCompiledMethodID(fp);
1051      if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) {
1052        CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
1053        ExceptionDeliverer exceptionDeliverer = compiledMethod.getExceptionDeliverer();
1054        Address ip = exceptionRegisters.getInnermostInstructionAddress();
1055        Offset ipOffset = compiledMethod.getInstructionOffset(ip);
1056        int catchBlockOffset = compiledMethod.findCatchBlockForInstruction(ipOffset, exceptionType);
1057
1058        if (catchBlockOffset >= 0) {
1059          // found an appropriate catch block
1060          if (VM.TraceExceptionDelivery) {
1061            VM.sysWriteln("found one; delivering.");
1062          }
1063          if (leapfroggedReturnBarrier) {
1064            RVMThread t = RVMThread.getCurrentThread();
1065            if (RVMThread.DEBUG_STACK_TRAMPOLINE) VM.sysWriteln("leapfrogged...");
1066            t.deInstallStackTrampoline();
1067          }
1068          Address catchBlockStart = compiledMethod.getInstructionAddress(Offset.fromIntSignExtend(catchBlockOffset));
1069          exceptionDeliverer.deliverException(compiledMethod, catchBlockStart, exceptionObject, exceptionRegisters);
1070          if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
1071        }
1072
1073        exceptionDeliverer.unwindStackFrame(compiledMethod, exceptionRegisters);
1074      } else {
1075        unwindInvisibleStackFrame(exceptionRegisters);
1076      }
1077      fp = exceptionRegisters.getInnermostFramePointer();
1078    }
1079
1080    if (VM.TraceExceptionDelivery) {
1081      VM.sysWriteln("Nope.");
1082      VM.sysWriteln("RuntimeEntrypoints.deliverException() found no catch block.");
1083    }
1084    /* No appropriate catch block found. */
1085    if (RVMThread.DEBUG_STACK_TRAMPOLINE && leapfroggedReturnBarrier) VM.sysWriteln("Leapfrogged, and unhandled!");
1086    handleUncaughtException(exceptionObject);
1087  }
1088
1089  @UnpreemptibleNoWarn("Uncaught exception handling that may cause preemption")
1090  private static void handleUncaughtException(Throwable exceptionObject) {
1091    RVMThread.getCurrentThread().handleUncaughtException(exceptionObject);
1092  }
1093
1094  /**
1095   * Skip over all frames below currfp with saved code pointers outside of heap
1096   * (C frames), stopping at the native frame immediately preceding the glue
1097   * frame which contains the method ID of the native method (this is necessary
1098   * to allow retrieving the return address of the glue frame).
1099   *
1100   * @param currfp The current frame is expected to be one of the JNI functions
1101   *            called from C, below which is one or more native stack frames
1102   * @return the frame pointer for the appropriate frame
1103   */
1104  @Uninterruptible
1105  public static Address unwindNativeStackFrame(Address currfp) {
1106    if (VM.BuildForIA32) {
1107      return currfp;
1108    }
1109    // Remembered address of previous FP
1110    Address callee_fp;
1111    // Address of native frame
1112    Address fp = Magic.getCallerFramePointer(currfp);
1113    // Instruction pointer for current native frame
1114    Address ip;
1115
1116    // Loop until either we fall off the stack or we find an instruction address
1117    // in one of our heaps
1118    do {
1119      callee_fp = fp;
1120      ip = Magic.getReturnAddressUnchecked(fp);
1121      fp = Magic.getCallerFramePointer(fp);
1122    } while (!MemoryManager.addressInVM(ip) && fp.NE(StackFrameLayout.getStackFrameSentinelFP()));
1123
1124    if (VM.BuildForPowerPC) {
1125      // We want to return fp, not callee_fp because we want the stack walkers
1126      // to see the "mini-frame" which has the RVM information, not the "main frame"
1127      // pointed to by callee_fp which is where the saved ip was actually stored.
1128      return fp;
1129    } else {
1130      return callee_fp;
1131    }
1132  }
1133
1134  /**
1135   * The current frame is expected to be one of the JNI functions
1136   * called from C,
1137   * below which is one or more native stack frames.
1138   * Skip over all frames below which do not contain any object
1139   * references.
1140   *
1141   * @param currfp the frame pointer of the current frame
1142   * @return the frame pointer for the appropriate frame
1143   */
1144  @Uninterruptible
1145  public static Address unwindNativeStackFrameForGC(Address currfp) {
1146     return unwindNativeStackFrame(currfp);
1147  }
1148
1149  /**
1150   * Unwind stack frame for an &lt;invisible method&gt;.
1151   * See also: ExceptionDeliverer.unwindStackFrame()
1152   * <p>
1153   * !!TODO: Could be a reflective method invoker frame.
1154   *        Does it clobber any non-volatiles?
1155   *        If so, how do we restore them?
1156   * (I don't think our current implementations of reflective method
1157   *  invokers save/restore any nonvolatiles, so we're probably ok.
1158   *  --dave 6/29/01
1159   *
1160   *  @param registers exception registers
1161   */
1162  @Uninterruptible
1163  private static void unwindInvisibleStackFrame(AbstractRegisters registers) {
1164    registers.unwindStackFrame();
1165  }
1166
1167  /**
1168   * Number of allocations left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0.
1169   */
1170  static int allocationCountDownToGC = VM.StressGCAllocationInterval;
1171
1172  /**
1173   * Number of c-to-java jni calls left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0.
1174   */
1175  static int jniCountDownToGC = VM.StressGCAllocationInterval;
1176
1177  /**
1178   * Check to see if we are stress testing garbage collector and if another JNI call should
1179   * trigger a gc then do so.
1180   */
1181  @Inline
1182  public static void checkJNICountDownToGC() {
1183    // Temporarily disabled as it will causes nightly to take too long to run
1184    // There should be a mechanism to optionally enable this countdown in Configuration
1185    if (false && canForceGC()) {
1186      if (jniCountDownToGC-- <= 0) {
1187        jniCountDownToGC = VM.StressGCAllocationInterval;
1188        System.gc();
1189      }
1190    }
1191  }
1192
1193  /**
1194   * Check to see if we are stress testing garbage collector and if another allocation should
1195   * trigger a GC then do so.
1196   */
1197  @Inline
1198  private static void checkAllocationCountDownToGC() {
1199    if (canForceGC()) {
1200      if (allocationCountDownToGC-- <= 0) {
1201        allocationCountDownToGC = VM.StressGCAllocationInterval;
1202        System.gc();
1203      }
1204    }
1205  }
1206
1207  /**
1208   * @return {@code true} if we are stress testing garbage collector and the
1209   *  system is in state where we can force a garbage collection.
1210   */
1211  @Inline
1212  @Uninterruptible
1213  private static boolean canForceGC() {
1214    return VM.ForceFrequentGC && RVMThread.safeToForceGCs() && MemoryManager.collectionEnabled();
1215  }
1216}