/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.runtime; import static com.sun.max.vm.runtime.VMRegister.*; import static com.sun.max.vm.runtime.VmOperation.*; import static com.sun.max.vm.stack.JavaFrameAnchor.*; import static com.sun.max.vm.thread.VmThread.*; import static com.sun.max.vm.thread.VmThreadLocal.*; import java.lang.reflect.*; import com.oracle.max.cri.intrinsics.*; import com.sun.cri.bytecode.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.object.*; import com.sun.max.vm.profile.*; import com.sun.max.vm.runtime.ResolutionGuard.InAccessingClass; import com.sun.max.vm.runtime.ResolutionGuard.InPool; import com.sun.max.vm.stack.*; import com.sun.max.vm.thread.*; /** * Runtime routines to be used for translating/implementing bytecodes. * * A compiler could preprocess some/all of these routines into its own * IR for direct use during translation (as opposed to making runtime * calls). * * History: These routines are a collection of some of the routines * previously encapsulated in subclasses of the Snippet class. */ public class Snippets { @INLINE public static Object createTupleOrHybrid(ClassActor classActor) { if (MaxineVM.isHosted()) { try { return ObjectUtils.allocateInstance(classActor.toJava()); } catch (InstantiationException instantiationException) { throw ProgramError.unexpected(instantiationException); } } if (classActor.isHybridClass()) { return Heap.createHybrid(classActor.dynamicHub()); } return Heap.createTuple(classActor.dynamicHub()); } @INLINE public static Object createArray(ClassActor arrayClassActor, int length) { if (length < 0) { throw Throw.negativeArraySizeException(length); } if (MaxineVM.isHosted()) { return Array.newInstance(arrayClassActor.componentClassActor().toJava(), length); } return Heap.createArray(arrayClassActor.dynamicHub(), length); } public static Object createMultiReferenceArray(ClassActor classActor, int[] lengths) { if (!classActor.isArrayClass()) { throw new VerifyError("MULTIANEWARRAY cannot be applied to non-array type " + classActor); } return createMultiReferenceArrayAtIndex(0, classActor, lengths); } private static Object createMultiReferenceArrayAtIndex(int index, ClassActor arrayClassActor, int[] lengths) { final int length = lengths[index]; final Object result = createNonNegativeSizeArray(arrayClassActor, length); if (length > 0) { final int nextIndex = index + 1; if (nextIndex < lengths.length) { final ClassActor subArrayClassActor = arrayClassActor.componentClassActor(); for (int i = 0; i < length; i++) { final Object subArray = createMultiReferenceArrayAtIndex(nextIndex, subArrayClassActor, lengths); if (MaxineVM.isHosted()) { final Object[] array = (Object[]) result; array[i] = subArray; } else { ArrayAccess.setObject(result, i, subArray); } } } } return result; } @INLINE static Object createNonNegativeSizeArray(ClassActor arrayClassActor, int length) { if (MaxineVM.isHosted()) { return Array.newInstance(arrayClassActor.componentClassActor().toJava(), length); } return Heap.createArray(arrayClassActor.dynamicHub(), length); } @INLINE public static Word selectNonPrivateVirtualMethod(Object receiver, VirtualMethodActor declaredMethod) { final Hub hub = ObjectAccess.readHub(receiver); return hub.getWord(declaredMethod.vTableIndex()); } @INLINE public static Word selectInterfaceMethod(Object receiver, InterfaceMethodActor interfaceMethod) { final Hub hub = ObjectAccess.readHub(receiver); final InterfaceActor interfaceActor = UnsafeCast.asInterfaceActor(interfaceMethod.holder()); final int interfaceIndex = hub.getITableIndex(interfaceActor.id); return hub.getWord(interfaceIndex + interfaceMethod.iIndexInInterface()); } @INLINE public static Address selectInterfaceMethod(Object receiver, InterfaceMethodActor interfaceMethodActor, MethodProfile mpo, int mpoIndex) { Hub hub = ObjectAccess.readHub(receiver); Address entryPoint = selectInterfaceMethod(receiver, interfaceMethodActor).asAddress(); MethodInstrumentation.recordType(mpo, hub, mpoIndex, MethodInstrumentation.DEFAULT_RECEIVER_METHOD_PROFILE_ENTRIES); return entryPoint; } @NEVER_INLINE private static void resolveStaticFieldForReading0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; final FieldActor fieldActor = constantPool.fieldAt(index).resolve(constantPool, index); if (!fieldActor.isStatic()) { throw new IncompatibleClassChangeError(); } guard.value = fieldActor; } /** * Resolves a constant pool entry denoting a static field that is being read. * * This snippet is used when translating the {@link Bytecodes#GETSTATIC} instruction. */ @INLINE public static FieldActor resolveStaticFieldForReading(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveStaticFieldForReading0(guard); } return UnsafeCast.asFieldActor(guard.value); } @NEVER_INLINE private static void resolveStaticFieldForWriting0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; final FieldActor fieldActor = constantPool.fieldAt(index).resolve(constantPool, index); if (!fieldActor.isStatic()) { throw new IncompatibleClassChangeError(); } if (fieldActor.isFinal() && fieldActor.holder() != constantPool.holder()) { throw new IllegalAccessError(); } guard.value = fieldActor; } /** * Resolves a constant pool entry denoting a static field that is being written to. * * This snippet is used when translating the {@link Bytecodes#PUTSTATIC} instruction. */ @INLINE public static FieldActor resolveStaticFieldForWriting(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveStaticFieldForWriting0(guard); } return UnsafeCast.asFieldActor(guard.value); } @NEVER_INLINE public static void resolveClass0(ResolutionGuard guard) { final ClassActor classActor; if (guard instanceof InPool) { InPool guardInPool = (InPool) guard; final ConstantPool constantPool = guardInPool.pool; final int index = guardInPool.cpi; classActor = constantPool.classAt(index).resolve(constantPool, index); } else { InAccessingClass guardInClass = (InAccessingClass) guard; classActor = guardInClass.resolve(); } guard.value = classActor; } /** * Resolves a constant pool entry denoting a class that will be resolved to any arbitrary class type. That is, the * entry can denote an array type, interface or normal class. * * This snippet is used when translating the {@link Bytecodes#INSTANCEOF}, {@link Bytecodes#CHECKCAST}, * {@link Bytecodes#LDC} and {@link Bytecodes#MULTIANEWARRAY} instructions. */ @INLINE public static ClassActor resolveClass(ResolutionGuard guard) { if (guard.value == null) { resolveClass0(guard); } return UnsafeCast.asClassActor(guard.value); } @NEVER_INLINE private static void resolveClassForNew0(ResolutionGuard guard) { final ClassActor classActor; if (guard instanceof InPool) { InPool guardInPool = (InPool) guard; final ConstantPool constantPool = guardInPool.pool; final int index = guardInPool.cpi; classActor = constantPool.classAt(index).resolve(constantPool, index); } else { classActor = ((InAccessingClass) guard).resolve(); } if (classActor.isAbstract() || classActor.isArrayClass()) { // Covers abstract classes and interfaces throw new InstantiationError(); } guard.value = classActor; } /** * Resolves a constant pool entry denoting a class that specifies a class type that can be instantiated. * * This snippet is used when translating the {@link Bytecodes#NEW} instruction. */ @INLINE public static ClassActor resolveClassForNew(ResolutionGuard guard) { if (guard.value == null) { resolveClassForNew0(guard); } return UnsafeCast.asClassActor(guard.value); } @NEVER_INLINE private static void resolveArrayClass0(ResolutionGuard guard) { final ClassActor arrayClassActor; if (guard instanceof InPool) { InPool guardInPool = (InPool) guard; final ConstantPool constantPool = guardInPool.pool; final int index = guardInPool.cpi; arrayClassActor = ArrayClassActor.forComponentClassActor(constantPool.classAt(index).resolve(constantPool, index)); } else { arrayClassActor = ((InAccessingClass) guard).resolve(); } guard.value = arrayClassActor; } /** * Resolves a constant pool entry denoting a class that specifies the component type of an object. * The resolved value returned by this snippet is the array type derived from the component type. * * This snippet is used when translating the {@link Bytecodes#ANEWARRAY} instruction. */ @INLINE public static ArrayClassActor resolveArrayClass(ResolutionGuard guard) { if (guard.value == null) { resolveArrayClass0(guard); } return UnsafeCast.asArrayClassActor(guard.value); } @NEVER_INLINE private static void resolveInstanceFieldForReading0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; final FieldActor fieldActor = constantPool.fieldAt(index).resolve(constantPool, index); if (fieldActor.isStatic()) { throw new IncompatibleClassChangeError(); } guard.value = fieldActor; } /** * Resolves a constant pool entry denoting an instance field that is being read. * * This snippet is used when translating the {@link Bytecodes#GETFIELD} instruction. */ @INLINE public static FieldActor resolveInstanceFieldForReading(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveInstanceFieldForReading0(guard); } return UnsafeCast.asFieldActor(guard.value); } @NEVER_INLINE private static void resolveInstanceFieldForWriting0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; final FieldActor fieldActor = constantPool.fieldAt(index).resolve(constantPool, index); if (fieldActor.isStatic()) { throw new IncompatibleClassChangeError(); } if (fieldActor.isFinal() && fieldActor.holder() != constantPool.holder()) { throw new IllegalAccessError(); } guard.value = fieldActor; } /** * Resolves a constant pool entry denoting an instance field that is being written to. * * This snippet is used when translating the {@link Bytecodes#PUTFIELD} instruction. */ @INLINE public static FieldActor resolveInstanceFieldForWriting(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveInstanceFieldForWriting0(guard); } return UnsafeCast.asFieldActor(guard.value); } /** * Resolves a constant pool entry denoting a static method that is being invoked. * * This snippet is used when translating the {@link Bytecodes#INVOKESTATIC} instruction. */ @NEVER_INLINE private static void resolveStaticMethod0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; guard.value = constantPool.resolveInvokeStatic(index); } @INLINE public static StaticMethodActor resolveStaticMethod(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveStaticMethod0(guard); } return UnsafeCast.asStaticMethodActor(guard.value); } @NEVER_INLINE private static void resolveSpecialMethod0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; guard.value = constantPool.resolveInvokeSpecial(index); } @INLINE public static VirtualMethodActor resolveSpecialMethod(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveSpecialMethod0(guard); } return UnsafeCast.asVirtualMethodActor(guard.value); } @NEVER_INLINE private static void resolveVirtualMethod0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; final MethodActor methodActor = constantPool.resolveInvokeVirtual(index); if (methodActor.isInitializer()) { throw new VerifyError("<init> must be invoked with invokespecial"); } guard.value = methodActor; } @INLINE public static VirtualMethodActor resolveVirtualMethod(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveVirtualMethod0(guard); } return UnsafeCast.asVirtualMethodActor(guard.value); } @NEVER_INLINE public static void resolveInterfaceMethod0(ResolutionGuard.InPool guard) { final ConstantPool constantPool = guard.pool; final int index = guard.cpi; guard.value = constantPool.resolveInvokeInterface(index); } /** * Resolves a constant pool entry denoting a method that is being invoked "interfacially". * * This snippet is used when translating the {@link Bytecodes#INVOKEINTERFACE} instruction. * * Special note: While it is legal to invoke a virtual method declared in java.lang.Object with invokeinterface, an * assumption is made by this snippet that the verifier detects such cases and rewrites the instruction to use * invokevirtual instead. */ @INLINE public static InterfaceMethodActor resolveInterfaceMethod(ResolutionGuard.InPool guard) { if (guard.value == null) { resolveInterfaceMethod0(guard); } return UnsafeCast.asInterfaceMethodActor(guard.value); } /** * Ensures that the class in which a given static method or field is declared is initialized, performing class * initialization if necessary. */ @INLINE public static void makeHolderInitialized(MemberActor memberActor) { Snippets.makeClassInitialized(memberActor.holder()); } /** * Ensures that a given class is initialized, performing class initialization if necessary. */ @INLINE public static void makeClassInitialized(ClassActor classActor) { if (MaxineVM.isHosted()) { classActor.makeInitialized(); } else if (!classActor.isInitialized()) { classActor.makeInitialized(); } } /** * Produces an address corresponding to a given entry point for the code of a given method. * * If the compiled code does not yet exist for the method, it is compiled with the * default compiler. */ @INLINE public static Address makeEntrypoint(ClassMethodActor classMethodActor, CallEntryPoint cep) { return classMethodActor.makeTargetMethod().getEntryPoint(cep).toAddress(); } static final VmThreadLocal NATIVE_CALLS_DISABLED = new VmThreadLocal("NATIVE_CALLS_DISABLED", false, ""); /** * Disables calling native methods on the current thread. This state is recursive. That is, * natives calls are only re-enabled once {@link #enableNativeCallsForCurrentThread()} is * called the same number of times as this method has been called. * * It is a {@linkplain FatalError fatal error} if calls to this method and {@link #enableNativeCallsForCurrentThread()} * are unbalanced. * * Note: This feature is only provided as a debugging aid. It imposes an overhead (a test and branch on a VM thread local) * on every native call. It could be removed or disabled in a product build of the VM once GC is debugged. */ public static void disableNativeCallsForCurrentThread() { final Address value = NATIVE_CALLS_DISABLED.load(currentTLA()); NATIVE_CALLS_DISABLED.store3(value.plus(1)); } /** * Re-enables calling native methods on the current thread. This state is recursive. That is, * native calls are only re-enabled once this method is called the same number of times as * {@link #disableNativeCallsForCurrentThread()} has been called. * * It is a {@linkplain FatalError fatal error} if calls to this method and {@link #disableNativeCallsForCurrentThread()} * are unbalanced. */ public static void enableNativeCallsForCurrentThread() { final Address value = NATIVE_CALLS_DISABLED.load(currentTLA()); if (value.isZero()) { FatalError.unexpected("Unbalanced calls to disable/enable native calls for current thread"); } NATIVE_CALLS_DISABLED.store3(value.minus(1)); } /** * Performs the transition into a native function call. */ @INLINE public static void nativeCallPrologue(NativeFunction nf) { Pointer etla = ETLA.load(currentTLA()); Pointer previousAnchor = LAST_JAVA_FRAME_ANCHOR.load(etla); CodePointer ip = nf.nativeCallSafepointAddress(); Pointer anchor = JavaFrameAnchor.create(getCpuStackPointer(), getCpuFramePointer(), ip, previousAnchor); nativeCallPrologue0(etla, anchor); } /** * Makes the transition from the 'in Java' state to the 'in native' state. * * @param etla the safepoints-triggered TLA for the current thread * @param anchor the value to which {@link VmThreadLocal#LAST_JAVA_FRAME_ANCHOR} will be set just before * the transition is made */ @INLINE public static void nativeCallPrologue0(Pointer etla, Word anchor) { if (!NATIVE_CALLS_DISABLED.load(currentTLA()).isZero()) { throw FatalError.unexpected("Calling native code while native calls are disabled"); } // Update the last Java frame anchor for the current thread: LAST_JAVA_FRAME_ANCHOR.store(etla, anchor); if (UseCASBasedThreadFreezing) { MUTATOR_STATE.store(etla, THREAD_IN_NATIVE); } else { MemoryBarriers.barrier(MemoryBarriers.LOAD_STORE | MemoryBarriers.STORE_STORE); // The following store must be last: MUTATOR_STATE.store(etla, THREAD_IN_NATIVE); } } /** * Performs the transition out of a native function call. */ @INLINE public static void nativeCallEpilogue() { Pointer etla = ETLA.load(currentTLA()); Pointer anchor = LAST_JAVA_FRAME_ANCHOR.load(etla); nativeCallEpilogue0(etla, PREVIOUS.get(anchor)); } /** * Makes the transition from the 'in native' state to the 'in Java' state, blocking on * {@link VmThreadMap#THREAD_LOCK} if current thread is {@linkplain VmOperation frozen}. * * @param etla the safepoints-triggered TLA for the current thread * @param anchor the value to which {@link VmThreadLocal#LAST_JAVA_FRAME_ANCHOR} will be set just after * the transition is made */ @INLINE public static void nativeCallEpilogue0(Pointer etla, Pointer anchor) { blockWhileFrozen(etla); LAST_JAVA_FRAME_ANCHOR.store(etla, anchor); while (SUSPEND.load(etla).equals(VmOperation.SUSPEND_REQUEST)) { // In particular SUSPEND_JAVA is not set, so this is a thread returning from native code // other than native calls involved in unwinding the safepoint mechanism. VmThread.fromTLA(etla).suspendMonitor.suspend(); // We must re-check the state because it is possible // that even though we were resumed, we may have remained // off CPU through another suspend operation. } } /** * Acquire and immediately release the {@link VmThreadMap#THREAD_LOCK}. We only call this when we assume * that the VM is still at a safepoint, i.e., that the VM Operation thread holds the thread lock. Therefore, * our thread will be blocked until the end of the safepoint, and we avoid a spin loop while waiting for the * safepoint to end. * <br> * This method is called while the native state of a JNI call is still completely set up. Therefore, no * native prologue and epilogue must be emitted for this native call. * <br> * The native environment already has a pointer to the thread lock for other purposes, so we don't have * to pass it in as a parameter. This makes the code to call the native method shorter. */ @C_FUNCTION private static native void nativeBlockOnThreadLock(); private static ClassMethodActor blockOnThreadLockMethod; @FOLD public static ClassMethodActor blockOnThreadLockMethod() { if (MaxineVM.isHosted()) { synchronized (Snippets.class) { // Note: This code cannot be in a static initializer because of circular initialization problems. if (blockOnThreadLockMethod == null) { CriticalNativeMethod cnm = new CriticalNativeMethod(Snippets.class, "nativeBlockOnThreadLock"); blockOnThreadLockMethod = cnm.classMethodActor; } } } return blockOnThreadLockMethod; } /** * This methods is blocked on {@link VmThreadMap#THREAD_LOCK} while the current thread is {@linkplain VmOperation frozen}. */ @INLINE @NO_SAFEPOINT_POLLS("Cannot take a trap while frozen") private static void blockWhileFrozen(Pointer etla) { if (UseCASBasedThreadFreezing) { while (true) { final Word oldMutatorState = etla.compareAndSwapWord(MUTATOR_STATE.offset, THREAD_IN_NATIVE, THREAD_IN_JAVA); if (oldMutatorState.equals(THREAD_IN_NATIVE)) { break; } if (oldMutatorState.equals(THREAD_IN_JAVA)) { throw FatalError.unexpected("Thread transitioned itself from THREAD_IS_FROZEN to THREAD_IN_JAVA -- only the VM operation thread should do that"); } nativeBlockOnThreadLock(); } } else { while (true) { // Signal that we intend to go back into Java: MUTATOR_STATE.store(etla, THREAD_IN_JAVA); // Ensure that the VM operation thread sees the above state transition: MemoryBarriers.barrier(MemoryBarriers.STORE_LOAD); // Ask if current thread is frozen: if (FROZEN.load(etla).isZero()) { // If current thread is not frozen then the state transition above was valid (common case) return; } // Current thread is frozen so above state transition is invalid // so undo it and wait until freezer thread thaws the current thread then retry transition MUTATOR_STATE.store(etla, THREAD_IN_NATIVE); while (!FROZEN.load(etla).isZero()) { nativeBlockOnThreadLock(); } } } } /** * Saves information about the last Java caller for direct/C_FUNCTION calls. * Used by the Inspector for debugging. * * ATTENTION: If this is ever used for anything else than the inspector, * use memory barriers properly. */ @INLINE public static void nativeCallPrologueForC(NativeFunction nf) { Pointer etla = ETLA.load(currentTLA()); Pointer previousAnchor = LAST_JAVA_FRAME_ANCHOR.load(etla); CodePointer ip = nf.nativeCallSafepointAddress(); Pointer anchor = JavaFrameAnchor.create(getCpuStackPointer(), getCpuFramePointer(), ip, previousAnchor); LAST_JAVA_FRAME_ANCHOR.store(etla, anchor); } @INLINE public static void nativeCallEpilogueForC() { Pointer etla = ETLA.load(currentTLA()); Pointer anchor = LAST_JAVA_FRAME_ANCHOR.load(etla); LAST_JAVA_FRAME_ANCHOR.store(etla, PREVIOUS.get(anchor)); } @INLINE public static void checkArrayDimension(int length) { if (length < 0) { throw new NegativeArraySizeException(); } } @INLINE public static void checkCast(ClassActor classActor, Object object) { if (MaxineVM.isHosted()) { if (object != null && !classActor.toJava().isAssignableFrom(object.getClass())) { throw Throw.classCastException(classActor, object); } } else if (!classActor.isNullOrInstance(object)) { throw Throw.classCastException(classActor, object); } } @INLINE public static boolean instanceOf(ClassActor classActor, Object object) { if (MaxineVM.isHosted()) { return object != null && classActor.toJava().isAssignableFrom(object.getClass()); } return classActor.isNonNullInstance(object); } @C_FUNCTION private static native double nativeDoubleRemainder(double dividend, double divisor); @INLINE public static double doubleRemainder(double dividend, double divisor) { if (MaxineVM.isHosted()) { return dividend % divisor; } return nativeDoubleRemainder(dividend, divisor); } @C_FUNCTION private static native float nativeFloatRemainder(float dividend, float divisor); @INLINE public static float floatRemainder(float dividend, float divisor) { if (MaxineVM.isHosted()) { return dividend % divisor; } return nativeFloatRemainder(dividend, divisor); } }