Skip to content

Commit d065058

Browse files
authored
Bound local optimizer to two sweeps (#1292)
1 parent 3445a67 commit d065058

3 files changed

Lines changed: 83 additions & 38 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImOptimizer.java

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -70,53 +70,54 @@ public void doInlining() {
7070
public int inlineLuaDivModHelpersWithinLocalBudget() {
7171
return new ImInliner(trans).inlineLuaDivModHelpersWithinLocalBudget();
7272
}
73-
74-
private int optCount = 1;
75-
7673
public void localOptimizations() {
7774
totalCount.clear();
78-
optCount = 1;
7975

8076
removeGarbage();
8177

82-
int finalItr = 0;
83-
for (int i = 1; i <= 10 && optCount > 0; i++) {
84-
optCount = 0;
85-
LocalPlayerContextAnalyzer localPlayerContextAnalyzer = null;
86-
for (OptimizerPass pass : localPasses) {
87-
int count;
88-
if (pass instanceof LocalPlayerAwareOptimizerPass) {
89-
if (localPlayerContextAnalyzer == null) {
90-
localPlayerContextAnalyzer =
91-
new LocalPlayerContextAnalyzer(trans.getImProg());
92-
}
93-
LocalPlayerContextAnalyzer analyzer = localPlayerContextAnalyzer;
94-
LocalPlayerAwareOptimizerPass localPlayerAwarePass =
95-
(LocalPlayerAwareOptimizerPass) pass;
96-
count = timeTaker.measure(
97-
pass.getName(),
98-
() -> localPlayerAwarePass.optimize(trans, analyzer));
99-
} else {
100-
count = timeTaker.measure(pass.getName(), () -> pass.optimize(trans));
101-
// A general mutating pass may invalidate dependency edges.
102-
localPlayerContextAnalyzer = null;
103-
}
104-
optCount += count;
105-
totalCount.put(pass.getName(), totalCount.getOrDefault(pass.getName(), 0) + count);
106-
}
107-
108-
if (optCount > 0) {
109-
removeGarbage();
110-
trans.getImProg().flatten(trans);
111-
}
78+
int optCount = runLocalOptimizationSweep();
79+
if (optCount > 0) {
80+
removeGarbage();
81+
trans.getImProg().flatten(trans);
82+
}
11283

113-
finalItr = i;
114-
WLogger.info("=== Optimization pass: " + i + " opts: " + optCount + " ===");
84+
int cleanupCount = runLocalOptimizationSweep();
85+
if (cleanupCount > 0) {
86+
removeGarbage();
87+
trans.getImProg().flatten(trans);
11588
}
116-
WLogger.info("=== Local optimizations done! Ran " + finalItr + " passes. ===");
89+
90+
WLogger.info("=== Local optimization passes done! Opts: " + (optCount + cleanupCount) + " ===");
11791
totalCount.forEach((k, v) -> WLogger.info("== " + k + ": " + v));
11892
}
11993

94+
private int runLocalOptimizationSweep() {
95+
int optCount = 0;
96+
LocalPlayerContextAnalyzer localPlayerContextAnalyzer = null;
97+
for (OptimizerPass pass : localPasses) {
98+
int count;
99+
if (pass instanceof LocalPlayerAwareOptimizerPass) {
100+
if (localPlayerContextAnalyzer == null) {
101+
localPlayerContextAnalyzer =
102+
new LocalPlayerContextAnalyzer(trans.getImProg());
103+
}
104+
LocalPlayerContextAnalyzer analyzer = localPlayerContextAnalyzer;
105+
LocalPlayerAwareOptimizerPass localPlayerAwarePass =
106+
(LocalPlayerAwareOptimizerPass) pass;
107+
count = timeTaker.measure(
108+
pass.getName(),
109+
() -> localPlayerAwarePass.optimize(trans, analyzer));
110+
} else {
111+
count = timeTaker.measure(pass.getName(), () -> pass.optimize(trans));
112+
// A general mutating pass may invalidate dependency edges.
113+
localPlayerContextAnalyzer = null;
114+
}
115+
optCount += count;
116+
totalCount.put(pass.getName(), totalCount.getOrDefault(pass.getName(), 0) + count);
117+
}
118+
return optCount;
119+
}
120+
120121
public void doNullsetting() {
121122
NullSetter ns = new NullSetter(trans);
122123
ns.optimize();

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2293,7 +2293,12 @@ public void luaInlinerReusesRegistersAcrossSequentialInlineSites() {
22932293
false, Collections.emptyList(),
22942294
new RunArgs().with("-lua", "-inline", "-localOptimizations"),
22952295
lines.toArray(new String[0]));
2296-
String callerBody = getFunctionBody(compiled, "caller");
2296+
int callerStart = compiled.indexOf("function caller(");
2297+
assertTrue("caller function not found", callerStart >= 0);
2298+
int callerBodyStart = compiled.indexOf('\n', callerStart);
2299+
int callerEnd = compiled.indexOf("\nend", callerBodyStart);
2300+
assertTrue("caller function end not found", callerEnd > callerBodyStart);
2301+
String callerBody = compiled.substring(callerBodyStart + 1, callerEnd);
22972302
assertFalse("low-pressure sequential helper calls should still inline:\n" + callerBody,
22982303
callerBody.contains("helper("));
22992304
assertFalse("sequential inline temporaries should reuse registers:\n" + callerBody,

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,45 @@ public void repeatedLocalOptimizationStartsANewIteration() {
15971597
"a second local-optimization invocation must execute its passes");
15981598
}
15991599

1600+
@Test
1601+
public void localOptimizationRunsTwoBoundedSweepsPerInvocation() {
1602+
class CountingTimeTaker extends TimeTaker.Default {
1603+
int measurements;
1604+
1605+
@Override
1606+
public <T> T measure(String name, java.util.function.Supplier<T> f) {
1607+
measurements++;
1608+
return f.get();
1609+
}
1610+
}
1611+
1612+
WurstModel model = Ast.WurstModel();
1613+
ImTranslator translator = new ImTranslator(model, false, new RunArgs());
1614+
ImVar value = JassIm.ImVar(model, TypesHelper.imInt(), "value", false);
1615+
ImFunction sink = JassIm.ImFunction(model, "sink", JassIm.ImTypeVars(),
1616+
JassIm.ImVars(value), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(),
1617+
Collections.singletonList(FunctionFlagEnum.IS_NATIVE));
1618+
ImFunctionCall call = JassIm.ImFunctionCall(model, sink, JassIm.ImTypeArguments(),
1619+
JassIm.ImExprs(JassIm.ImOperatorCall(de.peeeq.wurstscript.WurstOperator.PLUS,
1620+
JassIm.ImExprs(JassIm.ImIntVal(1), JassIm.ImIntVal(2)))), false,
1621+
de.peeeq.wurstscript.translation.imtranslation.CallType.NORMAL);
1622+
ImFunction main = JassIm.ImFunction(model, "main", JassIm.ImTypeVars(), JassIm.ImVars(),
1623+
JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(call), Collections.emptyList());
1624+
ImFunction config = JassIm.ImFunction(model, "config", JassIm.ImTypeVars(), JassIm.ImVars(),
1625+
JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList());
1626+
translator.getImProg().getFunctions().add(sink);
1627+
translator.getImProg().getFunctions().add(main);
1628+
translator.getImProg().getFunctions().add(config);
1629+
translator.setMainFunc(main);
1630+
translator.setConfigFunc(config);
1631+
CountingTimeTaker timeTaker = new CountingTimeTaker();
1632+
1633+
new ImOptimizer(timeTaker, translator).localOptimizations();
1634+
1635+
assertEquals(timeTaker.measurements, 16,
1636+
"the optimizer should run two fixed sweeps rather than iterating to convergence");
1637+
}
1638+
16001639
@Test
16011640
public void luaArithmeticHelperRetryRespectsFunctionLocalBudget() {
16021641
WurstModel model = Ast.WurstModel();

0 commit comments

Comments
 (0)