/* * Copyright (c) 2007, 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.actor.holder; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.type.ClassRegistry.*; import com.oracle.max.cri.intrinsics.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.code.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.compiler.deps.*; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.layout.Layout.Category; import com.sun.max.vm.monitor.modal.modehandlers.lightweight.biased.*; import com.sun.max.vm.object.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.type.*; /** * Every object has a reference to its "hub" in its header. */ public abstract class Hub extends Hybrid { /** * Indicates the "tuple cell size" for objects as follows. * - tuples: the permanent cell size * - hybrids: the cell size before expansion * - arrays: the object header size */ public final Size tupleSize; public final Hub componentHub; public final SpecificLayout specificLayout; @INSPECTED public final ClassActor classActor; public final Layout.Category layoutCategory; public BiasedLockEpoch64 biasedLockEpoch = BiasedLockEpoch64.init(); /** * Word index to the first element of the variable-size part of the hub. * The index is relative to the first word following the hub's header. * Note that all indexes to virtual and interface tables are also relative to the first word following the hub's header. */ private static final int firstWordIndex; public final int iTableStartIndex; public final int iTableLength; @INSPECTED public final int mTableStartIndex; @INSPECTED public final int mTableLength; @INSPECTED public final int referenceMapLength; @INSPECTED public final int referenceMapStartIndex; /** * Specifies if this is the hub for {@link java.lang.ref.Reference} or * a subclass of the former. */ public final boolean isJLRReference; /** * Determines whether a given set of class ids collide in a hash table of size {@code divisor} * using a hash function of {@code id % divisor}. * * @param ids set of class ids with interface ids encoded as their negative value * @see ClassIDManager */ private static boolean colliding(int[] ids, int divisor) { if (divisor < 64) { // Common case avoids allocating boolean[] long table = 0; for (int id : ids) { int posId = id >= 0 ? id : -id; final int index = posId % divisor; long entry = 1L << index; if ((table & entry) != 0) { return true; } table |= entry; } return false; } final boolean[] table = new boolean[divisor]; for (int id : ids) { int posId = id >= 0 ? id : -id; final int index = posId % divisor; if (table[index]) { return true; } table[index] = true; } return false; } /** * Gets the smallest table size for which we have perfect (collision free) hashing for the given class ids. * * @param ids set of class ids with interface ids encoded as their negative value */ private static int minCollisionFreeDivisor(int[] ids) { int divisor = ids.length; while (colliding(ids, divisor)) { divisor++; } return divisor; } static { final ClassActor classActor = ClassActor.fromJava(Hub.class); // Although the actual super class is 'Object', since it has no fields, we may pass 'null' here instead // and indeed we must to avoid not-yet-bootstrapped calls on the super class actor: final ClassActor superClassActor = null; final Size tupleSize = Layout.hybridLayout().layoutFields(superClassActor, classActor.localInstanceFieldActors()); firstWordIndex = Layout.hybridLayout().firstAvailableWordArrayIndex(tupleSize); } private static int computeFirstWordIndex() { final ClassActor classActor = ClassActor.fromJava(Hub.class); // Although the actual super class is 'Object', since it has no fields, we may pass 'null' here instead // and indeed we must to avoid not-yet-bootstrapped calls on the super class actor: final ClassActor superClassActor = null; final Size tupleSize = Layout.hybridLayout().layoutFields(superClassActor, classActor.localInstanceFieldActors()); return Layout.hybridLayout().firstAvailableWordArrayIndex(tupleSize); } @FOLD public static int getFirstWordIndex() { return firstWordIndex; } @Override public final int firstWordIndex() { return getFirstWordIndex(); } @Override public final int lastWordIndex() { return iTableStartIndex + iTableLength - 1; } @Override public final int firstIntIndex() { return UnsignedMath.divide((iTableStartIndex + iTableLength) * Word.size(), Ints.SIZE); } @Override public final int lastIntIndex() { return referenceMapStartIndex + referenceMapLength - 1; } /** * Index, relative to the first word following the hub's header, to the first entry of the virtual table. * @return an word index */ @INLINE public static int vTableStartIndex() { return getFirstWordIndex(); } public final int vTableLength() { return iTableStartIndex - vTableStartIndex(); } @CONSTANT_WHEN_NOT_ZERO private BiasedLockRevocationHeuristics biasedLockRevocationHeuristics; @INLINE public final BiasedLockRevocationHeuristics biasedLockRevocationHeuristics() { return biasedLockRevocationHeuristics; } public void setBiasedLockRevocationHeuristics(BiasedLockRevocationHeuristics biasedLockRevocationHeuristics) { this.biasedLockRevocationHeuristics = biasedLockRevocationHeuristics; } private int getITableLength(int[] superClassActorIds, Iterable<InterfaceActor> allInterfaceActors) { int result = 1 + superClassActorIds.length; if (classActor.isReferenceClassActor()) { for (InterfaceActor interfaceActor : allInterfaceActors) { result += interfaceActor.localInterfaceMethodActors().length; } } return result; } /** * Static Hub. */ protected Hub(Size tupleSize, ClassActor classActor, TupleReferenceMap referenceMap, int vTableLength) { this.tupleSize = tupleSize; this.componentHub = null; this.specificLayout = Layout.tupleLayout(); this.layoutCategory = Layout.Category.TUPLE; this.classActor = classActor; this.iTableStartIndex = firstWordIndex() + vTableLength; this.iTableLength = 1; this.mTableStartIndex = firstIntIndex(); this.mTableLength = 1; this.referenceMapStartIndex = mTableStartIndex + mTableLength; this.referenceMapLength = referenceMap.numberOfEntries(); this.isJLRReference = false; } /** * Dynamic Hub. */ protected Hub(Size tupleSize, SpecificLayout specificLayout, ClassActor classActor, int[] superClassActorIds, Iterable<InterfaceActor> allInterfaceActors, int vTableLength, TupleReferenceMap referenceMap) { this.tupleSize = tupleSize; this.specificLayout = specificLayout; this.layoutCategory = specificLayout.category(); if (layoutCategory == Category.ARRAY) { componentHub = classActor.componentClassActor().dynamicHub(); assert componentHub != null || classActor.componentClassActor().kind != Kind.REFERENCE; } else { componentHub = null; } this.classActor = classActor; this.iTableStartIndex = firstWordIndex() + vTableLength; this.iTableLength = getITableLength(superClassActorIds, allInterfaceActors); this.mTableStartIndex = firstIntIndex(); this.mTableLength = minCollisionFreeDivisor(superClassActorIds); this.referenceMapStartIndex = mTableStartIndex + mTableLength; this.referenceMapLength = referenceMap.numberOfEntries(); this.isJLRReference = isSupertypeOf(JLR_REFERENCE, classActor); } private static boolean isSupertypeOf(ClassActor c, ClassActor sub) { while (sub != null) { if (sub == c) { return true; } sub = sub.superClassActor; } return false; } protected final Hub expand() { return (Hub) expand(computeLength(referenceMapStartIndex, referenceMapLength)); } static Address checkCompiled(VirtualMethodActor virtualMethodActor) { if (!MaxineVM.isHosted()) { final TargetMethod current = virtualMethodActor.currentTargetMethod(); if (current != null) { return current.getEntryPoint(CallEntryPoint.VTABLE_ENTRY_POINT).toAddress(); } } return Address.zero(); } void initializeVTable(VirtualMethodActor[] allVirtualMethodActors) { for (int i = 0; i < allVirtualMethodActors.length; i++) { final VirtualMethodActor virtualMethodActor = allVirtualMethodActors[i]; final int vTableIndex = vTableStartIndex() + i; assert virtualMethodActor.vTableIndex() == vTableIndex; assert getWord(vTableIndex).isZero(); Address vTableEntry; if (MaxineVM.isHosted()) { vTableEntry = checkCompiled(virtualMethodActor); if (vTableEntry.isZero()) { vTableEntry = vm().stubs.virtualTrampoline(vTableIndex).toAddress(); } } else { // IMPORTANT: Don't fill with compiled method entry points. // This is delayed to until the class actor for this class is actually entered in the class hierarchy. // There's a couple of reasons for that: // 1. A deoptimization may occur between the initialization of the vtable and the insertion of the class actor to the class hierarchy graph. // In this interval of time, the class actor is not visible to the deoptimization process. By the time it becomes visible, the deopt is complete // and the vtable is corrupted with code pointers to obsolete optimized method whose entry point has been patched a jump to static trampoline. // This will cause subsequent vtable dispatch to fail as these will take the trampoline, which will attempt to patch what it believe was a direct call. // This results in an fatal error because the call site isn't a direct call but a register indirect call of a vtable dispatch. // // 2. multiple threads may race to create a ClassActor for the same class as they race for their definition. There's no need to waste time // checking for compiled code until the class actor is effectively selected to define the class (which is effected when adding to the class hierarchy. // This will be done with the method optimized vtable below. vTableEntry = vm().stubs.virtualTrampoline(vTableIndex).toAddress(); } setWord(vTableIndex, vTableEntry); } } private void checkVTableEntry(int vTableIndex) { final Address vTableEntry = getWord(vTableIndex).asAddress(); TargetMethod tm = Code.codePointerToTargetMethod(vTableEntry.asPointer()); if (tm == null || Stubs.isJumpToStaticTrampoline(tm)) { Log.println(classActor.toString() + "(hub = " + Reference.fromJava(this).toOrigin().to0xHexString() + ") has virtual table entry #" + (vTableIndex - firstWordIndex) + "(" + vTableIndex + ") points to method patched with static trampoline"); FatalError.unexpected("corrupted vtable"); } } public void checkVTable() { if (!MaxineVM.isHosted()) { final VirtualMethodActor[] allVirtualMethodActors = classActor.allVirtualMethodActors(); final int endOfVTable = firstWordIndex() + allVirtualMethodActors.length; for (int vTableIndex = firstWordIndex(); vTableIndex < endOfVTable; vTableIndex++) { checkVTableEntry(vTableIndex); } } } /** * Replace vtable trampoline with compiled method entry points for compiled virtual methods. * This is done once, upon adding the class actor to the class hierarchy. * @see DependenciesManager */ public void refreshVTable() { final VirtualMethodActor[] allVirtualMethodActors = classActor.allVirtualMethodActors(); if (allVirtualMethodActors.length > 0) { // Don't bother refreshing entries that are beyond this class superclass's virtual table: they either aren't compiled // or are already being updated by concurrent compilations. final int end = classActor.superClassActor.allVirtualMethodActors().length; for (int i = 0; i < end; i++) { final VirtualMethodActor virtualMethodActor = allVirtualMethodActors[i]; final Address vTableEntry = checkCompiled(virtualMethodActor); if (vTableEntry.isNotZero()) { setWord(vTableStartIndex() + i, vTableEntry); } if (MaxineVM.isDebug()) { checkVTableEntry(vTableStartIndex() + i); } } } } /** * Make a given vtable entry points to the trampoline again. * @param index the offset into the vtable as answered by {@linkplain VirtualMethodActor#vTableIndex()} */ public void resetVTableEntry(int index) { setWord(index, vm().stubs.virtualTrampoline(index).toAddress()); } /** * Computes the number of words that the non-header part of a hub occupies. That is, * if a hub is viewed as a word array, the returned value is the length of the array. */ protected static int computeLength(int referenceMapStartIndex, int referenceMapLength) { int referenceMapSize = Ints.roundUnsignedUpByPowerOfTwo((referenceMapStartIndex + referenceMapLength) * Ints.SIZE, Word.size()); return UnsignedMath.divide(referenceMapSize, Word.size()); } @INLINE public final int getMTableIndex(int id) { return (id % mTableLength) + mTableStartIndex; } @INLINE public final int getITableIndex(int id) { return getInt(getMTableIndex(id)); } @INLINE public final boolean isSubClassHub(ClassActor testClassActor) { if (this.classActor == testClassActor) { // the common case of an exact type match return true; } final int id = testClassActor.id; final int iTableIndex = getITableIndex(id); return getWord(iTableIndex).equals(Address.fromInt(id)); } public abstract FieldActor findFieldActor(int offset); @Override public String toString() { return getClass().getSimpleName() + "[" + classActor + "]"; } public static boolean validItableEntry(CodePointer p) { final long pvalue = p.toLong(); if (pvalue < 0 || pvalue > Integer.MAX_VALUE) { return false; } final int id = (int) pvalue; return ClassIDManager.toClassActor(id) != null || ClassIDManager.isUsedID(id); } /** * Visit references of the object described by this hub using the hub's reference maps. * Note that reference arrays have an empty reference map. */ @INLINE public final void visitMappedReferences(Pointer origin, PointerIndexVisitor visitor) { final int n = referenceMapStartIndex + referenceMapLength; for (int i = referenceMapStartIndex; i < n; i++) { final int index = getInt(i); visitor.visit(origin, index); } } }