Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -70,53 +70,54 @@ public void doInlining() {
public int inlineLuaDivModHelpersWithinLocalBudget() {
return new ImInliner(trans).inlineLuaDivModHelpersWithinLocalBudget();
}

private int optCount = 1;

public void localOptimizations() {
totalCount.clear();
optCount = 1;

removeGarbage();

int finalItr = 0;
for (int i = 1; i <= 10 && optCount > 0; i++) {
optCount = 0;
LocalPlayerContextAnalyzer localPlayerContextAnalyzer = null;
for (OptimizerPass pass : localPasses) {
int count;
if (pass instanceof LocalPlayerAwareOptimizerPass) {
if (localPlayerContextAnalyzer == null) {
localPlayerContextAnalyzer =
new LocalPlayerContextAnalyzer(trans.getImProg());
}
LocalPlayerContextAnalyzer analyzer = localPlayerContextAnalyzer;
LocalPlayerAwareOptimizerPass localPlayerAwarePass =
(LocalPlayerAwareOptimizerPass) pass;
count = timeTaker.measure(
pass.getName(),
() -> localPlayerAwarePass.optimize(trans, analyzer));
} else {
count = timeTaker.measure(pass.getName(), () -> pass.optimize(trans));
// A general mutating pass may invalidate dependency edges.
localPlayerContextAnalyzer = null;
}
optCount += count;
totalCount.put(pass.getName(), totalCount.getOrDefault(pass.getName(), 0) + count);
}

if (optCount > 0) {
removeGarbage();
trans.getImProg().flatten(trans);
}
int optCount = runLocalOptimizationSweep();
if (optCount > 0) {
removeGarbage();
trans.getImProg().flatten(trans);
}

finalItr = i;
WLogger.info("=== Optimization pass: " + i + " opts: " + optCount + " ===");
int cleanupCount = runLocalOptimizationSweep();
if (cleanupCount > 0) {
removeGarbage();
trans.getImProg().flatten(trans);
}
WLogger.info("=== Local optimizations done! Ran " + finalItr + " passes. ===");

WLogger.info("=== Local optimization passes done! Opts: " + (optCount + cleanupCount) + " ===");
totalCount.forEach((k, v) -> WLogger.info("== " + k + ": " + v));
}

private int runLocalOptimizationSweep() {
int optCount = 0;
LocalPlayerContextAnalyzer localPlayerContextAnalyzer = null;
for (OptimizerPass pass : localPasses) {
int count;
if (pass instanceof LocalPlayerAwareOptimizerPass) {
if (localPlayerContextAnalyzer == null) {
localPlayerContextAnalyzer =
new LocalPlayerContextAnalyzer(trans.getImProg());
}
LocalPlayerContextAnalyzer analyzer = localPlayerContextAnalyzer;
LocalPlayerAwareOptimizerPass localPlayerAwarePass =
(LocalPlayerAwareOptimizerPass) pass;
count = timeTaker.measure(
pass.getName(),
() -> localPlayerAwarePass.optimize(trans, analyzer));
} else {
count = timeTaker.measure(pass.getName(), () -> pass.optimize(trans));
// A general mutating pass may invalidate dependency edges.
localPlayerContextAnalyzer = null;
}
optCount += count;
totalCount.put(pass.getName(), totalCount.getOrDefault(pass.getName(), 0) + count);
}
return optCount;
}

public void doNullsetting() {
NullSetter ns = new NullSetter(trans);
ns.optimize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2293,7 +2293,12 @@ public void luaInlinerReusesRegistersAcrossSequentialInlineSites() {
false, Collections.emptyList(),
new RunArgs().with("-lua", "-inline", "-localOptimizations"),
lines.toArray(new String[0]));
String callerBody = getFunctionBody(compiled, "caller");
int callerStart = compiled.indexOf("function caller(");
assertTrue("caller function not found", callerStart >= 0);
int callerBodyStart = compiled.indexOf('\n', callerStart);
int callerEnd = compiled.indexOf("\nend", callerBodyStart);
assertTrue("caller function end not found", callerEnd > callerBodyStart);
String callerBody = compiled.substring(callerBodyStart + 1, callerEnd);
assertFalse("low-pressure sequential helper calls should still inline:\n" + callerBody,
callerBody.contains("helper("));
assertFalse("sequential inline temporaries should reuse registers:\n" + callerBody,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,45 @@ public void repeatedLocalOptimizationStartsANewIteration() {
"a second local-optimization invocation must execute its passes");
}

@Test
public void localOptimizationRunsTwoBoundedSweepsPerInvocation() {
class CountingTimeTaker extends TimeTaker.Default {
int measurements;

@Override
public <T> T measure(String name, java.util.function.Supplier<T> f) {
measurements++;
return f.get();
}
}

WurstModel model = Ast.WurstModel();
ImTranslator translator = new ImTranslator(model, false, new RunArgs());
ImVar value = JassIm.ImVar(model, TypesHelper.imInt(), "value", false);
ImFunction sink = JassIm.ImFunction(model, "sink", JassIm.ImTypeVars(),
JassIm.ImVars(value), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(),
Collections.singletonList(FunctionFlagEnum.IS_NATIVE));
ImFunctionCall call = JassIm.ImFunctionCall(model, sink, JassIm.ImTypeArguments(),
JassIm.ImExprs(JassIm.ImOperatorCall(de.peeeq.wurstscript.WurstOperator.PLUS,
JassIm.ImExprs(JassIm.ImIntVal(1), JassIm.ImIntVal(2)))), false,
de.peeeq.wurstscript.translation.imtranslation.CallType.NORMAL);
ImFunction main = JassIm.ImFunction(model, "main", JassIm.ImTypeVars(), JassIm.ImVars(),
JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(call), Collections.emptyList());
ImFunction config = JassIm.ImFunction(model, "config", JassIm.ImTypeVars(), JassIm.ImVars(),
JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList());
translator.getImProg().getFunctions().add(sink);
translator.getImProg().getFunctions().add(main);
translator.getImProg().getFunctions().add(config);
translator.setMainFunc(main);
translator.setConfigFunc(config);
CountingTimeTaker timeTaker = new CountingTimeTaker();

new ImOptimizer(timeTaker, translator).localOptimizations();

assertEquals(timeTaker.measurements, 16,
"the optimizer should run two fixed sweeps rather than iterating to convergence");
}

@Test
public void luaArithmeticHelperRetryRespectsFunctionLocalBudget() {
WurstModel model = Ast.WurstModel();
Expand Down
Loading