diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/BranchMerger.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/BranchMerger.java
index 6c8aa134f..9548782cd 100644
--- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/BranchMerger.java
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/BranchMerger.java
@@ -1,7 +1,6 @@
package de.peeeq.wurstscript.intermediatelang.optimizer;
import de.peeeq.wurstscript.jassIm.*;
-import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
import java.util.ListIterator;
@@ -11,15 +10,17 @@
*
* the input must be a flattened program
*/
-public class BranchMerger implements OptimizerPass {
+public class BranchMerger implements LocalPlayerAwareOptimizerPass {
private SideEffectAnalyzer sideEffectAnalyzer;
+ private LocalPlayerContextAnalyzer localPlayerContextAnalyzer;
public int branchesMerged = 0;
@Override
- public int optimize(ImTranslator trans) {
+ public int optimize(ImTranslator trans, LocalPlayerContextAnalyzer analyzer) {
branchesMerged = 0;
ImProg prog = trans.getImProg();
this.sideEffectAnalyzer = new SideEffectAnalyzer(prog);
+ this.localPlayerContextAnalyzer = analyzer;
for (ImFunction func : prog.getFunctions()) {
optimizeFunc(func);
@@ -52,6 +53,7 @@ public void visit(ImStmts stmts) {
// if first statement in both branches is the same
// and has no side-effects that could affect the if-condition:
if (firstStmtThen.structuralEquals(firstStmtElse)
+ && !localPlayerContextAnalyzer.isLocalPlayerDependent(ifStmt.getCondition())
&& !sideEffectAnalyzer.mightAffect(firstStmtThen, ifStmt.getCondition())) {
// remove statements
ifStmt.getThenBlock().remove(0);
diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ConstantAndCopyPropagation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ConstantAndCopyPropagation.java
index 32d6141be..e0319732b 100644
--- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ConstantAndCopyPropagation.java
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ConstantAndCopyPropagation.java
@@ -5,7 +5,6 @@
import de.peeeq.wurstscript.WurstOperator;
import de.peeeq.wurstscript.intermediatelang.optimizer.ControlFlowGraph.Node;
import de.peeeq.wurstscript.jassIm.*;
-import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
import de.peeeq.wurstscript.types.TypesHelper;
@@ -17,11 +16,13 @@
import static de.peeeq.wurstscript.WurstOperator.*;
-public class ConstantAndCopyPropagation implements OptimizerPass {
+public class ConstantAndCopyPropagation implements LocalPlayerAwareOptimizerPass {
private int totalPropagated = 0;
+ private @Nullable LocalPlayerContextAnalyzer localPlayerContextAnalyzer;
- public int optimize(ImTranslator trans) {
+ public int optimize(ImTranslator trans, LocalPlayerContextAnalyzer analyzer) {
ImProg prog = trans.getImProg();
+ localPlayerContextAnalyzer = analyzer;
totalPropagated = 0;
for (ImFunction func : ImHelper.calculateFunctionsOfProg(prog)) {
@@ -144,6 +145,11 @@ void optimizeFunc(ImFunction func) {
rewriteCode(cfg, knowledge);
}
+ void optimizeFunc(ImFunction func, LocalPlayerContextAnalyzer analyzer) {
+ localPlayerContextAnalyzer = analyzer;
+ optimizeFunc(func);
+ }
+
private void rewriteCode(ControlFlowGraph cfg, Map knowledge) {
for (Node node : cfg.getNodes()) {
ImStmt stmt = node.getStmt();
@@ -311,7 +317,13 @@ private void analyzeComponent(List scc, Map knowledge) {
// Constant folding is intentionally centralized in SimpleRewrites.
// This pass performs propagation only to keep fold semantics in one place.
- if (right instanceof ImConst) {
+ if (localPlayerContextAnalyzer != null
+ && localPlayerContextAnalyzer.isLocalPlayerDependent(right)) {
+ // Keep GetLocalPlayer-derived values at their original
+ // variable boundary. This is deliberately conservative:
+ // later rewrites must still be able to see that boundary.
+ newValue = null;
+ } else if (right instanceof ImConst) {
newValue = Value.tryValue(right);
} else if (right instanceof ImVarAccess) {
ImVar varRight = ((ImVarAccess) right).getVar();
diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java
index 55a91330d..423e92eba 100644
--- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java
@@ -39,9 +39,11 @@ private void optimize() {
Preconditions.checkArgument(func.getReturnType() instanceof ImVoid, "func must return void");
// run some basic optimizations first:
func.flatten(tr);
- new ConstantAndCopyPropagation().optimizeFunc(func);
+ LocalPlayerContextAnalyzer localPlayerContextAnalyzer =
+ new LocalPlayerContextAnalyzer(tr.getImProg());
+ new ConstantAndCopyPropagation().optimizeFunc(func, localPlayerContextAnalyzer);
// new TempMerger().optimizeFunc(func);
- new LocalMerger().optimizeFunc(func);
+ new LocalMerger().optimizeFunc(func, localPlayerContextAnalyzer);
Set usedVars = UsedVariables.calculate(func);
func.getLocals().removeIf(v -> !usedVars.contains(v));
func.flatten(tr);
diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalMerger.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalMerger.java
index 038305651..f2e02943e 100644
--- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalMerger.java
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalMerger.java
@@ -3,7 +3,6 @@
import de.peeeq.datastructures.GraphInterpreter;
import de.peeeq.wurstscript.intermediatelang.optimizer.ControlFlowGraph.Node;
import de.peeeq.wurstscript.jassIm.*;
-import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
import de.peeeq.wurstscript.types.TypesHelper;
@@ -14,12 +13,14 @@
import java.util.*;
-public class LocalMerger implements OptimizerPass {
+public class LocalMerger implements LocalPlayerAwareOptimizerPass {
private int totalLocalsMerged = 0;
+ private LocalPlayerContextAnalyzer localPlayerContextAnalyzer;
@Override
- public int optimize(ImTranslator trans) {
+ public int optimize(ImTranslator trans, LocalPlayerContextAnalyzer analyzer) {
ImProg prog = trans.getImProg();
+ localPlayerContextAnalyzer = analyzer;
totalLocalsMerged = 0;
for (ImFunction func : de.peeeq.wurstscript.translation.imtranslation.ImHelper.calculateFunctionsOfProg(prog)) {
if (!func.isNative() && !func.isBj()) {
@@ -38,6 +39,11 @@ void optimizeFunc(ImFunction func) {
mergeLocals(livenessInfo, func);
}
+ void optimizeFunc(ImFunction func, LocalPlayerContextAnalyzer analyzer) {
+ localPlayerContextAnalyzer = analyzer;
+ optimizeFunc(func);
+ }
+
private boolean canMerge(ImType a, ImType b) { return a.equalsType(b); }
private void mergeLocals(Map> livenessInfo, ImFunction func) {
@@ -63,6 +69,11 @@ private void mergeLocals(Map> livenessInfo, ImFunction func)
for (ImVar color : colors) {
if (!canMerge(color.getType(), v.getType())) continue;
+ if (localPlayerContextAnalyzer != null
+ && (localPlayerContextAnalyzer.isLocalPlayerDependent(v)
+ || localPlayerContextAnalyzer.isLocalPlayerDependent(color))) {
+ continue;
+ }
boolean conflict = false;
for (ImVar neigh : interference.get(v)) {
diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerAwareOptimizerPass.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerAwareOptimizerPass.java
new file mode 100644
index 000000000..de95b9c5e
--- /dev/null
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerAwareOptimizerPass.java
@@ -0,0 +1,18 @@
+package de.peeeq.wurstscript.intermediatelang.optimizer;
+
+import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
+import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
+
+/**
+ * An optimizer pass which can share a local-player dependency analysis with
+ * adjacent passes that preserve the analysis' conservative facts.
+ */
+public interface LocalPlayerAwareOptimizerPass extends OptimizerPass {
+
+ int optimize(ImTranslator trans, LocalPlayerContextAnalyzer analyzer);
+
+ @Override
+ default int optimize(ImTranslator trans) {
+ return optimize(trans, new LocalPlayerContextAnalyzer(trans.getImProg()));
+ }
+}
diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java
new file mode 100644
index 000000000..ddf2d9947
--- /dev/null
+++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java
@@ -0,0 +1,516 @@
+package de.peeeq.wurstscript.intermediatelang.optimizer;
+
+import de.peeeq.wurstscript.jassIm.*;
+import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.IS_VARARG;
+
+/**
+ * Conservative, flow-insensitive analysis for values and functions which may
+ * depend on {@code GetLocalPlayer()}.
+ *
+ * Optimizers use this analysis as a barrier. False positives only cost an
+ * optimization; false negatives could move synchronized work into a
+ * client-local control-flow region.
+ */
+public final class LocalPlayerContextAnalyzer {
+
+ private final Set localPlayerDependentVars =
+ Collections.newSetFromMap(new IdentityHashMap<>());
+ private final Set localPlayerDependentReturns =
+ Collections.newSetFromMap(new IdentityHashMap<>());
+ private final Set functionsUsingLocalPlayer =
+ Collections.newSetFromMap(new IdentityHashMap<>());
+ private final Set functionsDirectlyUsingLocalPlayer =
+ Collections.newSetFromMap(new IdentityHashMap<>());
+ private final Set indexedElements =
+ Collections.newSetFromMap(new IdentityHashMap<>());
+ private final Set