Skip to content

Commit 044797f

Browse files
committed
Emit less garbage without opts, if it has no side effects
1 parent ab66bd7 commit 044797f

5 files changed

Lines changed: 336 additions & 7 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,11 @@ public JassProg transformProgToJass() {
561561
// translate flattened intermediate lang to jass:
562562

563563
beginPhase(14, "translate to jass");
564+
optimizer.removeGarbage();
565+
imProg.flatten(imTranslator);
566+
imTranslator.removeEmptyPackageInits();
567+
optimizer.removeGarbage();
568+
imProg.flatten(imTranslator);
564569
getImTranslator().calculateCallRelationsAndUsedVariables();
565570
ImToJassTranslator translator =
566571
new ImToJassTranslator(getImProg(), getImTranslator().getCalledFunctions(), getImTranslator().getMainFunc(), getImTranslator().getConfFunc());

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

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import de.peeeq.wurstscript.intermediatelang.optimizer.BranchMerger;
77
import de.peeeq.wurstscript.intermediatelang.optimizer.ConstantAndCopyPropagation;
88
import de.peeeq.wurstscript.intermediatelang.optimizer.LocalMerger;
9+
import de.peeeq.wurstscript.intermediatelang.optimizer.SideEffectAnalyzer;
910
import de.peeeq.wurstscript.intermediatelang.optimizer.SimpleRewrites;
1011
import de.peeeq.wurstscript.jassIm.*;
1112
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
@@ -97,6 +98,9 @@ public void removeGarbage() {
9798
while (changes && iterations++ < 10) {
9899
ImProg prog = trans.imProg();
99100
trans.calculateCallRelationsAndUsedVariables();
101+
SideEffectAnalyzer sideEffectAnalyzer = new SideEffectAnalyzer(prog);
102+
Map<ImFunction, Boolean> sideEffectCache = new HashMap<>();
103+
Set<ImFunction> sideEffectInProgress = new HashSet<>();
100104

101105
// keep only used variables
102106
int globalsBefore = prog.getGlobals().size();
@@ -137,25 +141,30 @@ public void visit(ImSet e) {
137141
if (e.getLeft() instanceof ImVarAccess) {
138142
ImVarAccess va = (ImVarAccess) e.getLeft();
139143
if (!trans.getReadVariables().contains(va.getVar()) && !TRVEHelper.protectedVariables.contains(va.getVar().getName())) {
140-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
144+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer, sideEffectCache, sideEffectInProgress);
145+
replacements.add(Pair.create(e, sideEffects));
141146
}
142147
} else if (e.getLeft() instanceof ImVarArrayAccess) {
143148
ImVarArrayAccess va = (ImVarArrayAccess) e.getLeft();
144149
if (!trans.getReadVariables().contains(va.getVar()) && !TRVEHelper.protectedVariables.contains(va.getVar().getName())) {
145-
// IMPORTANT: removeAll() clears parent references
146-
List<ImExpr> exprs = va.getIndexes().removeAll();
147-
exprs.add(e.getRight());
150+
List<ImExpr> exprs = new ArrayList<>();
151+
for (ImExpr index : va.getIndexes()) {
152+
exprs.addAll(collectSideEffects(index, sideEffectAnalyzer, sideEffectCache, sideEffectInProgress));
153+
}
154+
exprs.addAll(collectSideEffects(e.getRight(), sideEffectAnalyzer, sideEffectCache, sideEffectInProgress));
148155
replacements.add(Pair.create(e, exprs));
149156
}
150157
} else if (e.getLeft() instanceof ImTupleSelection) {
151158
ImVar var = TypesHelper.getTupleVar((ImTupleSelection) e.getLeft());
152159
if(var != null && !trans.getReadVariables().contains(var) && !TRVEHelper.protectedVariables.contains(var.getName())) {
153-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
160+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer, sideEffectCache, sideEffectInProgress);
161+
replacements.add(Pair.create(e, sideEffects));
154162
}
155163
} else if(e.getLeft() instanceof ImMemberAccess) {
156164
ImMemberAccess va = ((ImMemberAccess) e.getLeft());
157165
if (!trans.getReadVariables().contains(va.getVar()) && !TRVEHelper.protectedVariables.contains(va.getVar().getName())) {
158-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
166+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer, sideEffectCache, sideEffectInProgress);
167+
replacements.add(Pair.create(e, sideEffects));
159168
}
160169
}
161170
}
@@ -165,7 +174,9 @@ public void visit(ImSet e) {
165174
for (Pair<ImStmt, List<ImExpr>> pair : replacements) {
166175
changes = true;
167176
ImExpr r;
168-
if (pair.getB().size() == 1) {
177+
if (pair.getB().isEmpty()) {
178+
r = ImHelper.statementExprVoid(JassIm.ImStmts());
179+
} else if (pair.getB().size() == 1) {
169180
r = pair.getB().get(0);
170181
// CRITICAL: Clear parent before reusing the node
171182
r.setParent(null);
@@ -187,4 +198,83 @@ public void visit(ImSet e) {
187198
}
188199
}
189200
}
201+
202+
private List<ImExpr> collectSideEffects(ImExpr expr, SideEffectAnalyzer analyzer, Map<ImFunction, Boolean> cache,
203+
Set<ImFunction> inProgress) {
204+
if (expr == null) {
205+
return Collections.emptyList();
206+
}
207+
if (hasSideEffects(expr, analyzer, cache, inProgress)) {
208+
return Collections.singletonList(expr);
209+
}
210+
return Collections.emptyList();
211+
}
212+
213+
private boolean hasSideEffects(ImExpr expr, SideEffectAnalyzer analyzer, Map<ImFunction, Boolean> cache,
214+
Set<ImFunction> inProgress) {
215+
if (expr instanceof ImDealloc || expr instanceof ImGetStackTrace || expr instanceof ImTypeVarDispatch) {
216+
return true;
217+
}
218+
if (!SideEffectAnalyzer.quickcheckHasSideeffects(expr)) {
219+
return false;
220+
}
221+
for (ImVar var : analyzer.directlySetVariables(expr)) {
222+
if (var.isGlobal()) {
223+
return true;
224+
}
225+
}
226+
for (ImFunction nativeFunc : analyzer.calledNatives(expr)) {
227+
if (!UselessFunctionCallsRemover.isFunctionWithoutSideEffect(nativeFunc.getName())) {
228+
return true;
229+
}
230+
}
231+
for (ImFunction called : analyzer.calledFunctions(expr)) {
232+
if (functionHasSideEffects(called, analyzer, cache, inProgress)) {
233+
return true;
234+
}
235+
}
236+
return false;
237+
}
238+
239+
private boolean functionHasSideEffects(ImFunction func, SideEffectAnalyzer analyzer, Map<ImFunction, Boolean> cache,
240+
Set<ImFunction> inProgress) {
241+
Boolean cached = cache.get(func);
242+
if (cached != null) {
243+
return cached;
244+
}
245+
if (func.isNative()) {
246+
boolean sideEffect = !UselessFunctionCallsRemover.isFunctionWithoutSideEffect(func.getName());
247+
cache.put(func, sideEffect);
248+
return sideEffect;
249+
}
250+
if (!inProgress.add(func)) {
251+
return true;
252+
}
253+
boolean sideEffect = false;
254+
for (ImVar var : analyzer.directlySetVariables(func.getBody())) {
255+
if (var.isGlobal()) {
256+
sideEffect = true;
257+
break;
258+
}
259+
}
260+
if (!sideEffect) {
261+
for (ImFunction nativeFunc : analyzer.calledNatives(func.getBody())) {
262+
if (!UselessFunctionCallsRemover.isFunctionWithoutSideEffect(nativeFunc.getName())) {
263+
sideEffect = true;
264+
break;
265+
}
266+
}
267+
}
268+
if (!sideEffect) {
269+
for (ImFunction called : analyzer.calledFunctions(func.getBody())) {
270+
if (functionHasSideEffects(called, analyzer, cache, inProgress)) {
271+
sideEffect = true;
272+
break;
273+
}
274+
}
275+
}
276+
inProgress.remove(func);
277+
cache.put(func, sideEffect);
278+
return sideEffect;
279+
}
190280
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,29 @@ private static ImStatementExpr inSet(ImSet imSet, ImFunction f) {
455455
+ "\nLHS=" + left + "\nRHS=" + right);
456456
}
457457

458+
boolean allLiteral = true;
459+
for (ImExpr r : rhsLeaves) {
460+
if (!isSimpleLiteral(r)) {
461+
allLiteral = false;
462+
break;
463+
}
464+
}
465+
466+
if (allLiteral) {
467+
for (int i = 0; i < lhsLeaves.size(); i++) {
468+
ImLExpr l = lhsLeaves.get(i);
469+
ImType targetT = l.attrTyp();
470+
ImExpr r = rhsLeaves.get(i);
471+
if (r instanceof ImNull) {
472+
r = ImHelper.defaultValueForComplexType(targetT);
473+
}
474+
l.setParent(null);
475+
r.setParent(null);
476+
stmts.add(JassIm.ImSet(imSet.getTrace(), l, r));
477+
}
478+
return ImHelper.statementExprVoid(stmts);
479+
}
480+
458481
// 4) Evaluate RHS leaves first into temps (preserve side-effect order & alias safety)
459482
List<ImVar> temps = new ArrayList<>(rhsLeaves.size());
460483
for (int i = 0; i < rhsLeaves.size(); i++) {
@@ -485,6 +508,14 @@ private static ImStatementExpr inSet(ImSet imSet, ImFunction f) {
485508
return ImHelper.statementExprVoid(stmts);
486509
}
487510

511+
private static boolean isSimpleLiteral(ImExpr expr) {
512+
return expr instanceof ImBoolVal
513+
|| expr instanceof ImIntVal
514+
|| expr instanceof ImRealVal
515+
|| expr instanceof ImStringVal
516+
|| expr instanceof ImNull;
517+
}
518+
488519
/** Flatten LHS recursively into addressable leaves (ImLExpr), hoisting side-effects */
489520
private static void flattenLhsTuple(ImExpr e, List<ImLExpr> out, ImStmts sideStmts) {
490521
ImExpr x = extractSideEffect(e, sideStmts);

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,154 @@ public ImProg translateProg() {
183183
}
184184
}
185185

186+
public void removeEmptyPackageInits() {
187+
Set<ImFunction> emptyInitFunctions = new HashSet<>();
188+
for (ImFunction initFunc : imProg.getFunctions()) {
189+
if (initFunc.getName().startsWith("init_") && isTrivialInitFunction(initFunc)) {
190+
emptyInitFunctions.add(initFunc);
191+
}
192+
}
193+
if (emptyInitFunctions.isEmpty()) {
194+
return;
195+
}
196+
197+
Map<ImVar, ImFunction> initFuncRefs = collectInitFuncRefs();
198+
removeInitCallsFromMain(emptyInitFunctions, initFuncRefs);
199+
removeInitFuncRefsFromGlobals(emptyInitFunctions);
200+
imProg.getFunctions().removeIf(emptyInitFunctions::contains);
201+
initFuncMap.values().removeIf(emptyInitFunctions::contains);
202+
}
203+
204+
private boolean isTrivialInitFunction(ImFunction initFunc) {
205+
if (initFunc.getBody().isEmpty()) {
206+
return true;
207+
}
208+
if (initFunc.getBody().size() != 1) {
209+
return false;
210+
}
211+
ImStmt stmt = initFunc.getBody().get(0);
212+
if (!(stmt instanceof ImReturn)) {
213+
return false;
214+
}
215+
ImExprOpt returnValue = ((ImReturn) stmt).getReturnValue();
216+
if (returnValue instanceof ImNoExpr) {
217+
return true;
218+
}
219+
return returnValue instanceof ImBoolVal && ((ImBoolVal) returnValue).getValB();
220+
}
221+
222+
private void removeInitCallsFromMain(Set<ImFunction> emptyInitFunctions, Map<ImVar, ImFunction> initFuncRefs) {
223+
ImFunction main = getMainFunc();
224+
if (main == null) {
225+
return;
226+
}
227+
228+
ImFunction native_TriggerAddCondition = getNativeFunc("TriggerAddCondition");
229+
ImFunction native_Condition = getNativeFunc("Condition");
230+
ImFunction native_ClearTrigger = getNativeFunc("TriggerClearConditions");
231+
232+
ImStmts mainBody = main.getBody();
233+
for (int i = 0; i < mainBody.size(); i++) {
234+
ImStmt stmt = mainBody.get(i);
235+
if (stmt instanceof ImFunctionCall) {
236+
ImFunctionCall call = (ImFunctionCall) stmt;
237+
if (emptyInitFunctions.contains(call.getFunc())) {
238+
mainBody.remove(i--);
239+
continue;
240+
}
241+
if (native_TriggerAddCondition != null && native_Condition != null
242+
&& call.getFunc() == native_TriggerAddCondition
243+
&& hasInitCondition(call, native_Condition, emptyInitFunctions, initFuncRefs)) {
244+
if (i + 2 < mainBody.size()
245+
&& mainBody.get(i + 1) instanceof ImIf
246+
&& isTriggerClear(mainBody.get(i + 2), native_ClearTrigger)) {
247+
mainBody.remove(i + 2);
248+
mainBody.remove(i + 1);
249+
mainBody.remove(i--);
250+
}
251+
}
252+
}
253+
}
254+
}
255+
256+
private void removeInitFuncRefsFromGlobals(Set<ImFunction> emptyInitFunctions) {
257+
ImFunction globalInit = getGlobalInitFunc();
258+
if (globalInit == null) {
259+
return;
260+
}
261+
ImStmts body = globalInit.getBody();
262+
for (int i = 0; i < body.size(); i++) {
263+
ImStmt stmt = body.get(i);
264+
if (!(stmt instanceof ImSet)) {
265+
continue;
266+
}
267+
ImExpr right = ((ImSet) stmt).getRight();
268+
if (right instanceof ImFuncRef && emptyInitFunctions.contains(((ImFuncRef) right).getFunc())) {
269+
body.remove(i--);
270+
}
271+
}
272+
}
273+
274+
private Map<ImVar, ImFunction> collectInitFuncRefs() {
275+
ImFunction globalInit = getGlobalInitFunc();
276+
if (globalInit == null) {
277+
return Collections.emptyMap();
278+
}
279+
Map<ImVar, ImFunction> refs = new HashMap<>();
280+
ImStmts body = globalInit.getBody();
281+
for (int i = 0; i < body.size(); i++) {
282+
ImStmt stmt = body.get(i);
283+
if (!(stmt instanceof ImSet)) {
284+
continue;
285+
}
286+
ImSet set = (ImSet) stmt;
287+
if (!(set.getLeft() instanceof ImVarAccess)) {
288+
continue;
289+
}
290+
if (!(set.getRight() instanceof ImFuncRef)) {
291+
continue;
292+
}
293+
refs.put(((ImVarAccess) set.getLeft()).getVar(), ((ImFuncRef) set.getRight()).getFunc());
294+
}
295+
return refs;
296+
}
297+
298+
private boolean hasInitCondition(ImFunctionCall call, ImFunction nativeCondition, Set<ImFunction> emptyInitFunctions,
299+
Map<ImVar, ImFunction> initFuncRefs) {
300+
if (call.getArguments().size() < 2) {
301+
return false;
302+
}
303+
ImExpr conditionExpr = call.getArguments().get(1);
304+
if (!(conditionExpr instanceof ImFunctionCall)) {
305+
return false;
306+
}
307+
ImFunctionCall conditionCall = (ImFunctionCall) conditionExpr;
308+
if (conditionCall.getFunc() != nativeCondition) {
309+
return false;
310+
}
311+
if (conditionCall.getArguments().size() != 1) {
312+
return false;
313+
}
314+
ImExpr argument = conditionCall.getArguments().get(0);
315+
if (argument instanceof ImFuncRef) {
316+
ImFuncRef funcRef = (ImFuncRef) argument;
317+
return emptyInitFunctions.contains(funcRef.getFunc());
318+
}
319+
if (argument instanceof ImVarAccess) {
320+
ImVar var = ((ImVarAccess) argument).getVar();
321+
ImFunction target = initFuncRefs.get(var);
322+
return target != null && emptyInitFunctions.contains(target);
323+
}
324+
return false;
325+
}
326+
327+
private boolean isTriggerClear(ImStmt stmt, ImFunction nativeClearTrigger) {
328+
if (nativeClearTrigger == null) {
329+
return false;
330+
}
331+
return stmt instanceof ImFunctionCall && ((ImFunctionCall) stmt).getFunc() == nativeClearTrigger;
332+
}
333+
186334
/**
187335
* Number all the compiletime functions and expressions,
188336
* so that the one with the lowest number can be executed first.

0 commit comments

Comments
 (0)