• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 228

29 Nov 2023 05:00PM UTC coverage: 62.48% (-0.09%) from 62.574%
228

push

circleci

web-flow
Show dialog for choosing game path, cleanup (#1083)

* show dialog for choosing game path

* cleanup code

* remove logs and refactor

* remove confusing mpq error, make some mpq loads readonly

17295 of 27681 relevant lines covered (62.48%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.59
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/TempMerger.java
1
package de.peeeq.wurstscript.intermediatelang.optimizer;
2

3
import com.google.common.base.Preconditions;
4
import com.google.common.collect.Lists;
5
import de.peeeq.wurstscript.jassIm.*;
6
import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
7
import de.peeeq.wurstscript.translation.imtranslation.AssertProperty;
8
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
9
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
10
import de.peeeq.wurstscript.types.TypesHelper;
11
import de.peeeq.wurstscript.utils.MapWithIndexes;
12
import de.peeeq.wurstscript.utils.MapWithIndexes.Index;
13
import de.peeeq.wurstscript.utils.MapWithIndexes.PredIndex;
14
import de.peeeq.wurstscript.utils.Utils;
15
import org.eclipse.jdt.annotation.Nullable;
16

17
import java.util.ArrayList;
18
import java.util.Collection;
19
import java.util.LinkedHashSet;
20
import java.util.Map.Entry;
21
import java.util.Set;
22

23
public class TempMerger implements OptimizerPass {
1✔
24
    private int totalMerged = 0;
1✔
25

26

27
    @Override
28
    public String getName() {
29
        return "Temp variables merged";
1✔
30
    }
31

32
    /**
33
     * @return The amount of merged temp variables
34
     */
35
    @Override
36
    public int optimize(ImTranslator trans) {
37
        ImProg prog = trans.getImProg();
1✔
38
        totalMerged = 0;
1✔
39
        trans.assertProperties(AssertProperty.FLAT, AssertProperty.NOTUPLES);
1✔
40
        prog.clearAttributes();
1✔
41
        for (ImFunction f : ImHelper.calculateFunctionsOfProg(prog)) {
1✔
42
            optimizeFunc(f);
1✔
43
        }
1✔
44
        // flatten the program because we introduced null-statements
45
        prog.flatten(trans);
1✔
46
        return totalMerged;
1✔
47
    }
48

49
    void optimizeFunc(ImFunction f) {
50
        optimizeStatements(f.getBody());
1✔
51
    }
1✔
52

53
    private void optimizeStatements(ImStmts stmts) {
54
        Knowledge kn = new Knowledge();
1✔
55

56
        Replacement replacement = null;
1✔
57
        do { // repeat while there are changes found
58
            kn.clear();
1✔
59
            // this terminates, because each replacement eliminates one set-statement
60
            // FIXME this is no longer true, because assignments which are used more than once are not removed
61
            for (ImStmt s : stmts) {
1✔
62
                if (s instanceof ImSet) {
1✔
63
                    ImSet imSet = (ImSet) s;
1✔
64
                    if (imSet.getRight() instanceof ImVarAccess
1✔
65
                            && imSet.getLeft() instanceof ImVarAccess) {
1✔
66
                        ImVarAccess right = (ImVarAccess) imSet.getRight();
1✔
67
                        ImVarAccess left = (ImVarAccess) imSet.getLeft();
1✔
68
                        if (left.getVar() == right.getVar()) {
1✔
69
                            // statement has the form 'x = x' so remove it
70
                            totalMerged++;
1✔
71
                            imSet.replaceBy(ImHelper.nullExpr());
1✔
72
                            continue;
1✔
73
                        }
74
                    }
75
                }
76

77
                replacement = processStatement(s, kn);
1✔
78
                if (replacement != null) {
1✔
79
                    // do the replacement
80
                    totalMerged++;
1✔
81
                    replacement.apply();
1✔
82
                    break;
1✔
83
                }
84
            }
1✔
85
        } while (replacement != null);
1✔
86

87
        // process nested statements:
88
        for (ImStmt s : stmts) {
1✔
89
            if (s instanceof ImIf) {
1✔
90
                ImIf imIf = (ImIf) s;
1✔
91
                optimizeStatements(imIf.getThenBlock());
1✔
92
                optimizeStatements(imIf.getElseBlock());
1✔
93
            } else if (s instanceof ImLoop) {
1✔
94
                ImLoop imLoop = (ImLoop) s;
1✔
95
                optimizeStatements(imLoop.getBody());
1✔
96
            } else if (s instanceof ImVarargLoop) {
1✔
97
                ImVarargLoop imVarargLoop = (ImVarargLoop) s;
×
98
                optimizeStatements(imVarargLoop.getBody());
×
99
            }
100
        }
1✔
101
    }
1✔
102

103
    private @Nullable Replacement processStatement(ImStmt s, Knowledge kn) {
104
        Replacement rep = getPossibleReplacement(s, kn);
1✔
105
        if (rep != null) {
1✔
106
            return rep;
1✔
107
        }
108
        if (containsFuncCall(s)) {
1✔
109
            kn.invalidateGlobals();
1✔
110
        }
111
        if (readsGlobal(s)) {
1✔
112
            kn.invalidateMutatingExpressions();
1✔
113
        }
114
        if (s instanceof ImSet) {
1✔
115
            ImSet imSet = (ImSet) s;
1✔
116
            if (imSet.getLeft() instanceof ImVarAccess) {
1✔
117
                ImVarAccess va = (ImVarAccess) imSet.getLeft();
1✔
118
                // update the knowledge with the new set statement
119
                kn.update(va.getVar(), imSet);
1✔
120
            } else if (imSet.getLeft() instanceof ImVarArrayAccess) {
1✔
121
                ImVarArrayAccess va = (ImVarArrayAccess) imSet.getLeft();
1✔
122
                kn.invalidateVar(va.getVar());
1✔
123
            } else if (imSet.getLeft() instanceof ImMemberAccess) {
1✔
124
                ImMemberAccess ma = (ImMemberAccess) imSet.getLeft();
1✔
125
                kn.invalidateVar(ma.getVar());
1✔
126
            } else if (imSet.getLeft() instanceof ImTupleSelection) {
1✔
127
                kn.invalidateVar(TypesHelper.getTupleVar(((ImTupleSelection) imSet.getLeft())));
×
128
            }
129
        } else if (s instanceof ImExitwhen || s instanceof ImIf || s instanceof ImLoop || s instanceof ImVarargLoop) {
1✔
130
            kn.clear();
1✔
131
            // TODO this could be more precise for local variables,
132
            // but for now we just forget everything if we see a loop or if statement
133
        }
134
        return null;
1✔
135
    }
136

137
    private @Nullable Replacement getPossibleReplacement(Element elem, Knowledge kn) {
138
        if (kn.isEmpty()) {
1✔
139
            return null;
1✔
140
        }
141
        if (elem instanceof ImVarAccess) {
1✔
142
            ImVarAccess va = (ImVarAccess) elem;
1✔
143
            if (!va.isUsedAsLValue()) {
1✔
144
                return kn.getReplacementIfPossible(va);
1✔
145
            }
146
        } else if (elem instanceof ImLoop) {
1✔
147
            return null;
1✔
148
        } else if (elem instanceof ImVarargLoop) {
1✔
149
            return null;
×
150
        } else if (elem instanceof ImIf) {
1✔
151
            ImIf imIf = (ImIf) elem;
1✔
152
            return getPossibleReplacement(imIf.getCondition(), kn);
1✔
153
        } else if (elem instanceof ImOperatorCall) {
1✔
154
            ImOperatorCall opCall = (ImOperatorCall) elem;
1✔
155
            if (opCall.getOp().isLazy()) {
1✔
156
                // for lazy operators (and, or) we only search the left expression for possible replacements
157
                return getPossibleReplacement(opCall.getArguments().get(0), kn);
1✔
158
            }
159
        }
160
        // process children
161
        for (int i = 0; i < elem.size(); i++) {
1✔
162
            Replacement r = getPossibleReplacement(elem.get(i), kn);
1✔
163
            if (r != null) {
1✔
164
                return r;
1✔
165
            }
166
        }
167
        if (elem instanceof ImFunctionCall) {
1✔
168
            // function call invalidates globals
169
            kn.invalidateGlobals();
1✔
170
        } else if (elem instanceof ImMethodCall) {
1✔
171
            // method call invalidates globals
172
            kn.invalidateGlobals();
×
173
        } else if (elem instanceof ImVarRead) { // this already covers member access as well
1✔
174
            ImVarRead va = (ImVarRead) elem;
1✔
175
            if (va.getVar().isGlobal()) {
1✔
176
                // in case we read a global variable
177
                kn.invalidateMutatingExpressions();
1✔
178
            }
179
        }
180
        return null;
1✔
181
    }
182

183

184
    private boolean containsFuncCall(Element elem) {
185
        if (elem instanceof ImFunctionCall || elem instanceof ImMethodCall) {
1✔
186
            return true;
1✔
187
        }
188
        // process children
189
        boolean r = false;
1✔
190
        for (int i = 0; i < elem.size(); i++) {
1✔
191
            r = containsFuncCall(elem.get(i));
1✔
192
            if (r) {
1✔
193
                return true;
1✔
194
            }
195
        }
196
        return false;
1✔
197
    }
198

199

200
    private boolean readsVar(Element elem, ImVar left) {
201
        if (elem instanceof ImVarRead) {
×
202
            ImVarRead va = (ImVarRead) elem;
×
203
            if (va.getVar() == left) {
×
204
                return true;
×
205
            }
206
        }
207
        if (elem instanceof ImMemberAccess) {
×
208
            if(((ImMemberAccess) elem).getVar() == left) {
×
209
                return true;
×
210
            }
211
        }
212
        // process children
213
        for (int i = 0; i < elem.size(); i++) {
×
214
            if (readsVar(elem.get(i), left)) {
×
215
                return true;
×
216
            }
217
        }
218
        return false;
×
219
    }
220

221

222
    private boolean readsGlobal(Element elem) {
223
        if (elem instanceof ImVarRead) {
1✔
224
            ImVarRead va = (ImVarRead) elem;
1✔
225
            if (va.getVar().isGlobal()) {
1✔
226
                return true;
1✔
227
            }
228
        }
229
        if (elem instanceof ImMemberAccess) {
1✔
230
            return true;
1✔
231
        }
232
        // process children
233
        for (int i = 0; i < elem.size(); i++) {
1✔
234
            if (readsGlobal(elem.get(i))) {
1✔
235
                return true;
1✔
236
            }
237
        }
238

239
        return false;
1✔
240
    }
241

242
    class Replacement {
243
        public final ImSet set;
244
        public final ImVarAccess read;
245

246
        public Replacement(ImSet set, ImVarAccess read) {
1✔
247
            Preconditions.checkArgument(set.getLeft() instanceof ImVarAccess);
1✔
248
            this.set = set;
1✔
249
            this.read = read;
1✔
250
        }
1✔
251

252
        @Override
253
        public String toString() {
254
            return "replace " + read + ", using " + set;
×
255
        }
256

257
        public void apply() {
258
            ImExpr e = set.getRight();
1✔
259
            if (getAssignedVar().attrReads().size() <= 1) {
1✔
260
                // make sure that an impure expression is only evaluated once
261
                // by removing the assignment
262
                set.replaceBy(ImHelper.nullExpr());
1✔
263

264
                // remove variables which are no longer read
265
                for (ImVarRead r : readVariables(set)) {
1✔
266
                    r.getVar().attrReads().remove(r);
1✔
267
                }
1✔
268
            }
269

270
            ImExpr newE = e.copy();
1✔
271
            read.replaceBy(newE);
1✔
272
            // update attrReads:
273
            getAssignedVar().attrReads().remove(read);
1✔
274

275
            // for all the variables in e: add to read
276
            for (ImVarRead r : readVariables(newE)) {
1✔
277
                r.getVar().attrReads().add(r);
1✔
278
            }
1✔
279

280
        }
1✔
281

282
        private ImVar getAssignedVar() {
283
            return ((ImVarAccess) set.getLeft()).getVar();
1✔
284
        }
285

286
    }
287

288
    private Collection<ImVarRead> readVariables(Element e) {
289
        Collection<ImVarRead> result = Lists.newArrayList();
1✔
290
        collectReadVariables(result, e);
1✔
291
        return result;
1✔
292
    }
293

294

295
    private void collectReadVariables(Collection<ImVarRead> result, Element e) {
296
        if (e instanceof ImVarRead) {
1✔
297
            result.add((ImVarRead) e);
1✔
298
        }
299
        for (int i = 0; i < e.size(); i++) {
1✔
300
            collectReadVariables(result, e.get(i));
1✔
301
        }
302
    }
1✔
303

304
    class Knowledge {
1✔
305

306
        class VarKnowledge {
307
            private final ImSet imSet;
308
            private final Set<ImVar> dependsOn;
309
            private final boolean dependsOnGlobals;
310
            private final boolean isMutating;
311

312
            public VarKnowledge(ImSet imSet, Set<ImVar> dependsOn, boolean dependsOnGlobals, boolean isMutating) {
×
313
                this.imSet = imSet;
×
314
                this.dependsOn = dependsOn;
×
315
                this.dependsOnGlobals = dependsOnGlobals;
×
316
                this.isMutating = isMutating;
×
317
            }
×
318

319
            public VarKnowledge(ImSet set) {
1✔
320
                this.imSet = set;
1✔
321
                this.dependsOn = new LinkedHashSet<>();
1✔
322
                collectReadVariables(this.dependsOn, set.getRight());
1✔
323
                boolean containsFuncCall = containsFuncCall(set);
1✔
324
                this.dependsOnGlobals = containsFuncCall || readsGlobal(set.getRight());
1✔
325
                this.isMutating = containsFuncCall;
1✔
326
            }
1✔
327

328
            private void collectReadVariables(Collection<ImVar> result, Element e) {
329
                if (e instanceof ImVarRead) {
1✔
330
                    result.add(((ImVarRead) e).getVar());
1✔
331
                }
332
                if (e instanceof ImMemberAccess) {
1✔
333
                    result.add(((ImMemberAccess) e).getVar());
×
334
                }
335
                for (int i = 0; i < e.size(); i++) {
1✔
336
                    collectReadVariables(result, e.get(i));
1✔
337
                }
338
            }
1✔
339
        }
340

341

342
        private final MapWithIndexes<ImVar, VarKnowledge> currentValues = new MapWithIndexes<>();
1✔
343
        // map from a variable to the keys in currentValues that read it
344
        private final Index<ImVar, ImVar> readBy = currentValues.createMultiIndex((v) -> v.dependsOn);
1✔
345
        // set of keys in currentValues that depend on global state
346
        private final PredIndex<ImVar> globalState = currentValues.createPredicateIndex(v -> v.dependsOnGlobals);
1✔
347
        // set of keys in currentValues that can change global state
348
        private final PredIndex<ImVar> mutating = currentValues.createPredicateIndex(v -> v.isMutating);
1✔
349

350
        public void invalidateGlobals() {
351
            // invalidate all knowledge which might be based on global state
352
            // i.e. using a global var or calling a function
353
            currentValues.removeAll(globalState.lookup());
1✔
354
        }
1✔
355

356
        public void invalidateMutatingExpressions() {
357
            // invalidate all knowledge which can change global state
358
            // i.e. calling a function
359
            currentValues.removeAll(mutating.lookup());
1✔
360
        }
1✔
361

362
        public void clear() {
363
            currentValues.clear();
1✔
364
        }
1✔
365

366
        public @Nullable Replacement getReplacementIfPossible(ImVarAccess va) {
367
            for (Entry<ImVar, VarKnowledge> e : currentValues.entrySet()) {
1✔
368
                if (e.getKey() == va.getVar()) {
1✔
369
                    return new Replacement(e.getValue().imSet, va);
1✔
370
                }
371
            }
1✔
372
            return null;
1✔
373
        }
374

375
        public boolean isEmpty() {
376
            return currentValues.isEmpty();
1✔
377
        }
378

379
        public void update(ImVar left, ImSet set) {
380
            invalidateVar(left);
1✔
381

382
            if (isMergable(left, set.getRight())) {
1✔
383
                // only store local vars which are read exactly once
384
                VarKnowledge k = new VarKnowledge(set);
1✔
385
                currentValues.put(left, k);
1✔
386
            }
387
        }
1✔
388

389
        private boolean isMergable(ImVar left, ImExpr e) {
390
            if (left.isGlobal()) {
1✔
391
                // never merge globals
392
                return false;
1✔
393
            }
394
            if (e instanceof ImVarAccess) {
1✔
395
                ImVarAccess va = (ImVarAccess) e;
1✔
396
                if (va.getVar() == left) {
1✔
397
                    // this is a stupid assignment, ignore it
398
                    return false;
×
399
                }
400
            }
401
            if (left.attrReads().size() == 1) {
1✔
402
                // variable read exactly once can be replaced
403
                return true;
1✔
404
            }
405
            // simple and pure expressions can always be merged
406
            return isSimplePureExpr(e);
1✔
407
        }
408

409
        /**
410
         * invalidates all expression depending on 'left'
411
         */
412
        private void invalidateVar(ImVar left) {
413
            currentValues.remove(left);
1✔
414
            if (left.isGlobal()) {
1✔
415
                invalidateGlobals();
1✔
416
            } else {
417
                currentValues.removeAll(readBy.lookup(left));
1✔
418
            }
419
        }
1✔
420

421
        @Override
422
        public String toString() {
423
            ArrayList<ImVar> keys = Lists.newArrayList(currentValues.keySet());
×
424
            keys.sort(Utils.compareByNameIm());
×
425
            StringBuilder sb = new StringBuilder();
×
426
            for (ImVar v : keys) {
×
427
                ImSet s = currentValues.get(v).imSet;
×
428
                sb.append(v.getName()).append(" -> ").append(s).append(", ");
×
429
            }
×
430
            return sb.toString();
×
431
        }
432

433
    }
434

435
    private boolean isSimplePureExpr(ImExpr e) {
436
        if (e instanceof ImConst) {
1✔
437
            // constants are ok
438
            return true;
1✔
439
        } else if (e instanceof ImVarAccess) {
1✔
440
            // local variables are ok
441
            ImVarAccess va = (ImVarAccess) e;
1✔
442
            return !va.getVar().isGlobal();
1✔
443
        }
444
        return false;
1✔
445
    }
446

447
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc