/** * Creates a multi expression for evaluating a method call instance, possible clinit, and all * arguments. This is a precursor for inlining the remainder of a method that does not reference * any parameters. */ private JMultiExpression createMultiExpressionIncludingArgs(JMethodCall x) { JMultiExpression multi = createMultiExpressionForInstanceAndClinit(x); for (int i = 0, c = x.getArgs().size(); i < c; ++i) { JExpression arg = x.getArgs().get(i); ExpressionAnalyzer analyzer = new ExpressionAnalyzer(); analyzer.accept(arg); if (analyzer.hasAssignment() || analyzer.canThrowException()) { multi.addExpressions(arg); } } return multi; }
/** * Creates a lists of expression for evaluating a method call instance, possible clinit, and all * arguments. This is a precursor for inlining the remainder of a method that does not reference * any parameters. */ private List<JExpression> expressionsIncludingArgs(JMethodCall x) { List<JExpression> expressions = Lists.newArrayListWithCapacity(x.getArgs().size() + 2); expressions.add(x.getInstance()); expressions.add(createClinitCall(x)); for (int i = 0, c = x.getArgs().size(); i < c; ++i) { JExpression arg = x.getArgs().get(i); ExpressionAnalyzer analyzer = new ExpressionAnalyzer(); analyzer.accept(arg); if (analyzer.hasAssignment() || analyzer.canThrowException()) { expressions.add(arg); } } return expressions; }
@Override public void endVisit(JParameterRef x, Context ctx) { JParameter param = x.getParameter(); // If the expression has side-effects before a parameter reference, fail if (hasAssignment() || accessesField() || canThrowException()) { succeeded = false; } // If this parameter reference won't always execute, fail if (isInConditional()) { succeeded = false; } // Ensure this parameter is evaluated in the correct order relative to // other parameters. if (parameters.indexOf(param) == currentIndex) { currentIndex++; } else { succeeded = false; } super.endVisit(x, ctx); }
/** * Inline a call to an expression. Returns {@code InlineResult.BLACKLIST} if the method is * deemed not inlineable regardless of call site; {@code InlineResult.DO_NOT_BLACKLIST} * otherwise. */ private InlineResult tryInlineBody( JMethodCall x, Context ctx, List<JExpression> bodyAsExpressionList, boolean ignoringReturn) { if (isTooComplexToInline(bodyAsExpressionList, ignoringReturn)) { return InlineResult.BLACKLIST; } // Do not inline anything that modifies one of its params. ExpressionAnalyzer targetAnalyzer = new ExpressionAnalyzer(); targetAnalyzer.accept(bodyAsExpressionList); if (targetAnalyzer.hasAssignmentToParameter()) { return InlineResult.BLACKLIST; } // Make sure the expression we're about to inline doesn't include a call // to the target method! RecursionCheckVisitor recursionCheckVisitor = new RecursionCheckVisitor(x.getTarget()); recursionCheckVisitor.accept(bodyAsExpressionList); if (recursionCheckVisitor.isRecursive()) { return InlineResult.BLACKLIST; } /* * After this point, it's possible that the method might be inlinable at * some call sites, depending on its arguments. From here on return 'true' * as the method might be inlinable elsewhere. */ /* * There are a different number of parameters than args - this is likely a * result of parameter pruning. Don't consider this call site a candidate. * * TODO: would this be possible in the trivial delegation case? */ if (x.getTarget().getParams().size() != x.getArgs().size()) { // Could not inline this call but the method might be inlineable at a different call site. return InlineResult.DO_NOT_BLACKLIST; } // Run the order check. This verifies that all the parameters are // referenced once and only once, not within a conditionally-executing // expression and before any tricky target expressions, such as: // - assignments to any variable // - expressions that throw exceptions // - field references /* * Ensure correct evaluation order or params relative to each other and to * other expressions. */ OrderVisitor orderVisitor = new OrderVisitor(x.getTarget().getParams()); orderVisitor.accept(bodyAsExpressionList); switch (orderVisitor.checkResults()) { case NO_REFERENCES: /* * A method that doesn't touch any parameters is trivially inlinable (this * covers the empty method case) */ if (!x.hasSideEffects()) { markCallsAsSideEffectFree(bodyAsExpressionList); } new LocalVariableExtruder(getCurrentMethod()).accept(bodyAsExpressionList); List<JExpression> expressions = expressionsIncludingArgs(x); expressions.addAll(bodyAsExpressionList); ctx.replaceMe(JjsUtils.createOptimizedMultiExpression(ignoringReturn, expressions)); return InlineResult.DO_NOT_BLACKLIST; case FAILS: /* * We can still inline in the case where all of the actual arguments are * "safe". They must have no side effects, and also have values which * could not be affected by the execution of any code within the callee. */ for (JExpression arg : x.getArgs()) { ExpressionAnalyzer argAnalyzer = new ExpressionAnalyzer(); argAnalyzer.accept(arg); if (argAnalyzer.hasAssignment() || argAnalyzer.accessesField() || argAnalyzer.createsObject() || argAnalyzer.canThrowException()) { /* * This argument evaluation could affect or be affected by the * callee so we cannot inline here. */ // Could not inline this call but the method is potentially inlineable. return InlineResult.DO_NOT_BLACKLIST; } } // Fall through! case CORRECT_ORDER: default: if (!x.hasSideEffects()) { markCallsAsSideEffectFree(bodyAsExpressionList); } new LocalVariableExtruder(getCurrentMethod()).accept(bodyAsExpressionList); // Replace all params in the target expression with the actual arguments. ParameterReplacer replacer = new ParameterReplacer(x); replacer.accept(bodyAsExpressionList); bodyAsExpressionList.add(0, x.getInstance()); bodyAsExpressionList.add(1, createClinitCall(x)); ctx.replaceMe( JjsUtils.createOptimizedMultiExpression(ignoringReturn, bodyAsExpressionList)); return InlineResult.DO_NOT_BLACKLIST; } }
/** Inline a call to an expression. */ private boolean tryInlineExpression(JMethodCall x, Context ctx, JMultiExpression targetExpr) { /* * Limit inlined methods to multiexpressions of length 2 for now. This * handles the simple { return JVariableRef; } or { expression; return * something; } cases. * * TODO: add an expression complexity analyzer. */ if (targetExpr.getNumberOfExpressions() > 2) { return false; } // Do not inline anything that modifies one of its params. ExpressionAnalyzer targetAnalyzer = new ExpressionAnalyzer(); targetAnalyzer.accept(targetExpr); if (targetAnalyzer.hasAssignmentToParameter()) { return false; } // Make sure the expression we're about to inline doesn't include a call // to the target method! RecursionCheckVisitor recursionCheckVisitor = new RecursionCheckVisitor(x.getTarget()); recursionCheckVisitor.accept(targetExpr); if (recursionCheckVisitor.isRecursive()) { return false; } /* * After this point, it's possible that the method might be inlinable at * some call sites, depending on its arguments. From here on return 'true' * as the method might be inlinable elsewhere. */ /* * There are a different number of parameters than args - this is likely a * result of parameter pruning. Don't consider this call site a candidate. * * TODO: would this be possible in the trivial delegation case? */ if (x.getTarget().getParams().size() != x.getArgs().size()) { return true; } // Run the order check. This verifies that all the parameters are // referenced once and only once, not within a conditionally-executing // expression and before any tricky target expressions, such as: // - assignments to any variable // - expressions that throw exceptions // - field references /* * Ensure correct evaluation order or params relative to each other and to * other expressions. */ OrderVisitor orderVisitor = new OrderVisitor(x.getTarget().getParams()); orderVisitor.accept(targetExpr); /* * A method that doesn't touch any parameters is trivially inlinable (this * covers the empty method case) */ if (orderVisitor.checkResults() == SideEffectCheck.NO_REFERENCES) { JMultiExpression multi = createMultiExpressionIncludingArgs(x); multi.addExpressions(targetExpr); replaceWithMulti(ctx, multi); return true; } /* * We can still inline in the case where all of the actual arguments are * "safe". They must have no side effects, and also have values which * could not be affected by the execution of any code within the callee. */ if (orderVisitor.checkResults() == SideEffectCheck.FAILS) { for (JExpression arg : x.getArgs()) { ExpressionAnalyzer argAnalyzer = new ExpressionAnalyzer(); argAnalyzer.accept(arg); if (argAnalyzer.hasAssignment() || argAnalyzer.accessesField() || argAnalyzer.createsObject() || argAnalyzer.canThrowException()) { /* * This argument evaluation could affect or be affected by the * callee so we cannot inline here. */ return true; } } } // We're safe to inline. JMultiExpression multi = createMultiExpressionForInstanceAndClinit(x); // Replace all params in the target expression with the actual arguments. ParameterReplacer replacer = new ParameterReplacer(x); replacer.accept(targetExpr); multi.addExpressions(targetExpr); replaceWithMulti(ctx, multi); return true; }