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}