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.inlining;
014
015 import java.util.Stack;
016
017 import org.jikesrvm.VM;
018 import org.jikesrvm.classloader.RVMClass;
019 import org.jikesrvm.classloader.RVMMethod;
020 import org.jikesrvm.classloader.NormalMethod;
021 import org.jikesrvm.classloader.TypeReference;
022 import org.jikesrvm.compilers.opt.driver.OptConstants;
023 import org.jikesrvm.compilers.opt.ir.Call;
024 import org.jikesrvm.compilers.opt.ir.Instruction;
025 import org.jikesrvm.compilers.opt.ir.operand.Operand;
026 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
027 import org.jikesrvm.compilers.opt.OptOptions;
028 import org.jikesrvm.runtime.Entrypoints;
029 import org.vmmagic.pragma.Inline;
030
031 /**
032 * This class provides some utilities that are useful for inlining.
033 */
034 public abstract class InlineTools implements OptConstants {
035
036 /**
037 * Does class <code>A</code> directly implement the interface <code>B</code>?
038 */
039 public static boolean implementsInterface(Class<?> A, Class<?> B) {
040 for (Class<?> i : A.getInterfaces()) {
041 if (i == B) {
042 return true;
043 }
044 }
045 return false;
046 }
047
048 /**
049 * Does the callee method have a body?
050 * @param callee The callee method
051 * @return <code>true</code> if it has bytecodes, false otherwise.
052 */
053 public static boolean hasBody(RVMMethod callee) {
054 return !(callee.isNative() || callee.isAbstract());
055 }
056
057 /**
058 * Does an inlined call to callee need a guard, to protect against
059 * a mispredicted dynamic dispatch?
060 *
061 * @param callee the callee method
062 */
063 public static boolean needsGuard(RVMMethod callee) {
064 return !(callee.isFinal() ||
065 callee.getDeclaringClass().isFinal() ||
066 callee.isPrivate() ||
067 callee.isObjectInitializer() ||
068 callee.isStatic());
069 }
070
071 /**
072 * Is the method CURRENTLY final (not overridden by any subclass)?
073 * Note that this says nothing about whether or not the method will
074 * be overriden by future dynamically loaded classes.
075 */
076 public static boolean isCurrentlyFinal(RVMMethod callee, boolean searchSubclasses) {
077 RVMClass klass = callee.getDeclaringClass();
078 if (klass.isInterface()) {
079 // interface methods are not final.
080 return false;
081 }
082 RVMClass[] subClasses = klass.getSubClasses();
083 if (subClasses.length == 0) {
084 // Currently no subclasses, so trivially not overridden
085 return true;
086 } else if (searchSubclasses) {
087 // see if any subclasses have overridden the method
088 Stack<RVMClass> s = new Stack<RVMClass>();
089 for (RVMClass subClass1 : subClasses) {
090 s.push(subClass1);
091 }
092 while (!s.isEmpty()) {
093 RVMClass subClass = s.pop();
094 if (subClass.findDeclaredMethod(callee.getName(), callee.getDescriptor()) != null) {
095 return false; // found an overridding method
096 }
097 subClasses = subClass.getSubClasses();
098 for (RVMClass subClass1 : subClasses) {
099 s.push(subClass1);
100 }
101 }
102 return true; // didn't find an overridding method in all currently resolved subclasses
103 } else {
104 return false; // could be one, so be conservative.
105 }
106 }
107
108 /**
109 * Given the currently available information at the call site,
110 * what's our best guess on the inlined size of the callee?
111 * @param callee the method to be inlined
112 * @param state the compilation state decribing the call site where it
113 * is to be inlined
114 * @return an inlined size estimate (number of machine code instructions)
115 */
116 public static int inlinedSizeEstimate(NormalMethod callee, CompilationState state) {
117 int sizeEstimate = callee.inlinedSizeEstimate();
118 // Adjust size estimate downward to account for optimizations
119 // that are typically enabled by constant parameters.
120 Instruction callInstr = state.getCallInstruction();
121 int numArgs = Call.getNumberOfParams(callInstr);
122 double reductionFactor = 1.0; // no reduction.
123 OptOptions opts = state.getOptions();
124 for (int i = 0; i < numArgs; i++) {
125 Operand op = Call.getParam(callInstr, i);
126 if (op instanceof RegisterOperand) {
127 RegisterOperand rop = (RegisterOperand)op;
128 TypeReference type = rop.getType();
129 if (type.isReferenceType()) {
130 if (type.isArrayType()) {
131 // Reductions only come from optimization of dynamic type checks; all virtual methods on arrays are defined on Object.
132 if (rop.isPreciseType()) {
133 reductionFactor -= opts.INLINE_PRECISE_REG_ARRAY_ARG_BONUS;
134 } else if (rop.isDeclaredType() && callee.hasArrayWrite() && type.getArrayElementType().isReferenceType()) {
135 // potential to optimize checkstore portion of aastore bytecode on parameter
136 reductionFactor -= opts.INLINE_DECLARED_AASTORED_ARRAY_ARG_BONUS;
137 }
138 } else {
139 // Reductions come from optimization of dynamic type checks and improved inlining of virtual/interface calls
140 if (rop.isPreciseType()) {
141 reductionFactor -= opts.INLINE_PRECISE_REG_CLASS_ARG_BONUS;
142 } else if (rop.isExtant()) {
143 reductionFactor -= opts.INLINE_EXTANT_REG_CLASS_ARG_BONUS;
144 }
145 }
146 }
147 } else if (op.isIntConstant()) {
148 reductionFactor -= opts.INLINE_INT_CONST_ARG_BONUS;
149 } else if (op.isNullConstant()) {
150 reductionFactor -= opts.INLINE_NULL_CONST_ARG_BONUS;
151 } else if (op.isObjectConstant()) {
152 reductionFactor -= opts.INLINE_OBJECT_CONST_ARG_BONUS;
153 }
154 }
155 reductionFactor = Math.max(reductionFactor, 1.0-opts.INLINE_MAX_ARG_BONUS);
156 if (opts.INLINE_CALL_DEPTH_COST != 0.00) {
157 double depthCost = Math.pow(1.0+opts.INLINE_CALL_DEPTH_COST, (double)(state.getInlineDepth()+1));
158 return (int) (sizeEstimate * reductionFactor * depthCost);
159 } else {
160 return (int) (sizeEstimate * reductionFactor);
161 }
162 }
163
164 /**
165 * Should the callee method always be inlined?
166 * Usually this is becuase of a programmer directive (InlinePragma),
167 * but we also use this mechanism to hardwire a couple special cases.
168 *
169 * @param callee the method being considered for inlining
170 * @param state the compilation state of the caller.
171 * @return whether or not the callee should be unconditionally inlined.
172 */
173 public static boolean hasInlinePragma(RVMMethod callee, CompilationState state) {
174 if (callee.hasInlineAnnotation()) {
175 Inline ann = callee.getAnnotation(Inline.class);
176 if (ann == null) {
177 // annotation was lost, assume it was Always
178 return true;
179 }
180
181 switch (ann.value()) {
182 case Always:
183 return true;
184 case AllArgumentsAreConstant: {
185 boolean result = true;
186 Instruction s = state.getCallInstruction();
187 for (int i=0, n=Call.getNumberOfParams(s); i < n; i++) {
188 if (!Call.getParam(s, i).isConstant()) {
189 result = false;
190 break;
191 }
192 }
193 if (result) {
194 return true;
195 }
196 break;
197 }
198 case ArgumentsAreConstant: {
199 boolean result = true;
200 Instruction s = state.getCallInstruction();
201 int[] args = ann.arguments();
202 for (int arg : args) {
203 if (VM.VerifyAssertions) {
204 VM._assert(arg >= 0, "argument is invalid: " + arg);
205 VM._assert(arg < Call.getNumberOfParams(s), "argument is invalid: " + arg);
206 }
207 if (!Call.getParam(s, arg).isConstant()) {
208 result = false;
209 break;
210 }
211 }
212 if (result) {
213 return true;
214 }
215 break;
216 }
217 case AssertionsDisabled: {
218 return !VM.VerifyAssertions;
219 }
220 }
221 }
222 // TODO: clean this hack up
223 // Hack to inline java.lang.VMSystem.arraycopy in the case that
224 // arg 0 isn't an Object
225 if (callee == Entrypoints.sysArrayCopy) {
226 Operand src = Call.getParam(state.getCallInstruction(), 0);
227 return src.getType() != TypeReference.JavaLangObject;
228 }
229 return false;
230 }
231
232 /**
233 * Should the callee method be barred from ever being considered for inlining?
234 *
235 * @param callee the method being considered for inlining
236 * @param state the compilation state of the caller.
237 * @return whether or not the callee should be unconditionally barred
238 * from being inlined.
239 */
240 public static boolean hasNoInlinePragma(RVMMethod callee, CompilationState state) {
241 return callee.hasNoInlinePragma();
242 }
243
244 /**
245 * Is it safe to speculatively inline the callee into the caller?
246 *
247 * Some forms of speculative inlining are unsafe to apply to
248 * methods of the core virtual machine because if we are forced to
249 * invalidate the methods, we will be unable to compile their
250 * replacement method.
251 * The current test is overly conservative, but past attempts at
252 * defining a more precise set of "third rail" classes have
253 * always resulted in missing some (only to discover them later
254 * when Jikes RVM hangs or crashes.)
255 *
256 * @param caller the caller method
257 * @param callee the callee method
258 * @return Whether or not we are allowed to speculatively inline
259 * the callee into the caller.
260 */
261 public static boolean isForbiddenSpeculation(RVMMethod caller, RVMMethod callee) {
262 return caller.getDeclaringClass().isInBootImage() && !callee.getDeclaringClass().getDescriptor().isRVMDescriptor();
263 }
264 }