Skip to content

Commit 0da3c2f

Browse files
authored
Elide total unused Lua tuple components (#1283)
1 parent da13f01 commit 0da3c2f

2 files changed

Lines changed: 65 additions & 5 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private static void retainDiscardedValue(ImExpr value, ImStmts stmts, ImTranslat
272272
}
273273
return;
274274
}
275-
if (isTriviallyDiscardable(value)) {
275+
if (isSafelyDiscardable(value)) {
276276
return;
277277
}
278278
if (SideEffectAnalyzer.quickcheckHasSideeffects(value)) {
@@ -284,14 +284,34 @@ private static void retainDiscardedValue(ImExpr value, ImStmts stmts, ImTranslat
284284
}
285285
}
286286

287-
private static boolean isTriviallyDiscardable(ImExpr value) {
288-
return value instanceof ImBoolVal
287+
private static boolean isSafelyDiscardable(ImExpr value) {
288+
if (value instanceof ImBoolVal
289289
|| value instanceof ImIntVal
290290
|| value instanceof ImRealVal
291291
|| value instanceof ImStringVal
292292
|| value instanceof ImNull
293293
|| value instanceof ImVarAccess
294-
|| value instanceof ImFuncRef;
294+
|| value instanceof ImFuncRef) {
295+
return true;
296+
}
297+
if (value instanceof ImOperatorCall operatorCall
298+
&& isTotalOperator(operatorCall.getOp())) {
299+
return operatorCall.getArguments().stream()
300+
.allMatch(EliminateTuples::isSafelyDiscardable);
301+
}
302+
return false;
303+
}
304+
305+
/**
306+
* Operators other than integer division and modulo are total for well-typed scalar IM values.
307+
* Keeping their unused results in a Lua discard sink only repeats pure scalar work and prevents
308+
* tuple-component DCE. Division and modulo stay conservative because a zero divisor can fail.
309+
*/
310+
private static boolean isTotalOperator(WurstOperator operator) {
311+
return switch (operator) {
312+
case DIV_INT, MOD_INT, MOD_REAL, JASS_MOD_INT -> false;
313+
default -> true;
314+
};
295315
}
296316

297317
/**
@@ -1006,7 +1026,7 @@ private static void lowerBundleInto(ImExpr expression, ImFunction f, boolean cap
10061026

10071027
value.setParent(null);
10081028
boolean requiresEagerEvaluation = capturePotentiallyFailingValues
1009-
&& !isTriviallyDiscardable(value)
1029+
&& !isSafelyDiscardable(value)
10101030
&& !SideEffectAnalyzer.quickcheckHasSideeffects(value);
10111031
if ((capture || requiresEagerEvaluation) && !isImmutableValue(value)) {
10121032
result.values.add(captureValue(value, temporaryName, result.prelude, f));

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,46 @@ public void tupleSelectionPreservesLeftToRightEvaluation() throws IOException {
731731
assertFalse(compiled.contains("tupleCopy"));
732732
}
733733

734+
@Test
735+
public void unusedTotalTupleComponentDoesNotSurviveLuaLowering() throws IOException {
736+
test().testLua(true).executeProg().lines(
737+
"package Test",
738+
"native testSuccess()",
739+
"tuple pair(int x, int y)",
740+
"function selectFirst(int left, int right) returns int",
741+
" return pair(left + 1, right + 2).x",
742+
"init",
743+
" if selectFirst(4, 10) == 5",
744+
" testSuccess()"
745+
);
746+
747+
String compiled = compiledLua("unusedTotalTupleComponentDoesNotSurviveLuaLowering");
748+
assertFalse("unused total tuple components should not require a Lua discard call",
749+
compiled.contains("__wurst_tuple_discard_"));
750+
assertFalse("unused total tuple components should not be evaluated",
751+
compiled.contains("right + 2"));
752+
}
753+
754+
@Test
755+
public void unreadTupleReturnSlotsAreRemoved() throws IOException {
756+
test().testLua(true).executeProg().lines(
757+
"package Test",
758+
"native testSuccess()",
759+
"tuple pair(int x, int y)",
760+
"@noinline function makePair(int left, int right) returns pair",
761+
" return pair(left + 1, right + 2)",
762+
"init",
763+
" if makePair(4, 10).x == 5",
764+
" testSuccess()"
765+
);
766+
767+
String compiled = compiledLua("unreadTupleReturnSlotsAreRemoved");
768+
assertFalse("unread scalar return slots should be removed after tuple lowering",
769+
compiled.contains("makePair_return_y"));
770+
assertFalse("the producer for an unread scalar return slot should be removed",
771+
compiled.contains("right + 2"));
772+
}
773+
734774
@Test
735775
public void discardedTupleComponentsThatCanFailAreStillEvaluated() {
736776
String compiled = compileLuaWithRunArgs(

0 commit comments

Comments
 (0)