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 java.util.Enumeration;
016    
017    import org.jikesrvm.VM;
018    import org.jikesrvm.classloader.RVMField;
019    import org.jikesrvm.classloader.RVMMethod;
020    import org.jikesrvm.classloader.RVMType;
021    import org.jikesrvm.classloader.TypeReference;
022    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
023    import org.jikesrvm.compilers.opt.ir.IR;
024    import org.jikesrvm.compilers.opt.ir.Instruction;
025    import org.jikesrvm.compilers.opt.ir.PutField;
026    import org.jikesrvm.compilers.opt.ir.PutStatic;
027    import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
028    import org.jikesrvm.compilers.opt.ir.operand.Operand;
029    
030    /**
031     * Flow-insensitive, context-insensitive, interprocedural analysis
032     * of fields.
033     *
034     * <ul>
035     * <li> TODO: handle more than just private fields
036     * <li> TODO: make this re-entrant
037     * <li> TODO: better class hierarchy analysis
038     * <li> TODO: context-sensitive or flow-sensitive summaries.
039     * <li> TODO: force eager analysis of methods
040     * </ul>
041     */
042    public final class FieldAnalysis extends CompilerPhase {
043      private static final boolean DEBUG = false;
044    
045      /**
046       * Return this instance of this phase. This phase contains no
047       * per-compilation instance fields.
048       * @param ir not used
049       * @return this
050       */
051      @Override
052      public CompilerPhase newExecution(IR ir) {
053        return this;
054      }
055    
056      @Override
057      public boolean shouldPerform(OptOptions options) {
058        return options.FIELD_ANALYSIS;
059      }
060    
061      @Override
062      public String getName() {
063        return "Field Analysis";
064      }
065    
066      /**
067       * Is a type a candidate for type analysis?
068       * <p> NO iff:
069       * <ul>
070       *    <li> it's a primitive
071       *    <li> it's final
072       *    <li> it's an array of primitive
073       *    <li> it's an array of final
074       * </ul>
075       */
076      private static boolean isCandidate(TypeReference tref) {
077        RVMType t = tref.peekType();
078        if (t == null) return false;
079        if (t.isPrimitiveType() || t.isUnboxedType()) {
080          return false;
081        }
082        if (t.isClassType() && t.asClass().isFinal()) {
083          return false;
084        }
085        if (t.isArrayType()) {
086          return isCandidate(tref.getInnermostElementType());
087        }
088        return true;
089      }
090    
091      /**
092       * Have we determined a single concrete type for a field? If so,
093       * return the concrete type.  Else, return null.
094       */
095      public static TypeReference getConcreteType(RVMField f) {
096        // don't bother for primitives and arrays of primitives
097        // and friends
098        if (!isCandidate(f.getType())) {
099          return f.getType();
100        }
101        // for some special classes, the flow-insensitive summaries
102        // are INCORRECT due to using the wrong implementation
103        // during boot image writing.  For these special cases,
104        // give up.
105        if (isTrouble(f)) {
106          return null;
107        }
108        if (DEBUG) {
109          TypeReference t = db.getConcreteType(f);
110          if (t != null) VM.sysWriteln("CONCRETE TYPE " + f + " IS " + t);
111        }
112        return db.getConcreteType(f);
113      }
114    
115      /**
116       * Record field analysis information for an IR.
117       *
118       * @param ir the governing IR
119       */
120      @Override
121      public void perform(IR ir) {
122        // walk over each instructions.  For each putfield or putstatic,
123        // record the concrete type assigned to a field; or, record
124        // BOTTOM if the concrete type is unknown.
125        for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
126          Instruction s = e.nextElement();
127          if (PutField.conforms(s)) {
128            LocationOperand l = PutField.getLocation(s);
129            RVMField f = l.getFieldRef().peekResolvedField();
130            if (f == null) continue;
131            if (!isCandidate(f.getType())) continue;
132            // a little tricky: we cannot draw any conclusions from inlined
133            // method bodies, since we cannot assume what information,
134            // gleaned from context, does not hold everywhere
135            if (s.position.getMethod() != ir.method) {
136              continue;
137            }
138            Operand value = PutField.getValue(s);
139            if (value.isRegister()) {
140              if (value.asRegister().isPreciseType()) {
141                TypeReference type = value.asRegister().getType();
142                recordConcreteType(ir.method, f, type);
143              } else {
144                recordBottom(ir.method, f);
145              }
146            }
147          } else if (PutStatic.conforms(s)) {
148            LocationOperand l = PutStatic.getLocation(s);
149            RVMField f = l.getFieldRef().peekResolvedField();
150            if (f == null) continue;
151            if (!isCandidate(f.getType())) continue;
152            // a little tricky: we cannot draw any conclusions from inlined
153            // method bodies, since we cannot assume what information,
154            // gleaned from context, does not hold everywhere
155            if (s.position.getMethod() != ir.method) {
156              continue;
157            }
158            Operand value = PutStatic.getValue(s);
159            if (value.isRegister()) {
160              if (value.asRegister().isPreciseType()) {
161                TypeReference type = value.asRegister().getType();
162                recordConcreteType(ir.method, f, type);
163              } else {
164                recordBottom(ir.method, f);
165              }
166            }
167          }
168        }
169      }
170    
171      /**
172       * The backing store
173       */
174      private static final FieldDatabase db = new FieldDatabase();
175    
176      /**
177       * Record that a method writes an unknown concrete type to a field.
178       */
179      private static synchronized void recordBottom(RVMMethod m, RVMField f) {
180        // for now, only track private fields
181        if (!f.isPrivate()) {
182          return;
183        }
184        if (isTrouble(f)) {
185          return;
186        }
187        FieldDatabase.FieldDatabaseEntry entry = db.findOrCreateEntry(f);
188        FieldDatabase.FieldWriterInfo info = entry.findMethodInfo(m);
189        if (VM.VerifyAssertions) {
190          if (info == null) {
191            VM.sysWrite("ERROR recordBottom: method " + m + " field " + f);
192          }
193          VM._assert(info != null);
194        }
195        info.setBottom();
196        info.setAnalyzed();
197      }
198    
199      /**
200       * Record that a method stores an object of a particular concrete type
201       * to a field.
202       */
203      private static synchronized void recordConcreteType(RVMMethod m, RVMField f, TypeReference t) {
204        // for now, only track private fields
205        if (!f.isPrivate()) {
206          return;
207        }
208        FieldDatabase.FieldDatabaseEntry entry = db.findOrCreateEntry(f);
209        FieldDatabase.FieldWriterInfo info = entry.findMethodInfo(m);
210        info.setAnalyzed();
211        if (info.isBottom()) {
212          return;
213        }
214        TypeReference oldType = info.concreteType;
215        if (oldType == null) {
216          // set a new concrete type for this field.
217          info.concreteType = t;
218        } else if (oldType != t) {
219          // we've previously determined a DIFFERENT! concrete type.
220          // meet the two types: i.e., change it to bottom.
221          info.setBottom();
222        }
223      }
224    
225      /**
226       * For some special classes, the flow-insensitive summaries
227       * are INCORRECT due to using the wrong implementation
228       * during boot image writing.  For these special cases,
229       * give up. TODO: work around this by recomputing the summary at
230       * runtime?
231       */
232      private static boolean isTrouble(RVMField f) {
233        return f.getDeclaringClass() == RVMType.JavaLangStringType;
234      }
235    }