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 *
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 *
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)
562 VM._assert(size == BYTES_IN_BYTE);
563 byte bits = Magic.getByteAtOffset(obj, offset);
564 Magic.setByteAtOffset(newObj, offset, bits);
565 }
566 }
567 }
568 }
569 return newObj;
570 }
571
572 /**
573 * Helper function to actually throw the required exception.
574 * Keep out of line to mitigate code space when quickNewArray is inlined.
575 */
576 @NoInline
577 private static void raiseNegativeArraySizeException() throws NegativeArraySizeException {
578 throw new NegativeArraySizeException();
579 }
580
581 /**
582 * Get an object's "hashcode" value.
583 *
584 * Side effect: hash value is generated and stored into object's
585 * status word.
586 *
587 * @return object's hashcode.
588 * @see java.lang.Object#hashCode().
589 */
590 public static int getObjectHashCode(Object object) {
591 return ObjectModel.getObjectHashCode(object);
592 }
593
594 //---------------------------------------------------------------//
595 // Dynamic linking. //
596 //---------------------------------------------------------------//
597
598 /**
599 * Prepare a class for use prior to first allocation,
600 * field access, or method invocation.
601 * Made public so that it is accessible from java.lang.reflect.*.
602 * @see MemberReference#needsDynamicLink
603 */
604 public static void initializeClassForDynamicLink(RVMClass cls) {
605 if (VM.TraceClassLoading) {
606 VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (begin) " + cls + "\n");
607 }
608
609 cls.resolve();
610 cls.instantiate();
611 cls.initialize(); // throws ExceptionInInitializerError
612
613 if (VM.TraceClassLoading) {
614 VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (end) " + cls + "\n");
615 }
616 }
617
618 //---------------------------------------------------------------//
619 // Implementation Errors. //
620 //---------------------------------------------------------------//
621
622 /**
623 * Report unexpected method call: interface method
624 * (virtual machine dispatching error, shouldn't happen).
625 */
626 static void unexpectedInterfaceMethodCall() {
627 VM.sysFail("interface method dispatching error");
628 }
629
630 /**
631 * Report unexpected method call: abstract method (verification error).
632 */
633 @Entrypoint
634 static void unexpectedAbstractMethodCall() {
635 VM.sysWrite("RuntimeEntrypoints.unexpectedAbstractMethodCall\n");
636 throw new AbstractMethodError();
637 }
638
639 //---------------------------------------------------------------//
640 // Exception Handling. //
641 //---------------------------------------------------------------//
642
643 /**
644 * Deliver a software exception to current java thread.
645 * @param exceptionObject exception object to deliver
646 * (null --> deliver NullPointerException).
647 * does not return
648 * (stack is unwound and execution resumes in a catch block)
649 *
650 * This method is public so that it can be invoked by java.lang.VMClass.
651 */
652 @NoInline
653 @Entrypoint
654 @Unpreemptible("Deliver exception possibly from unpreemptible code")
655 public static void athrow(Throwable exceptionObject) {
656 if (traceAthrow) {
657 VM.sysWriteln("in athrow.");
658 RVMThread.dumpStack();
659 }
660 RVMThread myThread = RVMThread.getCurrentThread();
661 Registers exceptionRegisters = myThread.getExceptionRegisters();
662 VM.disableGC(); // VM.enableGC() is called when the exception is delivered.
663 Magic.saveThreadState(exceptionRegisters);
664 exceptionRegisters.inuse = true;
665 deliverException(exceptionObject, exceptionRegisters);
666 }
667
668 /**
669 * Deliver a hardware exception to current java thread.
670 * @param trapCode code indicating kind of exception that was trapped
671 * (see TRAP_xxx, above)
672 * @param trapInfo array subscript (for array bounds trap, only)
673 * does not return
674 * (stack is unwound, starting at trap site, and
675 * execution resumes in a catch block somewhere up the stack)
676 * /or/ execution resumes at instruction following trap
677 * (for TRAP_STACK_OVERFLOW)
678 *
679 * <p> Note: Control reaches here by the actions of an
680 * external "C" signal handler
681 * which saves the register state of the trap site into the
682 * "exceptionRegisters" field of the current
683 * Thread object.
684 * The signal handler also inserts a <hardware trap> frame
685 * onto the stack immediately above this frame, for use by
686 * HardwareTrapGCMapIterator during garbage collection.
687 */
688 @Entrypoint
689 @UnpreemptibleNoWarn
690 static void deliverHardwareException(int trapCode, int trapInfo) {
691 if (false) VM.sysWriteln("delivering hardware exception");
692 RVMThread myThread = RVMThread.getCurrentThread();
693 if (false) VM.sysWriteln("we have a thread = ",Magic.objectAsAddress(myThread));
694 if (false) VM.sysWriteln("it's in state = ",myThread.getExecStatus());
695 Registers exceptionRegisters = myThread.getExceptionRegisters();
696 if (false) VM.sysWriteln("we have exception registers = ",Magic.objectAsAddress(exceptionRegisters));
697
698 if ((trapCode == TRAP_STACK_OVERFLOW || trapCode == TRAP_JNI_STACK) &&
699 myThread.getStack().length < (STACK_SIZE_MAX >> LOG_BYTES_IN_ADDRESS) &&
700 !myThread.hasNativeStackFrame()) {
701 // expand stack by the size appropriate for normal or native frame
702 // and resume execution at successor to trap instruction
703 // (C trap handler has set register.ip to the instruction following the trap).
704 if (trapCode == TRAP_JNI_STACK) {
705 RVMThread.resizeCurrentStack(myThread.getStackLength() + STACK_SIZE_JNINATIVE_GROW, exceptionRegisters);
706 } else {
707 RVMThread.resizeCurrentStack(myThread.getStackLength() + STACK_SIZE_GROW, exceptionRegisters);
708 }
709 if (VM.VerifyAssertions) VM._assert(exceptionRegisters.inuse);
710 exceptionRegisters.inuse = false;
711 Magic.restoreHardwareExceptionState(exceptionRegisters);
712
713 if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
714 }
715
716 // GC stress testing
717 if (canForceGC()) {
718 //VM.sysWrite("FORCING GC: in deliverHardwareException\n");
719 System.gc();
720 }
721
722 // Sanity checking.
723 // Hardware traps in uninterruptible code should be considered hard failures.
724 if (!VM.sysFailInProgress()) {
725 Address fp = exceptionRegisters.getInnermostFramePointer();
726 int compiledMethodId = Magic.getCompiledMethodID(fp);
727 if (compiledMethodId != INVISIBLE_METHOD_ID) {
728 CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
729 Address ip = exceptionRegisters.getInnermostInstructionAddress();
730 Offset instructionOffset = compiledMethod.getInstructionOffset(ip);
731 if (compiledMethod.isWithinUninterruptibleCode(instructionOffset)) {
732 switch (trapCode) {
733 case TRAP_NULL_POINTER:
734 VM.sysWriteln("\nFatal error: NullPointerException within uninterruptible region.");
735 break;
736 case TRAP_ARRAY_BOUNDS:
737 VM.sysWriteln("\nFatal error: ArrayIndexOutOfBoundsException within uninterruptible region (index was ", trapInfo, ").");
738 break;
739 case TRAP_DIVIDE_BY_ZERO:
740 VM.sysWriteln("\nFatal error: DivideByZero within uninterruptible region.");
741 break;
742 case TRAP_STACK_OVERFLOW:
743 case TRAP_JNI_STACK:
744 VM.sysWriteln("\nFatal error: StackOverflowError within uninterruptible region.");
745 break;
746 case TRAP_CHECKCAST:
747 VM.sysWriteln("\nFatal error: ClassCastException within uninterruptible region.");
748 break;
749 case TRAP_MUST_IMPLEMENT:
750 VM.sysWriteln("\nFatal error: IncompatibleClassChangeError within uninterruptible region.");
751 break;
752 case TRAP_STORE_CHECK:
753 VM.sysWriteln("\nFatal error: ArrayStoreException within uninterruptible region.");
754 break;
755 default:
756 VM.sysWriteln("\nFatal error: Unknown hardware trap within uninterruptible region.");
757 break;
758 }
759 VM.sysFail("Exiting virtual machine due to uninterruptibility violation.");
760 }
761 }
762 }
763
764 Throwable exceptionObject;
765 switch (trapCode) {
766 case TRAP_NULL_POINTER:
767 exceptionObject = new java.lang.NullPointerException();
768 break;
769 case TRAP_ARRAY_BOUNDS:
770 exceptionObject = new java.lang.ArrayIndexOutOfBoundsException(trapInfo);
771 break;
772 case TRAP_DIVIDE_BY_ZERO:
773 exceptionObject = new java.lang.ArithmeticException();
774 break;
775 case TRAP_STACK_OVERFLOW:
776 case TRAP_JNI_STACK:
777 exceptionObject = new java.lang.StackOverflowError();
778 break;
779 case TRAP_CHECKCAST:
780 exceptionObject = new java.lang.ClassCastException();
781 break;
782 case TRAP_MUST_IMPLEMENT:
783 exceptionObject = new java.lang.IncompatibleClassChangeError();
784 break;
785 case TRAP_STORE_CHECK:
786 exceptionObject = new java.lang.ArrayStoreException();
787 break;
788 default:
789 exceptionObject = new java.lang.UnknownError();
790 RVMThread.traceback("UNKNOWN ERROR");
791 break;
792 }
793
794 VM.disableGC(); // VM.enableGC() is called when the exception is delivered.
795 deliverException(exceptionObject, exceptionRegisters);
796 }
797
798 /**
799 * Unlock an object and then deliver a software exception
800 * to current java thread.
801 * @param objToUnlock object to unlock
802 * @param objToThrow exception object to deliver
803 * (null --> deliver NullPointerException).
804 * does not return (stack is unwound and execution resumes in a catch block)
805 */
806 @NoInline
807 @Entrypoint
808 static void unlockAndThrow(Object objToUnlock, Throwable objToThrow) {
809 ObjectModel.genericUnlock(objToUnlock);
810 athrow(objToThrow);
811 }
812
813 /**
814 * Create and throw a java.lang.ArrayIndexOutOfBoundsException
815 * Only used in some configurations where it is easier to make a call
816 * then recover the array index from a trap instruction.
817 */
818 @NoInline
819 @Entrypoint
820 static void raiseArrayIndexOutOfBoundsException(int index) {
821 throw new java.lang.ArrayIndexOutOfBoundsException(index);
822 }
823
824 /**
825 * Create and throw a java.lang.ArrayIndexOutOfBoundsException
826 * Used (rarely) by the opt compiler when it has determined that
827 * an array access will unconditionally raise an array bounds check
828 * error, but it has lost track of exactly what the index is going to be.
829 */
830 @NoInline
831 static void raiseArrayIndexOutOfBoundsException() {
832 throw new java.lang.ArrayIndexOutOfBoundsException();
833 }
834
835 /**
836 * Create and throw a java.lang.NullPointerException
837 * Used in a few circumstances to reduce code space costs
838 * of inlining (see java.lang.System.arraycopy()). Could also
839 * be used to raise a null pointer exception without going through
840 * the hardware trap handler; currently this is only done when the
841 * opt compiler has determined that an instruction will unconditionally
842 * raise a null pointer exception.
843 */
844 @NoInline
845 @Entrypoint
846 public static void raiseNullPointerException() {
847 throw new java.lang.NullPointerException();
848 }
849
850 /**
851 * Create and throw a java.lang.ArrayStoreException
852 * Used in a few circumstances to reduce code space costs
853 * of inlining (see java.lang.System.arraycopy()).
854 */
855 @NoInline
856 public static void raiseArrayStoreException() {
857 throw new java.lang.ArrayStoreException();
858 }
859
860 /**
861 * Create and throw a java.lang.ArithmeticException
862 * Used to raise an arithmetic exception without going through
863 * the hardware trap handler; currently this is only done when the
864 * opt compiler has determined that an instruction will unconditionally
865 * raise an arithmetic exception.
866 */
867 @NoInline
868 @Entrypoint
869 static void raiseArithmeticException() {
870 throw new java.lang.ArithmeticException();
871 }
872
873 /**
874 * Create and throw a java.lang.AbstractMethodError.
875 * Used to handle error cases in invokeinterface dispatching.
876 */
877 @NoInline
878 @Entrypoint
879 static void raiseAbstractMethodError() {
880 throw new java.lang.AbstractMethodError();
881 }
882
883 /**
884 * Create and throw a java.lang.IllegalAccessError.
885 * Used to handle error cases in invokeinterface dispatching.
886 */
887 @NoInline
888 @Entrypoint
889 static void raiseIllegalAccessError() {
890 throw new java.lang.IllegalAccessError();
891 }
892
893 //----------------//
894 // implementation //
895 //----------------//
896
897 public static void init() {
898 // tell "RunBootImage.C" to pass control to
899 // "RuntimeEntrypoints.deliverHardwareException()"
900 // whenever the host operating system detects a hardware trap
901 //
902 BootRecord.the_boot_record.hardwareTrapMethodId = CompiledMethods.createHardwareTrapCompiledMethod().getId();
903 BootRecord.the_boot_record.deliverHardwareExceptionOffset =
904 Entrypoints.deliverHardwareExceptionMethod.getOffset();
905
906 // tell "RunBootImage.C" to set "RVMThread.debugRequested" flag
907 // whenever the host operating system detects a debug request signal
908 //
909 BootRecord.the_boot_record.debugRequestedOffset = Entrypoints.debugRequestedField.getOffset();
910 }
911
912 /**
913 * Build a multi-dimensional array.
914 * @param methodId Apparently unused (!)
915 * @param numElements number of elements to allocate for each dimension
916 * @param arrayType type of array that will result
917 * @return array object
918 */
919 public static Object buildMultiDimensionalArray(int methodId, int[] numElements, RVMArray arrayType) {
920 RVMMethod method = MemberReference.getMemberRef(methodId).asMethodReference().peekResolvedMethod();
921 if (VM.VerifyAssertions) VM._assert(method != null);
922 return buildMDAHelper(method, numElements, 0, arrayType);
923 }
924
925 /**
926 * Build a two-dimensional array.
927 * @param methodId Apparently unused (!)
928 * @param dim0 the arraylength for arrays in dimension 0
929 * @param dim1 the arraylength for arrays in dimension 1
930 * @param arrayType type of array that will result
931 * @return array object
932 */
933 public static Object buildTwoDimensionalArray(int methodId, int dim0, int dim1, RVMArray arrayType) {
934 RVMMethod method = MemberReference.getMemberRef(methodId).asMethodReference().peekResolvedMethod();
935 if (VM.VerifyAssertions) VM._assert(method != null);
936
937 if (!arrayType.isInstantiated()) {
938 arrayType.resolve();
939 arrayType.instantiate();
940 }
941
942 Object[] newArray = (Object[])resolvedNewArray(dim0, arrayType);
943
944 RVMArray innerArrayType = arrayType.getElementType().asArray();
945 if (!innerArrayType.isInstantiated()) {
946 innerArrayType.resolve();
947 innerArrayType.instantiate();
948 }
949
950 for (int i=0; i<dim0; i++) {
951 newArray[i] = resolvedNewArray(dim1, innerArrayType);
952 }
953
954 return newArray;
955 }
956
957 /**
958 * @param method Apparently unused (!)
959 * @param numElements Number of elements to allocate for each dimension
960 * @param dimIndex Current dimension to build
961 * @param arrayType type of array that will result
962 */
963 public static Object buildMDAHelper(RVMMethod method, int[] numElements, int dimIndex, RVMArray arrayType) {
964
965 if (!arrayType.isInstantiated()) {
966 arrayType.resolve();
967 arrayType.instantiate();
968 }
969
970 int nelts = numElements[dimIndex];
971 Object newObject = resolvedNewArray(nelts, arrayType);
972
973 if (++dimIndex == numElements.length) {
974 return newObject; // all dimensions have been built
975 }
976
977 Object[] newArray = (Object[]) newObject;
978 RVMArray newArrayType = arrayType.getElementType().asArray();
979
980 for (int i = 0; i < nelts; ++i) {
981 newArray[i] = buildMDAHelper(method, numElements, dimIndex, newArrayType);
982 }
983
984 return newArray;
985 }
986
987 /**
988 * Deliver an exception to current java thread.
989 * <STRONG> Precondition: </STRONG> VM.disableGC has already been called.
990 * <ol>
991 * <li> exceptionRegisters may not match any reasonable stack
992 * frame at this point.
993 * <li> we're going to be playing with raw addresses (fp, ip).
994 * </ol>
995 * @param exceptionObject exception object to deliver
996 * @param exceptionRegisters register state corresponding to exception site
997 * does not return
998 * <ul>
999 * <li> stack is unwound and execution resumes in a catch block
1000 * <li> <em> or </em> current thread is terminated if no catch block is found
1001 * </ul>
1002 */
1003 @Unpreemptible("Deliver exception trying to avoid preemption")
1004 private static void deliverException(Throwable exceptionObject, Registers exceptionRegisters) {
1005 if (VM.TraceExceptionDelivery) {
1006 VM.sysWriteln("RuntimeEntrypoints.deliverException() entered; just got an exception object.");
1007 }
1008 //VM.sysWriteln("throwing exception!");
1009 //RVMThread.dumpStack();
1010
1011 // walk stack and look for a catch block
1012 //
1013 if (VM.TraceExceptionDelivery) {
1014 VM.sysWrite("Hunting for a catch block...");
1015 }
1016 RVMType exceptionType = Magic.getObjectType(exceptionObject);
1017 Address fp = exceptionRegisters.getInnermostFramePointer();
1018 while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) {
1019 int compiledMethodId = Magic.getCompiledMethodID(fp);
1020 if (compiledMethodId != INVISIBLE_METHOD_ID) {
1021 CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
1022 ExceptionDeliverer exceptionDeliverer = compiledMethod.getExceptionDeliverer();
1023 Address ip = exceptionRegisters.getInnermostInstructionAddress();
1024 Offset ipOffset = compiledMethod.getInstructionOffset(ip);
1025 int catchBlockOffset = compiledMethod.findCatchBlockForInstruction(ipOffset, exceptionType);
1026
1027 if (catchBlockOffset >= 0) {
1028 // found an appropriate catch block
1029 if (VM.TraceExceptionDelivery) {
1030 VM.sysWriteln("found one; delivering.");
1031 }
1032 Address catchBlockStart = compiledMethod.getInstructionAddress(Offset.fromIntSignExtend(catchBlockOffset));
1033 exceptionDeliverer.deliverException(compiledMethod, catchBlockStart, exceptionObject, exceptionRegisters);
1034 if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
1035 }
1036
1037 exceptionDeliverer.unwindStackFrame(compiledMethod, exceptionRegisters);
1038 } else {
1039 unwindInvisibleStackFrame(exceptionRegisters);
1040 }
1041 fp = exceptionRegisters.getInnermostFramePointer();
1042 }
1043
1044 if (VM.TraceExceptionDelivery) {
1045 VM.sysWriteln("Nope.");
1046 VM.sysWriteln("RuntimeEntrypoints.deliverException() found no catch block.");
1047 }
1048 /* No appropriate catch block found. */
1049
1050 handleUncaughtException(exceptionObject);
1051 }
1052
1053 @UnpreemptibleNoWarn("Uncaught exception handling that may cause preemption")
1054 private static void handleUncaughtException(Throwable exceptionObject) {
1055 RVMThread.getCurrentThread().handleUncaughtException(exceptionObject);
1056 }
1057
1058 /**
1059 * Skip over all frames below currfp with saved code pointers outside of heap
1060 * (C frames), stopping at the native frame immediately preceding the glue
1061 * frame which contains the method ID of the native method (this is necessary
1062 * to allow retrieving the return address of the glue frame)
1063 *
1064 * @param currfp The current frame is expected to be one of the JNI functions
1065 * called from C, below which is one or more native stack frames
1066 */
1067 @Uninterruptible
1068 public static Address unwindNativeStackFrame(Address currfp) {
1069 if (VM.BuildForIA32) {
1070 return currfp;
1071 }
1072 // Remembered address of previous FP
1073 Address callee_fp;
1074 // Address of native frame
1075 Address fp = Magic.getCallerFramePointer(currfp);
1076 // Instruction pointer for current native frame
1077 Address ip;
1078
1079 // Loop until either we fall off the stack or we find an instruction address
1080 // in one of our heaps
1081 do {
1082 callee_fp = fp;
1083 ip = Magic.getReturnAddress(fp);
1084 fp = Magic.getCallerFramePointer(fp);
1085 } while (!MemoryManager.addressInVM(ip) && fp.NE(STACKFRAME_SENTINEL_FP));
1086
1087 if (VM.BuildForPowerPC) {
1088 // We want to return fp, not callee_fp because we want the stack walkers
1089 // to see the "mini-frame" which has the RVM information, not the "main frame"
1090 // pointed to by callee_fp which is where the saved ip was actually stored.
1091 return fp;
1092 } else {
1093 return callee_fp;
1094 }
1095 }
1096
1097 /**
1098 * The current frame is expected to be one of the JNI functions
1099 * called from C,
1100 * below which is one or more native stack frames
1101 * Skip over all frames below which do not contain any object
1102 * references.
1103 */
1104 @Uninterruptible
1105 public static Address unwindNativeStackFrameForGC(Address currfp) {
1106 return unwindNativeStackFrame(currfp);
1107 }
1108
1109 /**
1110 * Unwind stack frame for an <invisible method>.
1111 * See also: ExceptionDeliverer.unwindStackFrame()
1112 *
1113 * !!TODO: Could be a reflective method invoker frame.
1114 * Does it clobber any non-volatiles?
1115 * If so, how do we restore them?
1116 * (I don't think our current implementations of reflective method
1117 * invokers save/restore any nonvolatiles, so we're probably ok.
1118 * --dave 6/29/01
1119 */
1120 @Uninterruptible
1121 private static void unwindInvisibleStackFrame(Registers registers) {
1122 registers.unwindStackFrame();
1123 }
1124
1125 /**
1126 * Number of allocations left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0.
1127 */
1128 static int allocationCountDownToGC = VM.StressGCAllocationInterval;
1129
1130 /**
1131 * Number of c-to-java jni calls left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0.
1132 */
1133 static int jniCountDownToGC = VM.StressGCAllocationInterval;
1134
1135 /**
1136 * Check to see if we are stress testing garbage collector and if another JNI call should
1137 * trigger a gc then do so.
1138 */
1139 @Inline
1140 public static void checkJNICountDownToGC() {
1141 // Temporarily disabled as it will causes nightly to take too long to run
1142 // There should be a mechanism to optionally enable this countdown in Configuration
1143 if (false && canForceGC()) {
1144 if (jniCountDownToGC-- <= 0) {
1145 jniCountDownToGC = VM.StressGCAllocationInterval;
1146 System.gc();
1147 }
1148 }
1149 }
1150
1151 /**
1152 * Check to see if we are stress testing garbage collector and if another allocation should
1153 * trigger a gc then do so.
1154 */
1155 @Inline
1156 private static void checkAllocationCountDownToGC() {
1157 if (canForceGC()) {
1158 if (allocationCountDownToGC-- <= 0) {
1159 allocationCountDownToGC = VM.StressGCAllocationInterval;
1160 System.gc();
1161 }
1162 }
1163 }
1164
1165 /**
1166 * Return true if we are stress testing garbage collector and the system is in state where we
1167 * can force a garbage collection.
1168 */
1169 @Inline
1170 @Uninterruptible
1171 private static boolean canForceGC() {
1172 return VM.ForceFrequentGC && RVMThread.safeToForceGCs();
1173 }
1174 }