Skip to content

Commit 949489b

Browse files
committed
t
1 parent 6f0a742 commit 949489b

6 files changed

Lines changed: 342 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/intermediatelang/optimizer/SideEffectAnalyzer.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
99

1010
import java.util.Collection;
11+
import java.util.HashMap;
1112
import java.util.LinkedHashSet;
13+
import java.util.Map;
1214
import java.util.Set;
15+
import java.util.function.Predicate;
1316
import java.util.stream.Collectors;
1417
import java.util.stream.Stream;
1518

@@ -455,4 +458,77 @@ public boolean hasSideEffects(Element elem) {
455458
Set<ImVar> imVars = directlySetVariables(elem);
456459
return natives.size() + directFuncs.size() + imVars.size() > 0;
457460
}
461+
462+
/**
463+
* Checks if the given element has observable side effects.
464+
* Pure natives can be configured via the predicate.
465+
*/
466+
public boolean hasObservableSideEffects(Element elem, Predicate<ImFunction> isNativeWithoutSideEffects) {
467+
return new ObservableSideEffectChecker(isNativeWithoutSideEffects).hasSideEffects(elem);
468+
}
469+
470+
private final class ObservableSideEffectChecker {
471+
private final Predicate<ImFunction> isNativeWithoutSideEffects;
472+
private final Map<ImFunction, Boolean> cache = new HashMap<>();
473+
private final Set<ImFunction> inProgress = new LinkedHashSet<>();
474+
475+
private ObservableSideEffectChecker(Predicate<ImFunction> isNativeWithoutSideEffects) {
476+
this.isNativeWithoutSideEffects = isNativeWithoutSideEffects;
477+
}
478+
479+
private boolean hasSideEffects(Element elem) {
480+
if (!directlySetVariables(elem).isEmpty()) {
481+
return true;
482+
}
483+
for (ImFunction nativeFunc : calledNatives(elem)) {
484+
if (!isNativeWithoutSideEffects.test(nativeFunc)) {
485+
return true;
486+
}
487+
}
488+
for (ImFunction called : calledFunctions(elem)) {
489+
if (functionHasSideEffects(called)) {
490+
return true;
491+
}
492+
}
493+
return false;
494+
}
495+
496+
private boolean functionHasSideEffects(ImFunction func) {
497+
Boolean cached = cache.get(func);
498+
if (cached != null) {
499+
return cached;
500+
}
501+
if (func.isNative()) {
502+
boolean sideEffect = !isNativeWithoutSideEffects.test(func);
503+
cache.put(func, sideEffect);
504+
return sideEffect;
505+
}
506+
if (!inProgress.add(func)) {
507+
return true;
508+
}
509+
boolean sideEffect = hasGlobalSideEffects(func.getBody());
510+
inProgress.remove(func);
511+
cache.put(func, sideEffect);
512+
return sideEffect;
513+
}
514+
515+
private boolean hasGlobalSideEffects(Element elem) {
516+
for (ImVar var : directlySetVariables(elem)) {
517+
if (var.isGlobal()) {
518+
return true;
519+
}
520+
}
521+
for (ImFunction nativeFunc : calledNatives(elem)) {
522+
if (!isNativeWithoutSideEffects.test(nativeFunc)) {
523+
return true;
524+
}
525+
}
526+
for (ImFunction called : calledFunctions(elem)) {
527+
if (functionHasSideEffects(called)) {
528+
return true;
529+
}
530+
}
531+
return false;
532+
}
533+
}
458534
}

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

Lines changed: 27 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,7 @@ public void removeGarbage() {
9798
while (changes && iterations++ < 10) {
9899
ImProg prog = trans.imProg();
99100
trans.calculateCallRelationsAndUsedVariables();
101+
SideEffectAnalyzer sideEffectAnalyzer = new SideEffectAnalyzer(prog);
100102

101103
// keep only used variables
102104
int globalsBefore = prog.getGlobals().size();
@@ -137,25 +139,30 @@ public void visit(ImSet e) {
137139
if (e.getLeft() instanceof ImVarAccess) {
138140
ImVarAccess va = (ImVarAccess) e.getLeft();
139141
if (!trans.getReadVariables().contains(va.getVar()) && !TRVEHelper.protectedVariables.contains(va.getVar().getName())) {
140-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
142+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer);
143+
replacements.add(Pair.create(e, sideEffects));
141144
}
142145
} else if (e.getLeft() instanceof ImVarArrayAccess) {
143146
ImVarArrayAccess va = (ImVarArrayAccess) e.getLeft();
144147
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());
148+
List<ImExpr> exprs = new ArrayList<>();
149+
for (ImExpr index : va.getIndexes()) {
150+
exprs.addAll(collectSideEffects(index, sideEffectAnalyzer));
151+
}
152+
exprs.addAll(collectSideEffects(e.getRight(), sideEffectAnalyzer));
148153
replacements.add(Pair.create(e, exprs));
149154
}
150155
} else if (e.getLeft() instanceof ImTupleSelection) {
151156
ImVar var = TypesHelper.getTupleVar((ImTupleSelection) e.getLeft());
152157
if(var != null && !trans.getReadVariables().contains(var) && !TRVEHelper.protectedVariables.contains(var.getName())) {
153-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
158+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer);
159+
replacements.add(Pair.create(e, sideEffects));
154160
}
155161
} else if(e.getLeft() instanceof ImMemberAccess) {
156162
ImMemberAccess va = ((ImMemberAccess) e.getLeft());
157163
if (!trans.getReadVariables().contains(va.getVar()) && !TRVEHelper.protectedVariables.contains(va.getVar().getName())) {
158-
replacements.add(Pair.create(e, Collections.singletonList(e.getRight())));
164+
List<ImExpr> sideEffects = collectSideEffects(e.getRight(), sideEffectAnalyzer);
165+
replacements.add(Pair.create(e, sideEffects));
159166
}
160167
}
161168
}
@@ -165,7 +172,9 @@ public void visit(ImSet e) {
165172
for (Pair<ImStmt, List<ImExpr>> pair : replacements) {
166173
changes = true;
167174
ImExpr r;
168-
if (pair.getB().size() == 1) {
175+
if (pair.getB().isEmpty()) {
176+
r = ImHelper.statementExprVoid(JassIm.ImStmts());
177+
} else if (pair.getB().size() == 1) {
169178
r = pair.getB().get(0);
170179
// CRITICAL: Clear parent before reusing the node
171180
r.setParent(null);
@@ -187,4 +196,15 @@ public void visit(ImSet e) {
187196
}
188197
}
189198
}
199+
200+
private List<ImExpr> collectSideEffects(ImExpr expr, SideEffectAnalyzer analyzer) {
201+
if (expr == null) {
202+
return Collections.emptyList();
203+
}
204+
if (analyzer.hasObservableSideEffects(expr, func -> func.isNative()
205+
&& UselessFunctionCallsRemover.isFunctionWithoutSideEffect(func.getName()))) {
206+
return Collections.singletonList(expr);
207+
}
208+
return Collections.emptyList();
209+
}
190210
}

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)