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;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.classloader.RVMClass;
017import org.jikesrvm.classloader.RVMField;
018import org.jikesrvm.classloader.RVMMethod;
019import org.jikesrvm.classloader.TypeReference;
020import org.jikesrvm.util.ImmutableEntryHashMapRVM;
021
022/**
023 * database to hold field-level information
024 * this is a mapping from RVMField -> FieldDatabaseEntry
025 */
026final class FieldDatabase {
027  private static final boolean DEBUG = false;
028
029  private final ImmutableEntryHashMapRVM<RVMField, FieldDatabase.FieldDatabaseEntry> db =
030    new ImmutableEntryHashMapRVM<RVMField, FieldDatabase.FieldDatabaseEntry>();
031
032  FieldDatabaseEntry findOrCreateEntry(RVMField f) {
033    FieldDatabaseEntry e = db.get(f);
034    if (e == null) {
035      e = new FieldDatabaseEntry(f);
036      db.put(f, e);
037    }
038    return e;
039  }
040
041  /**
042   * @param f the field in question
043   * @return the concrete type of a field, or null if none determined
044   */
045  public TypeReference getConcreteType(RVMField f) {
046    FieldDatabaseEntry e = db.get(f);
047    if (e == null) return null;
048
049    if (e.allMethodsAreAnalyzed()) {
050      return e.getConcreteType();
051    }
052
053    return null;
054  }
055
056  /**
057   * A data structure holding information about a field.
058   */
059  static final class FieldDatabaseEntry {
060    private final ImmutableEntryHashMapRVM<RVMMethod, FieldWriterInfo> summaries;
061    /** have we already determined all methods are analyzed? */
062    boolean cachedAllAnalyzed;
063    /** cache a copy of the concrete type already determined for this field */
064    TypeReference cachedConcreteType;
065
066    FieldWriterInfo findMethodInfo(RVMMethod m) {
067      return summaries.get(m);
068    }
069
070    // are all methods that may write this field analyzed already?
071    boolean allMethodsAreAnalyzed() {
072
073      if (cachedAllAnalyzed) return true;
074      for (FieldWriterInfo info : summaries.values()) {
075        if (!info.isAnalyzed()) return false;
076      }
077      cachedAllAnalyzed = true;
078      return true;
079    }
080
081    // return the concrete type of the field; null if no consistent
082    // concrete type has yet been determined.
083    TypeReference getConcreteType() {
084      if (cachedConcreteType != null) return cachedConcreteType;
085      TypeReference result = null;
086      for (FieldWriterInfo info : summaries.values()) {
087        if (!info.isAnalyzed()) return null;
088        if (info.isBottom()) return null;
089        TypeReference t = info.concreteType;
090        if (result != null) {
091          // make sure that all methods set the same concrete type.
092          if (result != t) return null;
093        } else {
094          result = info.concreteType;
095        }
096      }
097      // found a concrete type.  cache it and return it.
098      cachedConcreteType = result;
099      return result;
100    }
101
102    // create a new FieldDatabaseEntry, with a FieldWriterInfo
103    // for each method that may write this field
104    FieldDatabaseEntry(RVMField f) {
105      if (VM.VerifyAssertions) VM._assert(f.isPrivate());
106
107      RVMClass klass = f.getDeclaringClass();
108      summaries = new ImmutableEntryHashMapRVM<RVMMethod, FieldWriterInfo>(1);
109
110      // walk thru each method of the declaring class.
111      // If a method m may write to f, then create a FieldWriterInfo for m
112      for (RVMMethod m : klass.getDeclaredMethods()) {
113        if (m.mayWrite(f)) {
114          FieldWriterInfo info = new FieldWriterInfo();
115          if (DEBUG) debug("New summary METHOD " + m + " FIELD " + f + " INFO " + info);
116          summaries.put(m, info);
117        }
118      }
119    }
120  } // class FieldDatabaseEntry
121
122  /**
123   * A data structure holding information about a particular
124   * {@code <method,field>}  combination, where the method
125   * may write the field.
126   */
127  static final class FieldWriterInfo {
128    static final int BOTTOM = 0x1;
129    static final int ANALYZED = 0x2;
130    int status;
131    TypeReference concreteType;
132
133    void setBottom() {
134      status |= BOTTOM;
135    }
136
137    void setAnalyzed() {
138      status |= ANALYZED;
139    }
140
141    boolean isBottom() {
142      return (status & BOTTOM) != 0;
143    }
144
145    boolean isAnalyzed() {
146      return (status & ANALYZED) != 0;
147    }
148  }
149
150  // print a debug message
151  private static void debug(String s) {
152    if (DEBUG) VM.sysWrite(s + " \n");
153  }
154}