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.util.Iterator;
016import org.jikesrvm.classloader.RVMClass;
017import org.jikesrvm.classloader.RVMMethod;
018import org.jikesrvm.util.HashMapRVM;
019import org.jikesrvm.util.HashSetRVM;
020
021/**
022 * This class holds the dependencies that define invalidation
023 * requirements for the opt compiled methods.
024 *
025 * <p> Currently we only support 2 kinds of dependencies:
026 *   The set of compiled method id's that depend on a RVMMethod
027 *   not being overridden.
028 *   The set of compiled method id's that depend on a RVMClass
029 *   having no subclasses
030 *
031 * <p> Note we track by compiled method ids instead of pointers to
032 *     compiled methods because we don't have weak pointers.
033 *     We don't want the invalidaton database to keep code alive!
034 *     This would be an ideal use of weak references if we had them.
035 *
036 * <p> TODO: In the future, we should think about implementing a general
037 *       dependency mechanism.
038 *   See Chambers, Dean, Grove in ICSE-17 (1995) for one possible design
039 *   and pointers to related work.
040 */
041public final class InvalidationDatabase {
042
043  /**
044   * A mapping from RVMMethod to MethodSet: holds the set of methods which
045   * depend on a particular method being "final"
046   */
047  private final HashMapRVM<RVMMethod, MethodSet> nonOverriddenHash =
048    new HashMapRVM<RVMMethod, MethodSet>();
049
050  /**
051   * A mapping from RVMClass to MethodSet: holds the set of methods which
052   * depend on a particular class being "final"
053   */
054  private final HashMapRVM<RVMClass, MethodSet> noSubclassHash =
055    new HashMapRVM<RVMClass, MethodSet>();
056
057  /////////////////////
058  // (1) Dependency on a particular RVMMethod not being overridden.
059  /////////////////////
060
061  /**
062   * Returns an iteration of CMID's (compiled method ids) that are dependent
063   * on the argument RVMMethod not being overridden. If there are no dependent
064   * methods, {@code null} will be returned.<p>
065   *
066   * NOTE: {@code null} is used instead of {@code EmptyIterator.getInstance}
067   * as part of delicate dance to avoid recursive classloading.
068   *
069   * @param m a method that can be overridden
070   * @return an iterator of CMIDs or {@code null}
071   */
072  public Iterator<Integer> invalidatedByOverriddenMethod(RVMMethod m) {
073    MethodSet s = nonOverriddenHash.get(m);
074    return (s == null) ? null : s.iterator();
075  }
076
077  /**
078   * Records that if a particular RVMMethod method is ever overridden, then
079   * the CompiledMethod encoded by the cmid must be invalidated.
080   *
081   * @param source a method
082   * @param dependent_cmid id of the method that must be invalidated
083   */
084  public void addNotOverriddenDependency(RVMMethod source, int dependent_cmid) {
085    MethodSet s = findOrCreateMethodSet(nonOverriddenHash, source);
086    s.add(dependent_cmid);
087  }
088
089  /**
090   * Deletes a NotOverriddenDependency.
091   * No effect if the dependency doesn't exist..
092   *
093   * @param source a method
094   * @param dependent_cmid id of the method that must be invalidated
095   */
096  public void removeNotOverriddenDependency(RVMMethod source, int dependent_cmid) {
097    MethodSet s = nonOverriddenHash.get(source);
098    if (s != null) {
099      s.remove(dependent_cmid);
100    }
101  }
102
103  /**
104   * Delete all NotOverridden dependencies on the argument RVMMethod
105   *
106   * @param source a method
107   */
108  public void removeNotOverriddenDependency(RVMMethod source) {
109    nonOverriddenHash.remove(source);
110  }
111
112  /////////////////////
113  // (2) Dependency on a particular RVMClass not having any subclasses.
114  /////////////////////
115
116  /**
117   * Returns an iteration of CMID's (compiled method ids) that are dependent
118   * on the argument RVMMethod not having any subclasses. If there are no
119   * dependent methods, {@code null} will be returned.<p>
120   *
121   * NOTE: {@code null} is used instead of {@code EmptyIterator.getInstance}
122   * as part of delicate dance to avoid recursive classloading.
123   *
124   * @param m a method that can be overridden
125   * @return an iterator of CMIDs or {@code null}
126   */
127  public Iterator<Integer> invalidatedBySubclass(RVMClass m) {
128    MethodSet s = noSubclassHash.get(m);
129    return (s == null) ? null : s.iterator();
130  }
131
132  /**
133   * Records that if a particular RVMClass ever has a subclass, then
134   * the CompiledMethod encoded by the cmid must be invalidated.
135   *
136   * @param source a class
137   * @param dependent_cmid id of the method that must be invalidated
138   */
139  public void addNoSubclassDependency(RVMClass source, int dependent_cmid) {
140    MethodSet s = findOrCreateMethodSet(noSubclassHash, source);
141    s.add(dependent_cmid);
142  }
143
144  /**
145   * Delete as NoSubclassDependency. No effect if the dependency doesn't exist..
146   *
147   * @param source a class
148   * @param dependent_cmid id of the method that must be invalidated
149   */
150  public void removeNoSubclassDependency(RVMClass source, int dependent_cmid) {
151    MethodSet s = noSubclassHash.get(source);
152    if (s != null) {
153      s.remove(dependent_cmid);
154    }
155  }
156
157  /**
158   * Deletes all NoSubclass dependencies on the argument RVMClass.
159   *
160   * @param source class whose dependencies are to be removed
161   */
162  public void removeNoSubclassDependency(RVMClass source) {
163    noSubclassHash.remove(source);
164  }
165
166  /**
167   * Looks up the MethodSet corresponding to a given key in the database.
168   *
169   * @param <T> type of the key in the database
170   * @param hash the database
171   * @param key the key
172   * @return the method set for the given key
173   */
174  private <T> MethodSet findOrCreateMethodSet(HashMapRVM<T, MethodSet> hash, T key) {
175    MethodSet result = hash.get(key);
176    if (result == null) {
177      result = new MethodSet(key);
178      hash.put(key, result);
179    }
180    return result;
181  }
182
183  /**
184   * The following defines a set of methods that share a common "key"
185   */
186  static final class MethodSet {
187    final Object key;
188    /**
189     * a set of cmids (Integers)
190     */
191    final HashSetRVM<Integer> methods = new HashSetRVM<Integer>();
192
193    MethodSet(Object key) {
194      this.key = key;
195    }
196
197    void add(int cmid) {
198      methods.add(cmid);
199    }
200
201    void remove(int cmid) {
202      methods.remove(cmid);
203    }
204
205    public Iterator<Integer> iterator() {
206      return methods.iterator();
207    }
208  }
209}