Skip to content

Commit c7bb6c0

Browse files
authored
Fix uninitialized var bug (#1163)
1 parent 3686bd3 commit c7bb6c0

2 files changed

Lines changed: 112 additions & 1 deletion

File tree

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,19 @@ public void visit(ImFunctionCall called) {
209209
if (!(called.getReturnType() instanceof ImVoid)) {
210210
retVar = JassIm.ImVar(call.attrTrace(), called.getReturnType().copy(), "inlineRet", false);
211211
f.getLocals().add(retVar);
212-
stmts.add(JassIm.ImSet(call.attrTrace(), JassIm.ImVarAccess(retVar), ImHelper.defaultValueForComplexType(called.getReturnType())));
213212
}
214213

215214
ImStmts rewritten = rewriteForEarlyReturns(JassIm.ImStmts(copiedBody), doneVar, retVar);
216215
stmts.addAll(rewritten.removeAll());
217216

218217
if (retVar != null) {
218+
// Set fallback return value only on paths where the inlined body did not execute any return.
219+
// Keeping this write close to the final read avoids dead-store removal creating uninitialized JASS locals.
220+
ImExpr notDone = JassIm.ImOperatorCall(de.peeeq.wurstscript.WurstOperator.NOT, JassIm.ImExprs(JassIm.ImVarAccess(doneVar)));
221+
stmts.add(JassIm.ImIf(call.attrTrace(), notDone,
222+
JassIm.ImStmts(JassIm.ImSet(call.attrTrace(), JassIm.ImVarAccess(retVar),
223+
ImHelper.defaultValueForComplexType(called.getReturnType()))),
224+
JassIm.ImStmts()));
219225
newExpr = ImStatementExpr(ImStmts(stmts), JassIm.ImVarAccess(retVar));
220226
}
221227
}

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

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,111 @@ public void inlinerRatesByIncomingUsesNotOutgoingCalls() throws IOException {
972972
"Expected test setup to remain non-constant and observable in _inl output.");
973973
}
974974

975+
@Test
976+
public void inlinerMultiReturnFallbackInitComesAfterReturnRewrites() throws IOException {
977+
testAssertOkLines(true,
978+
"package test",
979+
"native testSuccess()",
980+
"@inline function maybeAbs(int x) returns int",
981+
" if x > 0",
982+
" return x",
983+
" return 0 - x",
984+
"init",
985+
" let y = maybeAbs(-4)",
986+
" if y == 4",
987+
" testSuccess()",
988+
"endpackage"
989+
);
990+
991+
String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerMultiReturnFallbackInitComesAfterReturnRewrites_inl.j"), Charsets.UTF_8);
992+
int firstReturnWrite = inlined.indexOf("set inlineRet = x");
993+
int fallbackDefaultWrite = inlined.lastIndexOf("set inlineRet = 0");
994+
assertTrue(firstReturnWrite >= 0, "Expected rewritten return assignment to inlineRet in _inl output.");
995+
assertTrue(fallbackDefaultWrite > firstReturnWrite,
996+
"Expected fallback default assignment to inlineRet after rewritten returns.");
997+
}
998+
999+
@Test
1000+
public void inlinerRepeatedTransitiveInliningSingleRun() throws IOException {
1001+
testAssertOkLinesWithStdLib(false,
1002+
"package test",
1003+
"@inline function c(int x) returns int",
1004+
" return x + 1",
1005+
"@inline function b(int x) returns int",
1006+
" return c(x) + 1",
1007+
"@inline function a(int x) returns int",
1008+
" return b(x) + 1",
1009+
"init",
1010+
" let y = a(GetRandomInt(1, 10))",
1011+
" if y > 0",
1012+
" testSuccess()",
1013+
"endpackage"
1014+
);
1015+
1016+
String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerRepeatedTransitiveInliningSingleRun_inl.j"), Charsets.UTF_8);
1017+
assertFalse(inlined.contains("call a("), "Expected a() to be inlined.");
1018+
assertFalse(inlined.contains("call b("), "Expected b() to be inlined transitively.");
1019+
assertFalse(inlined.contains("call c("), "Expected c() to be inlined transitively.");
1020+
}
1021+
1022+
@Test
1023+
public void inlinerDeepNestedTransitiveInlining() throws IOException {
1024+
testAssertOkLinesWithStdLib(false,
1025+
"package test",
1026+
"@inline function e(int x) returns int",
1027+
" return x + 1",
1028+
"@inline function d(int x) returns int",
1029+
" return e(x) + 1",
1030+
"@inline function c(int x) returns int",
1031+
" return d(x) + 1",
1032+
"@inline function b(int x) returns int",
1033+
" return c(x) + 1",
1034+
"@inline function a(int x) returns int",
1035+
" return b(x) + 1",
1036+
"init",
1037+
" let y = a(GetRandomInt(1, 10))",
1038+
" if y > 0",
1039+
" testSuccess()",
1040+
"endpackage"
1041+
);
1042+
1043+
String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerDeepNestedTransitiveInlining_inl.j"), Charsets.UTF_8);
1044+
assertFalse(inlined.contains("call a("), "Expected a() to be inlined.");
1045+
assertFalse(inlined.contains("call b("), "Expected b() to be inlined.");
1046+
assertFalse(inlined.contains("call c("), "Expected c() to be inlined.");
1047+
assertFalse(inlined.contains("call d("), "Expected d() to be inlined.");
1048+
assertFalse(inlined.contains("call e("), "Expected e() to be inlined.");
1049+
}
1050+
1051+
@Test
1052+
public void inlinerLocationLocalsAreInitializedBeforeUse() throws IOException {
1053+
testAssertOkLinesWithStdLib(true,
1054+
"package test",
1055+
"@inline function chooseLoc(boolean c, location a, location b) returns location",
1056+
" if c",
1057+
" return a",
1058+
" return b",
1059+
"init",
1060+
" location la = Location(0., 0.)",
1061+
" location lb = Location(1., 1.)",
1062+
" location picked = chooseLoc(GetRandomInt(0, 1) == 0, la, lb)",
1063+
" RemoveLocation(picked)",
1064+
" RemoveLocation(la)",
1065+
" RemoveLocation(lb)",
1066+
" testSuccess()",
1067+
"endpackage"
1068+
);
1069+
1070+
String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerLocationLocalsAreInitializedBeforeUse_inl.j"), Charsets.UTF_8);
1071+
assertFalse(inlined.contains("call chooseLoc("), "Expected chooseLoc() to be inlined.");
1072+
assertTrue(inlined.contains("local location inlineRet"), "Expected inline return temp for location type.");
1073+
1074+
int initIdx = inlined.indexOf("set inlineRet = null");
1075+
int useIdx = inlined.indexOf("set picked = inlineRet");
1076+
assertTrue(initIdx >= 0, "Expected explicit initialization of location inlineRet.");
1077+
assertTrue(useIdx > initIdx, "Expected inlineRet to be initialized before use.");
1078+
}
1079+
9751080

9761081
@Test
9771082
public void moveTowardsBug() { // see #737

0 commit comments

Comments
 (0)