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