Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ Notes; finished-work narrative does not.
type check of the dependencies only as far as the compilation needs it is the larger prize (the check is the
largest block of a cold build), but the validator also marks things the translation reads
(`NamePreservation.preserve`), so it needs its own investigation.
A joint class/function reachability worklist could avoid following every virtual override when only some
receiver classes can exist. It must distinguish a nominal type dependency from an allocation and preserve
generic construction, compile-time objects and externally preserved entry points. An initial castle fight
count found 209 retained method implementations in unallocated classes without direct function references
(5,818 body nodes of 868,171); it does not count their transitive callees or prove all can go. Measure the
total build cost before expanding the analysis: removing class metadata alone did not show a consistent gain.
Whole-program analyses which look at every function now see less, and a dead function can no longer make them
conservative: `LuaTypedValues` proves more values non-nil without the dead writers (a read of a static which only
dead code wrote loses its `__wurst_ensureInt`), `GlobalsInliner` inlines a constant of a package initialiser which
Expand Down Expand Up @@ -91,15 +97,13 @@ Notes; finished-work narrative does not.
keeps: `modification counts: ImFunction` in `jass_im.parseq`). Left:
(1) more removals which remove nothing: one which the test suite shows to find nothing becomes an `assertNoGarbage`,
as the second removal, the one in `optimize` and the one in front of the empty package initialisers did (the one in
front of the first local optimisation is a candidate); (2) the Jass
pipeline analyses the call relation once more after the last removal (`calculateCallRelationsAndReadVariables`
before `ImToJassTranslator`), which the removal has just done; (3) facts which live longer than one removal, valid
front of the first local optimisation is a candidate); (2) facts which live longer than one removal, valid
while `modificationCount()` of the function is the same (it replaces `ImTranslator.functionChanged`, which a pass
can forget to call), so that the first round of a removal does not walk the functions no pass touched; (4) `LocalPlayerContextAnalyzer`, built again for the whole program after each
pass which is not local-player aware (see "Build the local-player analysis fewer times" above); (5) the flatten after the tuples
can forget to call), so that the first round of a removal does not walk the functions no pass touched; (3) `LocalPlayerContextAnalyzer`, built again for the whole program after each
pass which is not local-player aware (see "Build the local-player analysis fewer times" above); (4) the flatten after the tuples
are eliminated is the one which does work (about 565 to 800 functions of castle fight): the producers of
statement expressions (`EliminateTuples`, `SimpleRewrites`, the inliner) could emit statements as the removal does;
(6) a chain of assignments to unread variables (`v1 = v0; v2 = v1; ...`, nothing reads the last) takes a round for
(5) a chain of assignments to unread variables (`v1 = v0; v2 = v1; ...`, nothing reads the last) takes a round for
each link, and a round costs the functions which changed (a walk of each, the rewrite of its statement list, its
locals), so the cost grows with the square of the length. Measured in a unit test (the checks of unit-test mode on,
one removal): a chain inside one function takes 0.24 s for 1,000 links, 1.5 s for 4,000 and 16.7 s for 16,000; with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,7 @@ public JassProg transformProgToJass() {
optimizer.removeGarbage();
timeTaker.endPhase();
imTranslator.assertFlat("before it is translated to Jass, which has no statement expression");
timeTaker.beginPhase("call relations");
getImTranslator().calculateCallRelationsAndReadVariables();
timeTaker.endPhase();
// The final garbage removal leaves current call relations; nothing modifies the program before emission.
timeTaker.beginPhase("print IM as Jass");
ImToJassTranslator translator =
new ImToJassTranslator(getImProg(), getImTranslator().getCalledFunctions(), getImTranslator().getMainFunc(), getImTranslator().getConfFunc());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.google.common.io.Files;
import de.peeeq.wurstio.TimeTaker;
import de.peeeq.wurstio.UtilsIO;
import de.peeeq.wurstio.WurstCompilerJassImpl;
import de.peeeq.wurstscript.gui.WurstGuiCliImpl;
import de.peeeq.wurstscript.RunArgs;
import de.peeeq.wurstscript.ast.Ast;
import de.peeeq.wurstscript.ast.Element;
Expand Down Expand Up @@ -37,6 +39,66 @@

public class OptimizerTests extends WurstScriptTest {

/** The backend must consume the final removal's current graph, including callbacks and preserved entry points. */
@Test
public void jassTranslationReusesTheFinalGarbageRemovalsCallRelations() {
class CapturingTimeTaker extends TimeTaker.Default {
final java.util.Deque<String> phases = new java.util.ArrayDeque<>();
WurstCompilerJassImpl compiler;
com.google.common.collect.Multimap<ImFunction, ImFunction> finalRelations;

@Override public void beginPhase(String name) {
phases.push(name);
}

@Override public void endPhase() {
String finished = phases.pop();
if (finished.equals("remove garbage") && "translate to jass".equals(phases.peek())) {
finalRelations = compiler.getImTranslator().getCalledFunctions();
}
}
}
for (boolean optimize : new boolean[]{false, true}) {
CapturingTimeTaker timeTaker = new CapturingTimeTaker();
WurstGuiCliImpl gui = new WurstGuiCliImpl();
RunArgs args = optimize ? new RunArgs().with("-inline", "-localOptimizations", "-opt") : new RunArgs();
WurstCompilerJassImpl compiler = new WurstCompilerJassImpl(timeTaker, null, gui, null, args);
timeTaker.compiler = compiler;
String source = String.join("\n",
"package Test",
"native register(code callback)",
"native effect()",
"@noinline function callback()",
" effect()",
"@preserveName function externalEntry()",
" effect()",
"function unused()",
" effect()",
"init",
" register(function callback)");
WurstModel model = parseFiles(Collections.emptyList(), List.of(new CU("ReuseRelations.wurst", source)),
false, compiler);
compiler.checkProg(model);
assertTrue(gui.getErrorList().isEmpty(), gui.getErrorList().toString());
compiler.translateProgToIm(model);
compiler.runCompiletime(org.wurstscript.projectconfig.WurstProjectConfigData.empty(), false, false);
compiler.transformProgToJass();
assertTrue(gui.getErrorList().isEmpty(), gui.getErrorList().toString());
assertNotNull(timeTaker.finalRelations, "the final removal must leave a call graph");
assertSame(compiler.getImTranslator().getCalledFunctions(), timeTaker.finalRelations,
"Jass emission must reuse the graph already calculated by the final garbage removal");
java.util.Set<String> callees = timeTaker.finalRelations.values().stream()
.map(f -> f.getTrace() instanceof de.peeeq.wurstscript.ast.FunctionDefinition definition
? definition.getName() : f.getName())
.collect(java.util.stream.Collectors.toSet());
assertTrue(callees.contains("callback"), callees.toString());
assertTrue(callees.contains("effect"), callees.toString());
assertFalse(callees.contains("unused"), callees.toString());
assertTrue(timeTaker.finalRelations.keySet().stream().anyMatch(f -> f.getName().equals("externalEntry")),
"the graph must retain the externally preserved entry point");
}
}

/**
* A common.j converter changes and reads nothing, so a call whose result nothing uses goes. The list is by name,
* and 40 converters of the Reforged natives were once missing from it: an unused native call stayed in the script.
Expand Down
Loading