log.debug("Aborting unrolling - body writes to condition/repeat deps!");
return;
}
// Attempt to evaluate the loop management code ahead of time.
EvaluationContext context = new EvaluationContext();
context.evaluateStatements(tree.init);
Value condition = context.evaluate(tree.cond);
if (condition == Value.UNKNOWN) {
log.debug("Abort: Condition unknown.");
return;
}
if (!((Boolean) condition.getValue())) {
log.debug("Instantly false for condition...");
// Special case - the loop condition is initially false.
// We can replace the loop with the init statements (Killing any that are variable initialisers or
// depend thereon.
AJCBlock block = tree.getEnclosingBlock();
block.insertBefore(tree, tree.init);
block.remove(tree);
return;
}
int iterations = 0;
while (iterations < UNROLL_LIMIT && condition != Value.UNKNOWN && (Boolean) condition.getValue()) {
context.evaluateExpressionStatements(tree.step);
log.debug("Status: \n{}", context);
condition = context.evaluate(tree.cond);
iterations++;
}
if (iterations >= UNROLL_LIMIT || condition == Value.UNKNOWN) {
log.debug("Abort: Condition unknown or cycles exceeded");
return;
}
// Don't unroll loops with very complicated bodies.
ExpressionComplexityClassifier classifier = new ExpressionComplexityClassifier();
classifier.visitTree(tree.body);
if (classifier.getScore() > UNROLLABLE_BODY_THRESHOLD) {
log.info("Skipping unrollable loop because complexity {} too high: {}", classifier.getScore(), tree);
return;
}
// Replace the for loop with its initialiser, then iterations-many repeats of the body inlining variables that
// are known in the EvaluationContext as we go.
// Some of these might turn out to be pointless. That's okay - we'll run the UnusedAssignmentStripper over them
// in a minute.
List<AJCStatement> statements = tree.init;
context = new EvaluationContext();
// Now we replay the loop evaluation that we know terminates nicely and make substitutions as we go...
context.evaluateStatements(tree.init);
// Strip everything that isn't a loop condition variable from the context.
final HashMap<VarSymbol, Value> currentAssignments = context.getCurrentAssignments();
currentAssignments.keySet().retainAll(condReads);
// The condition is now true. Time for a loop body.
while (iterations > 0) {
// Append the loop body with every known value from context substituted.
statements = statements.appendList(getSubstitutedCopy(tree.body, context));
context.evaluateExpressionStatements(tree.step);
iterations--;
}
tree.getEnclosingBlock().insertBefore(tree, statements);
tree.getEnclosingBlock().remove(tree);