Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,15 @@ public LuaCompilationUnit transformProgToLua() {
timeTaker.endPhase();
}

if (runArgs.isInline()) {
beginPhase(10, "inline Lua arithmetic helpers within allocated local budget");
int arithmeticHelpersInlined = optimizer.inlineLuaDivModHelpersWithinLocalBudget();
if (arithmeticHelpersInlined > 0 && runArgs.isLocalOptimizations()) {
optimizer.localOptimizations();
Comment thread
Frotty marked this conversation as resolved.
Comment thread
Frotty marked this conversation as resolved.
}
timeTaker.endPhase();
}

printDebugImProg("./test-output/lua/im " + stage++ + "_afterlocalopts.im");

boolean garbageChanged = optimizer.removeGarbage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ private Node getNode(ImStmt s) {
result.stmt = null;
} else if (s instanceof ImVarargLoop) {
result.setName("vararg loop");
result.stmt = null;
}
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
import de.peeeq.wurstscript.types.TypesHelper;
import io.vavr.collection.HashSet;
import io.vavr.collection.Set;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
Expand Down Expand Up @@ -56,10 +55,20 @@ void optimizeFunc(ImFunction func, LocalPlayerContextAnalyzer analyzer) {
private boolean canMerge(ImType a, ImType b) { return a.equalsType(b); }

private void mergeLocals(Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func) {
Map<ImVar, Set<ImVar>> interference = calculateInferenceGraph(livenessInfo);
Map<ImVar, java.util.Set<ImVar>> interference = calculateInterferenceGraph(livenessInfo, func);

Map<ImVar, Integer> declarationOrder = new IdentityHashMap<>();
int nextOrder = 0;
for (ImVar parameter : func.getParameters()) {
declarationOrder.put(parameter, nextOrder++);
}
for (ImVar local : func.getLocals()) {
declarationOrder.put(local, nextOrder++);
}

PriorityQueue<ImVar> queue = new PriorityQueue<>(
(x, y) -> interference.get(y).size() - interference.get(x).size()
Comparator.<ImVar>comparingInt(v -> interference.get(v).size()).reversed()
.thenComparingInt(declarationOrder::get)
);
queue.addAll(interference.keySet());

Expand All @@ -81,8 +90,8 @@ private void mergeLocals(Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func)
continue;
}
if (localPlayerContextAnalyzer != null
&& (localPlayerContextAnalyzer.isLocalPlayerDependent(v)
|| localPlayerContextAnalyzer.isLocalPlayerDependent(color))) {
&& localPlayerContextAnalyzer.isLocalPlayerDependent(v)
!= localPlayerContextAnalyzer.isLocalPlayerDependent(color)) {
continue;
}

Expand Down Expand Up @@ -158,17 +167,94 @@ private static int removeUnusedLocals(ImFunction f) {
return before - kept.size();
}

private Map<ImVar, Set<ImVar>> calculateInferenceGraph(Map<ImStmt, Set<ImVar>> livenessInfo) {
Map<ImVar, Set<ImVar>> g = new LinkedHashMap<>();
for (Map.Entry<ImStmt, Set<ImVar>> e : livenessInfo.entrySet()) {
Set<ImVar> live = e.getValue();
for (ImVar v1 : live) {
Set<ImVar> set = g.getOrDefault(v1, HashSet.empty());
set = set.addAll(live.filter(v2 -> canMerge(v1.getType(), v2.getType())));
g.put(v1, set);
private Map<ImVar, java.util.Set<ImVar>> calculateInterferenceGraph(
Map<ImStmt, Set<ImVar>> livenessInfo, ImFunction func) {
Map<ImVar, java.util.Set<ImVar>> graph = new LinkedHashMap<>();
for (ImVar parameter : func.getParameters()) {
graph.put(parameter, new ObjectOpenHashSet<>());
}
for (ImVar local : func.getLocals()) {
graph.put(local, new ObjectOpenHashSet<>());
}

java.util.Set<ImVar> explicitlyDefined = Collections.newSetFromMap(new IdentityHashMap<>());

// A definition interferes with every compatible value that remains live after it.
// Building only those edges is equivalent to cliquing every live set, while avoiding
// the old O(statements * liveValues^2) behavior on large inlined functions.
for (Map.Entry<ImStmt, Set<ImVar>> entry : livenessInfo.entrySet()) {
List<ImVar> defined = definedLocals(entry.getKey());
if (defined.isEmpty()) {
continue;
}
Comment thread
Frotty marked this conversation as resolved.
explicitlyDefined.addAll(defined);
for (int i = 0; i < defined.size(); i++) {
ImVar definition = defined.get(i);
java.util.Set<ImVar> neighbors = graph.computeIfAbsent(definition, ignored -> new ObjectOpenHashSet<>());
for (ImVar live : entry.getValue()) {
if (live == definition || !canMerge(definition.getType(), live.getType())) {
continue;
}
neighbors.add(live);
graph.computeIfAbsent(live, ignored -> new ObjectOpenHashSet<>()).add(definition);
}
// Vararg tuple components are assigned at the same loop boundary. They must
// occupy distinct slots even when neither component is live before the loop.
for (int j = i + 1; j < defined.size(); j++) {
ImVar other = defined.get(j);
if (canMerge(definition.getType(), other.getType())) {
neighbors.add(other);
graph.computeIfAbsent(other, ignored -> new ObjectOpenHashSet<>()).add(definition);
}
}
}
}

// Parameters and locals with no explicit assignment receive their values at function
// entry. Model that simultaneous definition so a warning-only read of an uninitialized
// local cannot be colored onto a parameter (or another implicit entry value).
List<ImVar> entryDefinitions = new ArrayList<>(func.getParameters());
for (ImVar local : func.getLocals()) {
if (!explicitlyDefined.contains(local)) {
entryDefinitions.add(local);
Comment thread
Frotty marked this conversation as resolved.
}
}
for (int i = 0; i < entryDefinitions.size(); i++) {
ImVar definition = entryDefinitions.get(i);
java.util.Set<ImVar> neighbors = graph.get(definition);
for (int j = i + 1; j < entryDefinitions.size(); j++) {
ImVar other = entryDefinitions.get(j);
if (canMerge(definition.getType(), other.getType())) {
neighbors.add(other);
graph.get(other).add(definition);
}
}
}
return graph;
}

private static List<ImVar> definedLocals(ImStmt stmt) {
if (stmt instanceof ImVarargLoop loop) {
List<ImVar> result = new ArrayList<>(loop.getLoopVars().size());
for (ImVarargLoopVar loopVar : loop.getLoopVars()) {
result.add(loopVar.getVar());
}
return result;
}
if (!(stmt instanceof ImSet set)) {
return Collections.emptyList();
}
return g;
ImLExpr left = set.getLeft();
if (left instanceof ImVarAccess access && !access.getVar().isGlobal()) {
return Collections.singletonList(access.getVar());
}
if (left instanceof ImTupleSelection selection) {
ImVar var = TypesHelper.getSimpleAndPureTupleVar(selection);
if (var != null && !var.isGlobal()) {
return Collections.singletonList(var);
}
}
return Collections.emptyList();
}

private void eliminateDeadCode(Map<ImStmt, Set<ImVar>> livenessInfo) {
Expand Down Expand Up @@ -272,6 +358,17 @@ public Map<ImStmt, Set<ImVar>> calculateLiveness(ImFunction func) {
ImStmt stmt = node.getStmt();
if (stmt == null) continue;

if (stmt instanceof ImVarargLoop loop) {
for (ImVarargLoopVar loopVar : loop.getLoopVars()) {
if (!loopVar.getVar().isGlobal()) {
def[i].add(loopVar.getVar());
}
}
// The loop body has its own CFG nodes. Visiting it here would incorrectly
// classify all body reads as uses at the loop header.
continue;
}

final int ii = i;
stmt.accept(new ImStmt.DefaultVisitor() {
@Override public void visit(ImVarAccess va) {
Expand Down
Loading
Loading