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 */ 013package org.jikesrvm.compilers.opt.inlining; 014 015import java.io.FileOutputStream; 016import java.io.IOException; 017import java.io.PrintStream; 018import java.util.Iterator; 019 020import org.jikesrvm.VM; 021import org.jikesrvm.classloader.RVMClass; 022import org.jikesrvm.classloader.ClassLoadingListener; 023import org.jikesrvm.classloader.RVMMethod; 024import org.jikesrvm.compilers.common.CompiledMethod; 025import org.jikesrvm.compilers.common.CompiledMethods; 026import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; 027 028/** 029 * This class acts as an intermediary between RVMClassLoader and the 030 * optimizing compiler's dependency database. Just before a class 031 * is marked as INITIALIZED, RVMClass.initialize() invokes 032 * ClassLoadingDependencyManager.classInitialized(), which is responsible 033 * for identifying and performing all necessary invalidations of 034 * opt compiler code. 035 */ 036public final class ClassLoadingDependencyManager implements ClassLoadingListener { 037 038 /** Database holding information on class loading */ 039 private final InvalidationDatabase db = new InvalidationDatabase(); 040 041 /** Debug execution */ 042 static final boolean DEBUG = false; 043 /** Trace execution */ 044 static final boolean TRACE = false; 045 /** Stream used in debug tracing */ 046 private static PrintStream log; 047 048 //////////////////////// 049 // Entrypoints from RVMClass 050 //////////////////////// 051 @Override 052 public synchronized void classInitialized(RVMClass c, boolean writingBootImage) { 053 // Process any dependencies on methods not being overridden. 054 if (!writingBootImage) { 055 if (DEBUG) { 056 report("CLDM: " + c + " is about to be marked as initialized.\n"); 057 } 058 handleOverriddenMethods(c); 059 handleSubclassing(c); 060 } 061 InterfaceHierarchy.notifyClassInitialized(c); 062 } 063 064 ///////////////////////// 065 // Entrypoints for the opt compiler to record dependencies 066 ///////////////////////// 067 068 /** 069 * Record that the code currently being compiled (cm) must be 070 * invalidated if source is overridden. 071 * 072 * @param source the method whose overriding will cause invalidation 073 * @param cm the method to invalidate 074 */ 075 public synchronized void addNotOverriddenDependency(RVMMethod source, CompiledMethod cm) { 076 int cmid = cm.getId(); 077 if (TRACE || DEBUG) { 078 report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not being overridden\n"); 079 } 080 db.addNotOverriddenDependency(source, cmid); 081 } 082 083 /** 084 * Record that the code currently being compiled (cm) must be 085 * invalidated if source ever has a subclass. 086 * 087 * @param source the class whose subclassing will cause invalidation 088 * @param cm the method to invalidate 089 */ 090 public synchronized void addNoSubclassDependency(RVMClass source, CompiledMethod cm) { 091 int cmid = cm.getId(); 092 if (TRACE || DEBUG) { 093 report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not having a subclass\n"); 094 } 095 db.addNoSubclassDependency(source, cmid); 096 } 097 098 //////////////////////// 099 // Implementation 100 //////////////////////// 101 102 /** 103 * Takes action when a method is overridden. 104 * @param c a class that has just been loaded. 105 */ 106 private void handleOverriddenMethods(RVMClass c) { 107 if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do. 108 RVMClass sc = c.getSuperClass(); 109 // for each virtual method of sc, if it is overriden by 110 // a virtual method declared by c, then handle any required invalidations. 111 RVMMethod[] sc_methods = sc.getVirtualMethods(); 112 RVMMethod[] c_methods = c.getVirtualMethods(); 113 for (int i = 0; i < sc_methods.length; i++) { 114 if (sc_methods[i] != c_methods[i]) { 115 processOverride(sc_methods[i]); 116 } 117 } 118 // for each interface implmented by c, note that c provides an overridding 119 // implementation 120 for (RVMClass intf : c.getAllImplementedInterfaces()) { 121 for (RVMMethod m : intf.getVirtualMethods()) { 122 processOverride(m); 123 } 124 } 125 } 126 127 private void processOverride(RVMMethod overridden) { 128 Iterator<Integer> invalidatedMethods = db.invalidatedByOverriddenMethod(overridden); 129 if (invalidatedMethods != null) { 130 while (invalidatedMethods.hasNext()) { 131 int cmid = invalidatedMethods.next(); 132 CompiledMethod im = CompiledMethods.getCompiledMethod(cmid); 133 if (im != null) { // im == null implies that the code has been GCed already 134 invalidate(im); 135 } 136 } 137 db.removeNotOverriddenDependency(overridden); 138 } 139 } 140 141 private void handleSubclassing(RVMClass c) { 142 if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do 143 RVMClass sc = c.getSuperClass(); 144 Iterator<Integer> invalidatedMethods = db.invalidatedBySubclass(sc); 145 if (invalidatedMethods != null) { 146 while (invalidatedMethods.hasNext()) { 147 int cmid = invalidatedMethods.next(); 148 CompiledMethod im = CompiledMethods.getCompiledMethod(cmid); 149 if (im != null) { // im == null implies that the code has been GCed already 150 invalidate(im); 151 } 152 } 153 db.removeNoSubclassDependency(sc); 154 } 155 } 156 157 private void invalidate(CompiledMethod cm) { 158 RVMMethod m = cm.getMethod(); 159 if (TRACE || DEBUG) { 160 report("CLDM: Invalidating compiled method " + cm.getId() + "(" + m + ")\n"); 161 } 162 163 // (1) Mark the compiled method as invalid. 164 synchronized (cm) { 165 if (cm.isInvalid()) { 166 if (TRACE || DEBUG) report("\tcmid was alrady invalid; nothing more to do\n"); 167 return; 168 } 169 170 // (2) Apply any code patches to protect invocations already executing 171 // in the soon to be invalid code. 172 ((OptCompiledMethod)cm).applyCodePatches(cm); 173 174 cm.setInvalid(); 175 } 176 177 // (3) Inform its RVMMethod that cm is invalid; 178 // This will update all the dispatching entries (TIB, JTOC, IMTs) 179 // so that no new invocations will reach the invalid compiled code. 180 // It also marks cm as obsolete so it can eventually be reclaimed by GC. 181 m.invalidateCompiledMethod(cm); 182 } 183 184 void report(String s) { 185 if (VM.runningVM) { 186 if (log == null) { 187 if (true || !VM.fullyBooted) { 188 VM.sysWriteln("CLDM: VM not fully booted ", s); 189 return; 190 } 191 try { 192 log = new PrintStream(new FileOutputStream("PREEX_OPTS.TRACE")); 193 } catch (IOException e) { 194 VM.sysWrite("\n\nCLDM: Error opening logging file!!\n\n"); 195 } 196 } 197 } else { 198 System.out.print(s); 199 } 200 } 201}