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.specialization;
014
015import java.util.Iterator;
016
017import org.jikesrvm.classloader.RVMMethod;
018import org.jikesrvm.util.ImmutableEntryHashMapRVM;
019import org.jikesrvm.util.HashSetRVM;
020
021/**
022 * Database to store multiple specialized versions for a given method.
023 *
024 * <p> The overall design is very similar to that of the
025 * InvalidationDatabase (see InvalidationDatabase.java)
026 * In this database, the key is the RVMMethod object of the source method
027 * and the value is a method set. The method set is a list of
028 * specialized versions of the method pointed by the key. Specialized
029 * versions are represented by using the SpecializedMethod class.
030 * There is no provision for removing/deleting method versions as classes
031 * are never unloaded and the ClassLoader.compiledMethods[] is never cleaned.
032 */
033public final class SpecializationDatabase {
034
035  private static boolean specializationInProgress;
036
037  private static final HashSetRVM<SpecializedMethod> deferredMethods =
038    new HashSetRVM<SpecializedMethod>();
039
040  private static final ImmutableEntryHashMapRVM<RVMMethod, MethodSet<RVMMethod>> specialVersionsHash =
041      new ImmutableEntryHashMapRVM<RVMMethod, MethodSet<RVMMethod>>();
042
043  /**
044   * Drain the queue of methods waiting for specialized code
045   * generation.
046   */
047  public static synchronized void doDeferredSpecializations() {
048    // prevent recursive entry to this method
049    if (specializationInProgress) {
050      return;
051    }
052    specializationInProgress = true;
053    Iterator<SpecializedMethod> methods = deferredMethods.iterator();
054    while (methods.hasNext()) {
055      SpecializedMethod m = methods.next();
056      if (m.getCompiledMethod() == null) {
057        m.compile();
058        registerCompiledMethod(m);
059      }
060      deferredMethods.remove(m);
061      // since we modified the set, reset the iterator.
062      // TODO: use a better abstraction
063      // (ModifiableSetIterator of some kind?)
064      methods = deferredMethods.iterator();
065    }
066    specializationInProgress = false;
067  }
068
069  // write the new compiled method in the specialized method pool
070  private static void registerCompiledMethod(SpecializedMethod m) {
071    SpecializedMethodPool.registerCompiledMethod(m);
072  }
073
074  /**
075   * @param m the method whose specialized methods are queried
076   * @return an iteration of specialized compiled versions, {@code null}
077   *  if no specialized versions
078   */
079  static synchronized Iterator<SpecializedMethod> getSpecialVersions(RVMMethod m) {
080    MethodSet<RVMMethod> s = specialVersionsHash.get(m);
081    if (s == null) {
082      return null;
083    } else {
084      return s.iterator();
085    }
086  }
087
088  static int getSpecialVersionCount(RVMMethod m) {
089    Iterator<SpecializedMethod> versions = getSpecialVersions(m);
090    int count = 0;
091    if (versions != null) {
092      while (versions.hasNext() && (versions.next() != null)) {
093        count++;
094      }
095    }
096    return count;
097  }
098
099  /**
100   * Records a new specialized method in this database.
101   * Also remember that this method will need to be compiled later,
102   * at the next call to <code> doDeferredSpecializations() </code>
103   *
104   * @param spMethod the method to register
105   */
106  static synchronized void registerSpecialVersion(SpecializedMethod spMethod) {
107    RVMMethod source = spMethod.getMethod();
108    MethodSet<RVMMethod> s = findOrCreateMethodSet(specialVersionsHash, source);
109    s.add(spMethod);
110    deferredMethods.add(spMethod);
111  }
112
113  /**
114   * Looks up the MethodSet corresponding to a given key in the database.
115   *
116   * @param <T> type of the key in the database
117   * @param hash the database
118   * @param key the key
119   * @return the method set for the given key
120   */
121  private static <T> MethodSet<T> findOrCreateMethodSet(ImmutableEntryHashMapRVM<T, MethodSet<T>> hash, T key) {
122    MethodSet<T> result = hash.get(key);
123    if (result == null) {
124      result = new MethodSet<T>(key);
125      hash.put(key, result);
126    }
127    return result;
128  }
129
130  /**
131   * The following defines a set of methods that share a common "key"
132   */
133  static class MethodSet<T> {
134    final T key;
135
136    /**
137     * a set of SpecializedMethod
138     */
139    final HashSetRVM<SpecializedMethod> methods = new HashSetRVM<SpecializedMethod>();
140
141    MethodSet(T key) {
142      this.key = key;
143    }
144
145    void add(SpecializedMethod spMethod) {
146      methods.add(spMethod);
147    }
148
149    public Iterator<SpecializedMethod> iterator() {
150      return methods.iterator();
151    }
152  }
153}