/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Iterator;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_ClassLoadingListener;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.common.VM_CompiledMethods;
//TODO - Deal with Subarch
/**
* This class acts as an intermediary between VM_ClassLoader and the
* optimizing compiler's dependency database. Just before a class
* is marked as INITIALIZED, VM_Class.initialize() invokes
* OPT_ClassLoadingDependencyManager.classInitialized(), which is responsible
* for identifying and performing all necessary invalidations of
* opt compiler code.
*/
public final class OPT_ClassLoadingDependencyManager implements VM_ClassLoadingListener {
////////////////////////
// Entrypoints from VM_Class
////////////////////////
public synchronized void classInitialized(VM_Class c, boolean writingBootImage) {
// Process any dependencies on methods not being overridden.
if (!writingBootImage) {
if (DEBUG) {
report("CLDM: " + c + " is about to be marked as initialized.\n");
}
handleOverriddenMethods(c);
handleSubclassing(c);
}
OPT_InterfaceHierarchy.notifyClassInitialized(c);
}
/////////////////////////
// Entrypoints for the opt compiler to record dependencies
/////////////////////////
/**
* Record that the code currently being compiled (cm) must be
* invalidated if source is overridden.
*/
public synchronized void addNotOverriddenDependency(VM_Method source, VM_CompiledMethod cm) {
int cmid = cm.getId();
if (TRACE || DEBUG) {
report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not being overridden\n");
}
db.addNotOverriddenDependency(source, cmid);
}
/**
* Record that the code currently being compiled (cm) must be
* invalidated if source ever has a subclass.
*/
public synchronized void addNoSubclassDependency(VM_Class source, VM_CompiledMethod cm) {
int cmid = cm.getId();
if (TRACE || DEBUG) {
report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not having a subclass\n");
}
db.addNoSubclassDependency(source, cmid);
}
////////////////////////
// Implementation
////////////////////////
/**
* Take action when a method is overridden.
* @param c a class that has just been loaded.
*/
private void handleOverriddenMethods(VM_Class c) {
if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do.
VM_Class sc = c.getSuperClass();
// for each virtual method of sc, if it is overriden by
// a virtual method declared by c, then handle any required invalidations.
VM_Method[] sc_methods = sc.getVirtualMethods();
VM_Method[] c_methods = c.getVirtualMethods();
for (int i = 0; i < sc_methods.length; i++) {
if (sc_methods[i] != c_methods[i]) {
processOverride(sc_methods[i]);
}
}
// for each interface implmented by c, note that c provides an overridding
// implementation
for (VM_Class intf : c.getAllImplementedInterfaces()) {
for (VM_Method m : intf.getVirtualMethods()) {
processOverride(m);
}
}
}
private void processOverride(VM_Method overridden) {
Iterator<Integer> invalidatedMethods = db.invalidatedByOverriddenMethod(overridden);
if (invalidatedMethods != null) {
while (invalidatedMethods.hasNext()) {
int cmid = invalidatedMethods.next();
VM_CompiledMethod im = VM_CompiledMethods.getCompiledMethod(cmid);
if (im != null) { // im == null implies that the code has been GCed already
invalidate(im);
}
}
db.removeNotOverriddenDependency(overridden);
}
}
private void handleSubclassing(VM_Class c) {
if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do
VM_Class sc = c.getSuperClass();
Iterator<Integer> invalidatedMethods = db.invalidatedBySubclass(sc);
if (invalidatedMethods != null) {
while (invalidatedMethods.hasNext()) {
int cmid = invalidatedMethods.next();
VM_CompiledMethod im = VM_CompiledMethods.getCompiledMethod(cmid);
if (im != null) { // im == null implies that the code has been GCed already
invalidate(im);
}
}
db.removeNoSubclassDependency(sc);
}
}
/**
* helper method to invalidate a particular compiled method
*/
private void invalidate(VM_CompiledMethod cm) {
VM_Method m = cm.getMethod();
if (TRACE || DEBUG) {
report("CLDM: Invalidating compiled method " + cm.getId() + "(" + m + ")\n");
}
// (1) Mark the compiled method as invalid.
synchronized (cm) {
if (cm.isInvalid()) {
if (TRACE || DEBUG) report("\tcmid was alrady invalid; nothing more to do\n");
return;
}
// (2) Apply any code patches to protect invocations already executing
// in the soon to be invalid code.
((VM_OptCompiledMethod) cm).applyCodePatches(cm);
cm.setInvalid();
}
// (3) Inform its VM_Method that cm is invalid;
// This will update all the dispatching entries (TIB, JTOC, IMTs)
// so that no new invocations will reach the invalid compiled code.
// It also marks cm as obsolete so it can eventually be reclaimed by GC.
m.invalidateCompiledMethod(cm, false);
}
static final boolean DEBUG = false;
static final boolean TRACE = false;
void report(String s) {
if (VM.runningVM) {
if (log == null) {
if (true || !VM.fullyBooted) {
VM.sysWriteln("CLDM: VM not fully booted ", s);
return;
}
try {
log = new PrintStream(new FileOutputStream("PREEX_OPTS.TRACE"));
} catch (IOException e) {
VM.sysWrite("\n\nCLDM: Error opening logging file!!\n\n");
}
}
} else {
System.out.print(s);
}
}
private OPT_InvalidationDatabase db = new OPT_InvalidationDatabase();
private static PrintStream log;
}