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}