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, 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 because 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       * <p>
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       * <p>
252       * TODO The current test is overly conservative, but past attempts at
253       * defining a more precise set of "third rail" classes have
254       * always resulted in missing some (only to discover them later
255       * when Jikes RVM hangs or crashes.)
256       *
257       * @param caller the caller method
258       * @param callee the callee method
259       * @return Whether or not we are allowed to speculatively inline
260       *         the callee into the caller.
261       */
262      public static boolean isForbiddenSpeculation(RVMMethod caller, RVMMethod callee) {
263        return caller.getDeclaringClass().isInBootImage() && !callee.getDeclaringClass().getDescriptor().isRVMDescriptor();
264      }
265    }