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 java.util.Enumeration;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.classloader.RVMField;
019import org.jikesrvm.classloader.RVMMethod;
020import org.jikesrvm.classloader.RVMType;
021import org.jikesrvm.classloader.TypeReference;
022import org.jikesrvm.compilers.opt.driver.CompilerPhase;
023import org.jikesrvm.compilers.opt.ir.IR;
024import org.jikesrvm.compilers.opt.ir.Instruction;
025import org.jikesrvm.compilers.opt.ir.PutField;
026import org.jikesrvm.compilers.opt.ir.PutStatic;
027import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
028import 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 */
042public 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   * @param tref the type
077   * @return {@code true} if the type is a candidate for
078   *  type analysis, i.e. if it's a non-final reference type
079   */
080  private static boolean isCandidate(TypeReference tref) {
081    RVMType t = tref.peekType();
082    if (t == null) return false;
083    if (t.isPrimitiveType() || t.isUnboxedType()) {
084      return false;
085    }
086    if (t.isClassType() && t.asClass().isFinal()) {
087      return false;
088    }
089    if (t.isArrayType()) {
090      return isCandidate(tref.getInnermostElementType());
091    }
092    return true;
093  }
094
095  /**
096   * Gets the a single concrete type for a field, if there is one.
097   *
098   * @param f the field that's of interest
099   * @return the concrete type of a field if available, {@code null}
100   *  otherwise
101   */
102  public static TypeReference getConcreteType(RVMField f) {
103    // don't bother for primitives and arrays of primitives
104    // and friends
105    if (!isCandidate(f.getType())) {
106      return f.getType();
107    }
108    // for some special classes, the flow-insensitive summaries
109    // are INCORRECT due to using the wrong implementation
110    // during boot image writing.  For these special cases,
111    // give up.
112    if (isTrouble(f)) {
113      return null;
114    }
115    if (DEBUG) {
116      TypeReference t = db.getConcreteType(f);
117      if (t != null) VM.sysWriteln("CONCRETE TYPE " + f + " IS " + t);
118    }
119    return db.getConcreteType(f);
120  }
121
122  /**
123   * Record field analysis information for an IR.
124   *
125   * @param ir the governing IR
126   */
127  @Override
128  public void perform(IR ir) {
129    // walk over each instructions.  For each putfield or putstatic,
130    // record the concrete type assigned to a field; or, record
131    // BOTTOM if the concrete type is unknown.
132    for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
133      Instruction s = e.nextElement();
134      if (PutField.conforms(s)) {
135        LocationOperand l = PutField.getLocation(s);
136        RVMField f = l.getFieldRef().peekResolvedField();
137        if (f == null) continue;
138        if (!isCandidate(f.getType())) continue;
139        // a little tricky: we cannot draw any conclusions from inlined
140        // method bodies, since we cannot assume what information,
141        // gleaned from context, does not hold everywhere
142        if (s.position.getMethod() != ir.method) {
143          continue;
144        }
145        Operand value = PutField.getValue(s);
146        if (value.isRegister()) {
147          if (value.asRegister().isPreciseType()) {
148            TypeReference type = value.asRegister().getType();
149            recordConcreteType(ir.method, f, type);
150          } else {
151            recordBottom(ir.method, f);
152          }
153        }
154      } else if (PutStatic.conforms(s)) {
155        LocationOperand l = PutStatic.getLocation(s);
156        RVMField f = l.getFieldRef().peekResolvedField();
157        if (f == null) continue;
158        if (!isCandidate(f.getType())) continue;
159        // a little tricky: we cannot draw any conclusions from inlined
160        // method bodies, since we cannot assume what information,
161        // gleaned from context, does not hold everywhere
162        if (s.position.getMethod() != ir.method) {
163          continue;
164        }
165        Operand value = PutStatic.getValue(s);
166        if (value.isRegister()) {
167          if (value.asRegister().isPreciseType()) {
168            TypeReference type = value.asRegister().getType();
169            recordConcreteType(ir.method, f, type);
170          } else {
171            recordBottom(ir.method, f);
172          }
173        }
174      }
175    }
176  }
177
178  /**
179   * The backing store
180   */
181  private static final FieldDatabase db = new FieldDatabase();
182
183  /**
184   * Records that a method writes an unknown concrete type to a field.
185   *
186   * @param m the writing method
187   * @param f the written field
188   */
189  private static synchronized void recordBottom(RVMMethod m, RVMField f) {
190    // for now, only track private fields
191    if (!f.isPrivate()) {
192      return;
193    }
194    if (isTrouble(f)) {
195      return;
196    }
197    FieldDatabase.FieldDatabaseEntry entry = db.findOrCreateEntry(f);
198    FieldDatabase.FieldWriterInfo info = entry.findMethodInfo(m);
199    if (VM.VerifyAssertions) {
200      if (info == null) {
201        VM.sysWrite("ERROR recordBottom: method " + m + " field " + f);
202      }
203      VM._assert(info != null);
204    }
205    info.setBottom();
206    info.setAnalyzed();
207  }
208
209  /**
210   * Record that a method stores an object of a particular concrete type
211   * to a field.
212   *
213   * @param m the writing method
214   * @param f the written field
215   * @param t the concrete type
216   */
217  private static synchronized void recordConcreteType(RVMMethod m, RVMField f, TypeReference t) {
218    // for now, only track private fields
219    if (!f.isPrivate()) {
220      return;
221    }
222    FieldDatabase.FieldDatabaseEntry entry = db.findOrCreateEntry(f);
223    FieldDatabase.FieldWriterInfo info = entry.findMethodInfo(m);
224    info.setAnalyzed();
225    if (info.isBottom()) {
226      return;
227    }
228    TypeReference oldType = info.concreteType;
229    if (oldType == null) {
230      // set a new concrete type for this field.
231      info.concreteType = t;
232    } else if (oldType != t) {
233      // we've previously determined a DIFFERENT! concrete type.
234      // meet the two types: i.e., change it to bottom.
235      info.setBottom();
236    }
237  }
238
239  /**
240   * For some special classes, the flow-insensitive summaries
241   * are INCORRECT due to using the wrong implementation
242   * during boot image writing.  For these special cases,
243   * give up. TODO: work around this by recomputing the summary at
244   * runtime?
245   *
246   * @param f the field to check
247   * @return {@code true} if the field information may be incorrect
248   */
249  private static boolean isTrouble(RVMField f) {
250    return f.getDeclaringClass() == RVMType.JavaLangStringType;
251  }
252}