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