/*
* Copyright (c) 2010, 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.compiler.deps;
import java.util.*;
import java.util.concurrent.locks.*;
import com.sun.cri.ci.*;
import com.sun.cri.ci.CiAssumptions.Assumption;
import com.sun.max.annotate.*;
import com.sun.max.program.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.hosted.*;
import com.sun.max.vm.log.VMLog.*;
import com.sun.max.vm.log.hosted.*;
/**
* The {@linkplain DependenciesManager} is the central point of control for the
* management of dependencies in {@link TargetMethod compiled code}.
* <p>
* Compilers issue queries against the class hierarchy and encode the answers as {@link Dependencies dependencies}
* which enable speculative optimizations (e.g., de-virtualization, type check elimination).
* A compiler aggregates dependencies when compiling a method.
* The dependencies must be validated before a target method is installed.
* If validation fails (because of changes in the class hierarchy since the assumptions
* were made), the target method is discarded and the compilation is repeated.
* <p>
* The set of assumptions/dependencies is open ended and each is managed by a {@link DependencyProcessor}.
* The manager is responsible for recording the set of {@linkplain DependencyProcessor dependency processors},
* and providing locking and logging support.
* <p>
* Dependencies can be invalidated for a number of reasons, the most common being
* the addition of a new class to the system. The {@link #addToHierarchy(ClassActor)} method
* is the method that should be called by the class definition system of the VM to report
* the addition of a class.
*
* See <a href="https://wikis.oracle.com/display/MaxineVM/Code+Dependencies">the Wiki page</a> for more details.
*/
public final class DependenciesManager {
/**
* The collection of {@link DependencyProcessor} objects that handle specific types of {@link Assumption}.
* This is immutable after image build and we keep an array of the values for fast, allocation free, iteration.
*/
public static final Map<Class<? extends CiAssumptions.Assumption>, DependencyProcessor> dependencyProcessors = new HashMap<Class<? extends CiAssumptions.Assumption>, DependencyProcessor>();
/**
* The current packed encoding limits the number of {@linkplain DependencyProcessor} instances.
*/
public static final int MAX_DEPENDENCY_PROCESSORS = 16;
/**
* A bit number that uniquely identifies the {@linkplain DependencyProcessor}.
*/
private static int nextDependencyProcessorId;
/**
* Read-write lock used to synchronize modifications to the class hierarchy with validation of dependencies.
* New class definition must acquire the lock in write mode to exclude all concurrent updates to the class hierarchy,
* and, more importantly, to exclude all concurrent validations or installations of validated dependencies.
* Validation and installation of dependencies acquire the lock in read mode to exclude all modifications to
* class hierarchy information by concurrent class definition. This allows
* multiple validation to be performed concurrently. Installation of dependencies in the dependency table
* requires additional synchronization as it updates both the table and per class type dependency information.
*/
public static final ReentrantReadWriteLock classHierarchyLock = new ReentrantReadWriteLock();
/**
* Used during registration to accumulate {@linkplain DependencyProcessor} instances.
*/
@HOSTED_ONLY
private static final ArrayList<DependencyProcessor> dependencyProcessorList = new ArrayList<DependencyProcessor>();
private static class InitializationCompleteCallback implements JavaPrototype.InitializationCompleteCallback {
@Override
public void initializationComplete() {
dependencyProcessorsArray = new DependencyProcessor[dependencyProcessorList.size()];
dependencyProcessorList.toArray(dependencyProcessorsArray);
}
}
static {
JavaPrototype.registerInitializationCompleteCallback(new InitializationCompleteCallback());
}
/**
* The dependency processors, ordered by their id number.
*/
@CONSTANT_WHEN_NOT_ZERO
static DependencyProcessor[] dependencyProcessorsArray;
/**
* The data structure mapping classes to their dependents.
*/
public static final ContextDependents contextDependents = new ContextDependents();
/**
* Registration of a new {@linkplain DependencyProcessor}.
* @param dependencyProcessor the {@linkplain DependencyProcessor}
* @param assumptionClass the associated subclass of {@linkplain Assumption}
* @return the unique id for the processor
*/
@HOSTED_ONLY
static synchronized int registerDependencyProcessor(DependencyProcessor dependencyProcessor,
Class< ? extends CiAssumptions.Assumption> assumptionClass) {
ProgramError.check(dependencyProcessors.put(assumptionClass, dependencyProcessor) == null);
ProgramError.check(nextDependencyProcessorId < MAX_DEPENDENCY_PROCESSORS);
dependencyProcessorList.add(dependencyProcessor);
return nextDependencyProcessorId++;
}
/**
* Adds a class to the class hierarchy.
* This checks dependencies on the type hierarchy and invalidates all target methods whose dependencies are no longer valid.
*
* @param classActor the class to be added to the global class hierarchy
*/
public static void addToHierarchy(ClassActor classActor) {
boolean refreshTables = false;
classHierarchyLock.writeLock().lock();
try {
classActor.prependToSiblingList();
ArrayList<Dependencies> invalidated = ConcreteTypeDependencyProcessor.recordUniqueConcreteSubtype(classActor);
ConcreteTypeDependencyProcessor.invalidateDependencies(invalidated, classActor);
refreshTables = true;
} finally {
classHierarchyLock.writeLock().unlock();
if (!MaxineVM.isHosted() && refreshTables) {
// Don't need to be under the class hierarchy lock to do this.
classActor.dynamicHub().refreshVTable();
classActor.dynamicHub().refreshITable();
}
}
}
// Logging
@HOSTED_ONLY
@VMLoggerInterface
private interface DependenciesLoggerInterface {
void add(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "id") int id,
@VMLogParam(name = "type") ClassActor type);
void remove(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "id") int id,
@VMLogParam(name = "type") ClassActor type);
void register(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "id") int id);
void invalidateDeps(
@VMLogParam(name = "type") ClassActor type);
void invalidated(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "id") int id);
void invalidateUCT(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "context") ClassActor context,
@VMLogParam(name = "subtype") ClassActor subtype);
void invalidateUCM(
@VMLogParam(name = "targetMethod") TargetMethod targetMethod,
@VMLogParam(name = "context") ClassActor context,
@VMLogParam(name = "method") MethodActor method,
@VMLogParam(name = "impl") MethodActor impl);
}
public static final DependenciesLogger dependenciesLogger = new DependenciesLogger();
public static final class DependenciesLogger extends DependenciesLoggerAuto {
DependenciesLogger() {
super("Deps", "compilation dependencies.");
}
@Override
protected void traceAdd(TargetMethod targetMethod, int id, ClassActor type) {
traceAddRemove(id, type, "Added");
}
@Override
protected void traceRegister(TargetMethod targetMethod, int id) {
Dependencies deps = Dependencies.fromId(id);
printPrefix();
Log.println("Register " + deps.toString(true));
}
@Override
protected void traceRemove(TargetMethod targetMethod, int id, ClassActor type) {
traceAddRemove(id, type, "Removed");
}
@Override
protected void traceInvalidated(TargetMethod targetMethod, int id) {
printPrefix();
Dependencies deps = Dependencies.fromId(id);
Log.println(" " + deps);
}
@Override
protected void traceInvalidateDeps(ClassActor type) {
printPrefix();
Log.println("adding " + type + " to the hierarchy invalidates:");
}
@Override
protected void traceInvalidateUCT(TargetMethod targetMethod, ClassActor context, ClassActor subtype) {
StringBuilder sb = invalidateSB(targetMethod, "UCT[").append(context);
if (context != subtype) {
sb.append(",").append(subtype);
}
sb.append(']');
Log.println(sb.toString());
}
@Override
protected void traceInvalidateUCM(TargetMethod targetMethod, ClassActor context, MethodActor method, MethodActor impl) {
StringBuilder sb = invalidateSB(targetMethod, "UCM[").append(method);
if (method != impl) {
sb.append(",").append(impl);
}
sb.append("]");
Log.println(sb.toString());
}
private static void traceAddRemove(int id, ClassActor type, String kind) {
printPrefix();
Dependencies deps = Dependencies.fromId(id);
Log.println(kind + " dependency from " + deps + " to " + type);
}
private static StringBuilder invalidateSB(TargetMethod targetMethod, String iKind) {
return new StringBuilder("DEPS: invalidated ").append(targetMethod).append(", invalid dep: ").append(iKind);
}
private static void printPrefix() {
Log.print("DEPS: ");
}
}
// START GENERATED CODE
private static abstract class DependenciesLoggerAuto extends com.sun.max.vm.log.VMLogger {
public enum Operation {
Add, InvalidateDeps, InvalidateUCM,
InvalidateUCT, Invalidated, Register, Remove;
@SuppressWarnings("hiding")
public static final Operation[] VALUES = values();
}
private static final int[] REFMAPS = new int[] {0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1};
protected DependenciesLoggerAuto(String name, String optionDescription) {
super(name, Operation.VALUES.length, optionDescription, REFMAPS);
}
@Override
public String operationName(int opCode) {
return Operation.VALUES[opCode].name();
}
@INLINE
public final void logAdd(TargetMethod targetMethod, int id, ClassActor type) {
log(Operation.Add.ordinal(), objectArg(targetMethod), intArg(id), classActorArg(type));
}
protected abstract void traceAdd(TargetMethod targetMethod, int id, ClassActor type);
@INLINE
public final void logInvalidateDeps(ClassActor type) {
log(Operation.InvalidateDeps.ordinal(), classActorArg(type));
}
protected abstract void traceInvalidateDeps(ClassActor type);
@INLINE
public final void logInvalidateUCM(TargetMethod targetMethod, ClassActor context, MethodActor method, MethodActor impl) {
log(Operation.InvalidateUCM.ordinal(), objectArg(targetMethod), classActorArg(context), methodActorArg(method), methodActorArg(impl));
}
protected abstract void traceInvalidateUCM(TargetMethod targetMethod, ClassActor context, MethodActor method, MethodActor impl);
@INLINE
public final void logInvalidateUCT(TargetMethod targetMethod, ClassActor context, ClassActor subtype) {
log(Operation.InvalidateUCT.ordinal(), objectArg(targetMethod), classActorArg(context), classActorArg(subtype));
}
protected abstract void traceInvalidateUCT(TargetMethod targetMethod, ClassActor context, ClassActor subtype);
@INLINE
public final void logInvalidated(TargetMethod targetMethod, int id) {
log(Operation.Invalidated.ordinal(), objectArg(targetMethod), intArg(id));
}
protected abstract void traceInvalidated(TargetMethod targetMethod, int id);
@INLINE
public final void logRegister(TargetMethod targetMethod, int id) {
log(Operation.Register.ordinal(), objectArg(targetMethod), intArg(id));
}
protected abstract void traceRegister(TargetMethod targetMethod, int id);
@INLINE
public final void logRemove(TargetMethod targetMethod, int id, ClassActor type) {
log(Operation.Remove.ordinal(), objectArg(targetMethod), intArg(id), classActorArg(type));
}
protected abstract void traceRemove(TargetMethod targetMethod, int id, ClassActor type);
@Override
protected void trace(Record r) {
switch (r.getOperation()) {
case 0: { //Add
traceAdd(toTargetMethod(r, 1), toInt(r, 2), toClassActor(r, 3));
break;
}
case 1: { //InvalidateDeps
traceInvalidateDeps(toClassActor(r, 1));
break;
}
case 2: { //InvalidateUCM
traceInvalidateUCM(toTargetMethod(r, 1), toClassActor(r, 2), toMethodActor(r, 3), toMethodActor(r, 4));
break;
}
case 3: { //InvalidateUCT
traceInvalidateUCT(toTargetMethod(r, 1), toClassActor(r, 2), toClassActor(r, 3));
break;
}
case 4: { //Invalidated
traceInvalidated(toTargetMethod(r, 1), toInt(r, 2));
break;
}
case 5: { //Register
traceRegister(toTargetMethod(r, 1), toInt(r, 2));
break;
}
case 6: { //Remove
traceRemove(toTargetMethod(r, 1), toInt(r, 2), toClassActor(r, 3));
break;
}
}
}
}
// END GENERATED CODE
}