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