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 }