66import de .peeeq .wurstscript .intermediatelang .optimizer .BranchMerger ;
77import de .peeeq .wurstscript .intermediatelang .optimizer .ConstantAndCopyPropagation ;
88import de .peeeq .wurstscript .intermediatelang .optimizer .LocalMerger ;
9+ import de .peeeq .wurstscript .intermediatelang .optimizer .SideEffectAnalyzer ;
910import de .peeeq .wurstscript .intermediatelang .optimizer .SimpleRewrites ;
1011import de .peeeq .wurstscript .jassIm .*;
1112import 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}
0 commit comments