Skip to content

Commit edc2627

Browse files
authored
optimizers for lua (#1004)
lua does not eliminate as many constructs from the intermediate language as JASS. As a result, the optimizers need to support these constructs: classes, methods, vararg loops
1 parent 449489f commit edc2627

File tree

16 files changed

+207
-31
lines changed

16 files changed

+207
-31
lines changed

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import de.peeeq.wurstscript.luaAst.LuaCompilationUnit;
2323
import de.peeeq.wurstscript.parser.WPos;
2424
import de.peeeq.wurstscript.translation.imoptimizer.ImOptimizer;
25+
import de.peeeq.wurstscript.translation.imtojass.ImAttrType;
2526
import de.peeeq.wurstscript.translation.imtojass.ImToJassTranslator;
2627
import de.peeeq.wurstscript.translation.imtranslation.*;
2728
import de.peeeq.wurstscript.translation.lua.translation.LuaTranslator;
@@ -802,6 +803,8 @@ public void setMapFile(Optional<File> mapFile) {
802803

803804
public LuaCompilationUnit transformProgToLua() {
804805

806+
ImAttrType.setWurstClassType(null);
807+
int stage;
805808
if (runArgs.isNoDebugMessages()) {
806809
beginPhase(3, "remove debug messages");
807810
DebugMessageRemover.removeDebugMessages(imProg);
@@ -812,9 +815,51 @@ public LuaCompilationUnit transformProgToLua() {
812815
new StackTraceInjector2(imProg, imTranslator).transform(timeTaker);
813816
}
814817
}
818+
ImTranslator imTranslator2 = getImTranslator();
819+
ImOptimizer optimizer = new ImOptimizer(timeTaker, imTranslator2);
820+
// inliner
821+
stage = 5;
822+
if (runArgs.isInline()) {
823+
beginPhase(5, "inlining");
824+
optimizer.doInlining();
825+
imTranslator2.assertProperties();
826+
827+
printDebugImProg("./test-output/lua/im " + stage++ + "_afterinline.im");
828+
}
829+
830+
optimizer.removeGarbage();
831+
imProg.flatten(imTranslator);
832+
833+
stage = 10;
834+
if (runArgs.isLocalOptimizations()) {
835+
beginPhase(10, "local optimizations");
836+
optimizer.localOptimizations();
837+
}
815838

839+
printDebugImProg("./test-output/lua/im " + stage++ + "_afterlocalopts.im");
840+
841+
optimizer.removeGarbage();
842+
imProg.flatten(imTranslator);
843+
844+
// Re-run to avoid #883
845+
optimizer.removeGarbage();
846+
imProg.flatten(imTranslator);
847+
848+
printDebugImProg("./test-output/lua/im " + stage++ + "_afterremoveGarbage1.im");
849+
850+
stage = 12;
851+
if (runArgs.isOptimize()) {
852+
beginPhase(12, "froptimize");
853+
optimizer.optimize();
854+
855+
optimizer.removeGarbage();
856+
imProg.flatten(imTranslator);
857+
printDebugImProg("./test-output/lua/im " + stage++ + "_afteroptimize.im");
858+
}
859+
beginPhase(13, "translate to lua");
816860
LuaTranslator luaTranslator = new LuaTranslator(imProg, imTranslator);
817861
LuaCompilationUnit luaCode = luaTranslator.translate();
862+
ImAttrType.setWurstClassType(TypesHelper.imInt());
818863
return luaCode;
819864
}
820865
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ConstantAndCopyPropagation.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.peeeq.wurstscript.intermediatelang.optimizer.ControlFlowGraph.Node;
55
import de.peeeq.wurstscript.jassIm.*;
66
import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
7+
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
78
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
89
import io.vavr.Tuple2;
910
import io.vavr.collection.HashMap;
@@ -18,8 +19,10 @@ public int optimize(ImTranslator trans) {
1819
ImProg prog = trans.getImProg();
1920

2021
totalPropagated = 0;
21-
for (ImFunction func : prog.getFunctions()) {
22-
optimizeFunc(func);
22+
for (ImFunction func : ImHelper.calculateFunctionsOfProg(prog)) {
23+
if (!func.isNative() && !func.isBj()) {
24+
optimizeFunc(func);
25+
}
2326
}
2427
return totalPropagated;
2528
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ControlFlowGraph.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public Node setName(String name) {
5050
private Map<ImStmt, Node> nodes = new HashMap<>();
5151
private Map<ImIf, Node> ifEnd = new HashMap<>();
5252
private Map<ImLoop, Node> loopEnd = new HashMap<>();
53+
private Map<ImVarargLoop, Node> varargLoopEnd = new HashMap<>();
5354
private List<Node> nodeList = new ArrayList<>();
5455

5556
public ControlFlowGraph(ImStmts stmts) {
@@ -72,6 +73,17 @@ private void buildCfg(ImStmts stmts) {
7273
Node endloopNode = getEndloopNode(imLoop);
7374
nodeList.add(endloopNode);
7475
getSuccessors(imLoop, i).forEach(succ -> addSuccessor(endloopNode, succ));
76+
} else if(s instanceof ImVarargLoop) {
77+
ImVarargLoop imVarargLoop = (ImVarargLoop) s;
78+
ImStmts body = imVarargLoop.getBody();
79+
buildCfg(body);
80+
if(!body.isEmpty()) {
81+
addSuccessor(current, getNode(body.get(0)));
82+
}
83+
Node endloopNode = getEndVarargLoopNode(imVarargLoop);
84+
addSuccessor(current, endloopNode);
85+
nodeList.add(endloopNode);
86+
getSuccessors(imVarargLoop, i).forEach(succ -> addSuccessor(endloopNode, succ));
7587
} else if (s instanceof ImIf) {
7688
ImIf imIf = (ImIf) s;
7789
ImStmts thenBlock = imIf.getThenBlock();
@@ -147,6 +159,9 @@ private List<Node> successorsOfBlock(ImStmt par) throws Error {
147159
if (par instanceof ImLoop) {
148160
// successor is beginning of loop
149161
return Collections.singletonList(getNode(par));
162+
} else if(par instanceof ImVarargLoop){
163+
// successor is beginning of loop
164+
return Collections.singletonList(getNode(par));
150165
} else if (par instanceof ImIf) {
151166
// successor is end of if
152167
return Collections.singletonList(getEndIfNode((ImIf) par));
@@ -167,6 +182,9 @@ private Node getNode(ImStmt s) {
167182
} else if (stmt instanceof ImLoop) {
168183
result.setName("loop");
169184
result.stmt = null;
185+
} else if(stmt instanceof ImVarargLoop) {
186+
result.setName("vararg loop");
187+
result.stmt = null;
170188
}
171189
return result;
172190
}
@@ -175,6 +193,10 @@ private Node getEndloopNode(ImLoop e) {
175193
return getNode(loopEnd, e, null).setName("endloop");
176194
}
177195

196+
private Node getEndVarargLoopNode(ImVarargLoop e) {
197+
return getNode(varargLoopEnd, e, null).setName("endvarargloop");
198+
}
199+
178200
private Node getEndIfNode(ImIf e) {
179201
return getNode(ifEnd, e, null).setName("endif");
180202
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalMerger.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.peeeq.wurstscript.intermediatelang.optimizer.ControlFlowGraph.Node;
55
import de.peeeq.wurstscript.jassIm.*;
66
import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
7+
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
78
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
89
import io.vavr.collection.HashSet;
910
import io.vavr.collection.Set;
@@ -14,6 +15,8 @@
1415
import java.util.Map;
1516
import java.util.PriorityQueue;
1617

18+
import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.IS_VARARG;
19+
1720
/**
1821
* merges local variable, if they have disjoint live-spans
1922
* <p>
@@ -26,7 +29,7 @@ public class LocalMerger implements OptimizerPass {
2629
public int optimize(ImTranslator trans) {
2730
ImProg prog = trans.getImProg();
2831
totalLocalsMerged = 0;
29-
for (ImFunction func : prog.getFunctions()) {
32+
for (ImFunction func : ImHelper.calculateFunctionsOfProg(prog)) {
3033
if (!func.isNative() && !func.isBj()) {
3134
optimizeFunc(func);
3235
}
@@ -46,6 +49,10 @@ void optimizeFunc(ImFunction func) {
4649
mergeLocals(livenessInfo, func);
4750
}
4851

52+
private boolean canMerge(ImType a, ImType b) {
53+
return a.equalsType(b);
54+
}
55+
4956
private void mergeLocals(Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func) {
5057
Map<ImVar, Set<ImVar>> inferenceGraph = calculateInferenceGraph(livenessInfo);
5158

@@ -58,7 +65,9 @@ private void mergeLocals(Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func)
5865

5966
// variables which represent their own 'color', initially these are the parameters
6067
List<ImVar> assigned = new ArrayList<>(func.getParameters());
61-
68+
if(func.hasFlag(IS_VARARG)) {
69+
assigned.remove(assigned.size() - 1);
70+
}
6271
Map<ImVar, ImVar> merges = new HashMap<>();
6372

6473
nextVar:
@@ -68,7 +77,7 @@ private void mergeLocals(Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func)
6877
// check if there is some other variable which is already assigned, has the same type and does not interfere
6978
nextAssigned:
7079
for (ImVar other : assigned) {
71-
if (other.getType().equalsType(v.getType())) {
80+
if (canMerge(other.getType(), v.getType()) ) {
7281
for (ImVar inferingVar : inferenceGraph.get(v)) {
7382
if (merges.getOrDefault(inferingVar, inferingVar) == other) {
7483
// variable already used by infering var, try next color
@@ -105,6 +114,15 @@ public void visit(ImSet set) {
105114
}
106115
}
107116
}
117+
118+
@Override
119+
public void visit(ImVarargLoop varargLoop) {
120+
super.visit(varargLoop);
121+
ImVar v = varargLoop.getLoopVar();
122+
if (merges.containsKey(v)) {
123+
varargLoop.setLoopVar(merges.get(v));
124+
}
125+
}
108126
});
109127
}
110128

@@ -120,7 +138,7 @@ private Map<ImVar, Set<ImVar>> calculateInferenceGraph(Map<ImStmt, Set<ImVar>> l
120138
Set<ImVar> live = livenessInfo.get(s);
121139
for (ImVar v1 : live) {
122140
Set<ImVar> inferenceSet = inferenceGraph.getOrDefault(v1, HashSet.empty());
123-
inferenceSet = inferenceSet.addAll(live.filter(v2 -> v1.getType().equalsType(v2.getType())));
141+
inferenceSet = inferenceSet.addAll(live.filter(v2 -> canMerge(v1.getType(), v2.getType()) ));
124142
inferenceGraph.put(v1, inferenceSet);
125143
}
126144
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.common.collect.Multimap;
66
import de.peeeq.datastructures.TransitiveClosure;
77
import de.peeeq.wurstscript.jassIm.*;
8+
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
89

910
import java.util.Collection;
1011
import java.util.LinkedHashSet;
@@ -165,7 +166,7 @@ public Multimap<ImFunction, ImFunction> getCallRelation() {
165166
return callRelation;
166167
}
167168
callRelation = LinkedListMultimap.create();
168-
for (ImFunction caller : prog.getFunctions()) {
169+
for (ImFunction caller : ImHelper.calculateFunctionsOfProg(prog)) {
169170
callRelation.putAll(caller, directlyCalledFunctions(caller));
170171
}
171172
return callRelation;
@@ -190,7 +191,7 @@ public Multimap<ImFunction, ImVar> getUsedGlobals() {
190191
return usedGlobals;
191192
}
192193
usedGlobals = LinkedHashMultimap.create();
193-
for (ImFunction function : prog.getFunctions()) {
194+
for (ImFunction function : ImHelper.calculateFunctionsOfProg(prog)) {
194195
for (ImVar v : directlyUsedVariables(function)) {
195196
if (v.isGlobal()) {
196197
usedGlobals.put(function, v);
@@ -250,6 +251,12 @@ public void visit(ImFunctionCall c) {
250251
super.visit(c);
251252
calledFunctions.add(c.getFunc());
252253
}
254+
255+
@Override
256+
public void visit(ImMethodCall c) {
257+
super.visit(c);
258+
calledFunctions.add(c.getMethod().getImplementation());
259+
}
253260
});
254261
return calledFunctions;
255262

@@ -322,7 +329,7 @@ public void case_ImVarArrayAccess(ImVarArrayAccess v) {
322329

323330
@Override
324331
public void case_ImMemberAccess(ImMemberAccess v) {
325-
throw new RuntimeException("Should run after objects");
332+
imVars.add(v.getVar());
326333
}
327334

328335
@Override

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/TempMerger.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public int optimize(ImTranslator trans) {
3535
totalMerged = 0;
3636
trans.assertProperties(AssertProperty.FLAT, AssertProperty.NOTUPLES);
3737
prog.clearAttributes();
38-
for (ImFunction f : prog.getFunctions()) {
38+
for (ImFunction f : ImHelper.calculateFunctionsOfProg(prog)) {
3939
optimizeFunc(f);
4040
}
4141
// flatten the program because we introduced null-statements
@@ -90,6 +90,9 @@ private void optimizeStatements(ImStmts stmts) {
9090
} else if (s instanceof ImLoop) {
9191
ImLoop imLoop = (ImLoop) s;
9292
optimizeStatements(imLoop.getBody());
93+
} else if (s instanceof ImVarargLoop) {
94+
ImVarargLoop imVarargLoop = (ImVarargLoop) s;
95+
optimizeStatements(imVarargLoop.getBody());
9396
}
9497
}
9598
}
@@ -115,7 +118,7 @@ private void optimizeStatements(ImStmts stmts) {
115118
ImVarArrayAccess va = (ImVarArrayAccess) imSet.getLeft();
116119
kn.invalidateVar(va.getVar());
117120
}
118-
} else if (s instanceof ImExitwhen || s instanceof ImIf || s instanceof ImLoop) {
121+
} else if (s instanceof ImExitwhen || s instanceof ImIf || s instanceof ImLoop || s instanceof ImVarargLoop) {
119122
kn.clear();
120123
// TODO this could be more precise for local variables,
121124
// but for now we just forget everything if we see a loop or if statement
@@ -134,6 +137,8 @@ private void optimizeStatements(ImStmts stmts) {
134137
}
135138
} else if (elem instanceof ImLoop) {
136139
return null;
140+
} else if (elem instanceof ImVarargLoop) {
141+
return null;
137142
} else if (elem instanceof ImIf) {
138143
ImIf imIf = (ImIf) elem;
139144
return getPossibleReplacement(imIf.getCondition(), kn);
@@ -154,7 +159,10 @@ private void optimizeStatements(ImStmts stmts) {
154159
if (elem instanceof ImFunctionCall) {
155160
// function call invalidates globals
156161
kn.invalidateGlobals();
157-
} else if (elem instanceof ImVarRead) {
162+
} else if (elem instanceof ImMethodCall) {
163+
// method call invalidates globals
164+
kn.invalidateGlobals();
165+
} else if (elem instanceof ImVarRead) { // this already covers member access as well
158166
ImVarRead va = (ImVarRead) elem;
159167
if (va.getVar().isGlobal()) {
160168
// in case we read a global variable
@@ -166,7 +174,7 @@ private void optimizeStatements(ImStmts stmts) {
166174

167175

168176
private boolean containsFuncCall(Element elem) {
169-
if (elem instanceof ImFunctionCall) {
177+
if (elem instanceof ImFunctionCall || elem instanceof ImMethodCall) {
170178
return true;
171179
}
172180
// process children
@@ -188,6 +196,11 @@ private boolean readsVar(Element elem, ImVar left) {
188196
return true;
189197
}
190198
}
199+
if (elem instanceof ImMemberAccess) {
200+
if(((ImMemberAccess) elem).getVar() == left) {
201+
return true;
202+
}
203+
}
191204
// process children
192205
for (int i = 0; i < elem.size(); i++) {
193206
if (readsVar(elem.get(i), left)) {
@@ -205,6 +218,9 @@ private boolean readsGlobal(Element elem) {
205218
return true;
206219
}
207220
}
221+
if (elem instanceof ImMemberAccess) {
222+
return true;
223+
}
208224
// process children
209225
for (int i = 0; i < elem.size(); i++) {
210226
if (readsGlobal(elem.get(i))) {

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,20 @@ public int optimize(ImTranslator trans) {
6363
return isInInit(nearestFunc);
6464
}).collect(Collectors.toList());
6565
if (initWrites.size() == 1) {
66-
ImExpr write = v.attrWrites().iterator().next().getRight();
67-
try {
68-
ImExpr defaultValue = ImHelper.defaultValueForType((ImSimpleType) v.getType());
69-
boolean isDefault = defaultValue.structuralEquals(write);
70-
if (isDefault) {
71-
// Assignment is default value and can be removed
72-
v.attrWrites().iterator().next().replaceBy(ImHelper.nullExpr());
66+
if(v.getType() instanceof ImSimpleType) {
67+
ImExpr write = v.attrWrites().iterator().next().getRight();
68+
try {
69+
ImExpr defaultValue = ImHelper.defaultValueForType((ImSimpleType) v.getType());
70+
boolean isDefault = defaultValue.structuralEquals(write);
71+
if (isDefault) {
72+
// Assignment is default value and can be removed
73+
v.attrWrites().iterator().next().replaceBy(ImHelper.nullExpr());
74+
}
75+
} catch (Exception e) {
76+
throw new CompileError(write.attrTrace().attrErrorPos(),
77+
"Could not inline " + Utils.printElementWithSource(Optional.of(v.getTrace())),
78+
CompileError.ErrorType.ERROR, e);
7379
}
74-
} catch (Exception e) {
75-
throw new CompileError(write.attrTrace().attrErrorPos(),
76-
"Could not inline " + Utils.printElementWithSource(Optional.of(v.getTrace())),
77-
CompileError.ErrorType.ERROR, e);
7880
}
7981
}
8082
}

0 commit comments

Comments
 (0)