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 */ 013 package org.jikesrvm.compilers.opt.inlining; 014 015 import java.io.FileOutputStream; 016 import java.io.IOException; 017 import java.io.PrintStream; 018 import java.util.Iterator; 019 020 import org.jikesrvm.VM; 021 import org.jikesrvm.classloader.RVMClass; 022 import org.jikesrvm.classloader.ClassLoadingListener; 023 import org.jikesrvm.classloader.RVMMethod; 024 import org.jikesrvm.compilers.common.CompiledMethod; 025 import org.jikesrvm.compilers.common.CompiledMethods; 026 import 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 */ 036 public 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 public synchronized void addNotOverriddenDependency(RVMMethod source, CompiledMethod cm) { 073 int cmid = cm.getId(); 074 if (TRACE || DEBUG) { 075 report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not being overridden\n"); 076 } 077 db.addNotOverriddenDependency(source, cmid); 078 } 079 080 /** 081 * Record that the code currently being compiled (cm) must be 082 * invalidated if source ever has a subclass. 083 */ 084 public synchronized void addNoSubclassDependency(RVMClass source, CompiledMethod cm) { 085 int cmid = cm.getId(); 086 if (TRACE || DEBUG) { 087 report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not having a subclass\n"); 088 } 089 db.addNoSubclassDependency(source, cmid); 090 } 091 092 //////////////////////// 093 // Implementation 094 //////////////////////// 095 096 /** 097 * Take action when a method is overridden. 098 * @param c a class that has just been loaded. 099 */ 100 private void handleOverriddenMethods(RVMClass c) { 101 if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do. 102 RVMClass sc = c.getSuperClass(); 103 // for each virtual method of sc, if it is overriden by 104 // a virtual method declared by c, then handle any required invalidations. 105 RVMMethod[] sc_methods = sc.getVirtualMethods(); 106 RVMMethod[] c_methods = c.getVirtualMethods(); 107 for (int i = 0; i < sc_methods.length; i++) { 108 if (sc_methods[i] != c_methods[i]) { 109 processOverride(sc_methods[i]); 110 } 111 } 112 // for each interface implmented by c, note that c provides an overridding 113 // implementation 114 for (RVMClass intf : c.getAllImplementedInterfaces()) { 115 for (RVMMethod m : intf.getVirtualMethods()) { 116 processOverride(m); 117 } 118 } 119 } 120 121 private void processOverride(RVMMethod overridden) { 122 Iterator<Integer> invalidatedMethods = db.invalidatedByOverriddenMethod(overridden); 123 if (invalidatedMethods != null) { 124 while (invalidatedMethods.hasNext()) { 125 int cmid = invalidatedMethods.next(); 126 CompiledMethod im = CompiledMethods.getCompiledMethod(cmid); 127 if (im != null) { // im == null implies that the code has been GCed already 128 invalidate(im); 129 } 130 } 131 db.removeNotOverriddenDependency(overridden); 132 } 133 } 134 135 private void handleSubclassing(RVMClass c) { 136 if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do 137 RVMClass sc = c.getSuperClass(); 138 Iterator<Integer> invalidatedMethods = db.invalidatedBySubclass(sc); 139 if (invalidatedMethods != null) { 140 while (invalidatedMethods.hasNext()) { 141 int cmid = invalidatedMethods.next(); 142 CompiledMethod im = CompiledMethods.getCompiledMethod(cmid); 143 if (im != null) { // im == null implies that the code has been GCed already 144 invalidate(im); 145 } 146 } 147 db.removeNoSubclassDependency(sc); 148 } 149 } 150 151 /** 152 * helper method to invalidate a particular compiled method 153 */ 154 private void invalidate(CompiledMethod cm) { 155 RVMMethod m = cm.getMethod(); 156 if (TRACE || DEBUG) { 157 report("CLDM: Invalidating compiled method " + cm.getId() + "(" + m + ")\n"); 158 } 159 160 // (1) Mark the compiled method as invalid. 161 synchronized (cm) { 162 if (cm.isInvalid()) { 163 if (TRACE || DEBUG) report("\tcmid was alrady invalid; nothing more to do\n"); 164 return; 165 } 166 167 // (2) Apply any code patches to protect invocations already executing 168 // in the soon to be invalid code. 169 ((OptCompiledMethod)cm).applyCodePatches(cm); 170 171 cm.setInvalid(); 172 } 173 174 // (3) Inform its RVMMethod that cm is invalid; 175 // This will update all the dispatching entries (TIB, JTOC, IMTs) 176 // so that no new invocations will reach the invalid compiled code. 177 // It also marks cm as obsolete so it can eventually be reclaimed by GC. 178 m.invalidateCompiledMethod(cm); 179 } 180 181 void report(String s) { 182 if (VM.runningVM) { 183 if (log == null) { 184 if (true || !VM.fullyBooted) { 185 VM.sysWriteln("CLDM: VM not fully booted ", s); 186 return; 187 } 188 try { 189 log = new PrintStream(new FileOutputStream("PREEX_OPTS.TRACE")); 190 } catch (IOException e) { 191 VM.sysWrite("\n\nCLDM: Error opening logging file!!\n\n"); 192 } 193 } 194 } else { 195 System.out.print(s); 196 } 197 } 198 }