Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -209,13 +209,19 @@ public void visit(ImFunctionCall called) {
if (!(called.getReturnType() instanceof ImVoid)) {
retVar = JassIm.ImVar(call.attrTrace(), called.getReturnType().copy(), "inlineRet", false);
f.getLocals().add(retVar);
stmts.add(JassIm.ImSet(call.attrTrace(), JassIm.ImVarAccess(retVar), ImHelper.defaultValueForComplexType(called.getReturnType())));
}

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

if (retVar != null) {
// Set fallback return value only on paths where the inlined body did not execute any return.
// Keeping this write close to the final read avoids dead-store removal creating uninitialized JASS locals.
ImExpr notDone = JassIm.ImOperatorCall(de.peeeq.wurstscript.WurstOperator.NOT, JassIm.ImExprs(JassIm.ImVarAccess(doneVar)));
stmts.add(JassIm.ImIf(call.attrTrace(), notDone,
JassIm.ImStmts(JassIm.ImSet(call.attrTrace(), JassIm.ImVarAccess(retVar),
ImHelper.defaultValueForComplexType(called.getReturnType()))),
JassIm.ImStmts()));
newExpr = ImStatementExpr(ImStmts(stmts), JassIm.ImVarAccess(retVar));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,111 @@ public void inlinerRatesByIncomingUsesNotOutgoingCalls() throws IOException {
"Expected test setup to remain non-constant and observable in _inl output.");
}

@Test
public void inlinerMultiReturnFallbackInitComesAfterReturnRewrites() throws IOException {
testAssertOkLines(true,
"package test",
"native testSuccess()",
"@inline function maybeAbs(int x) returns int",
" if x > 0",
" return x",
" return 0 - x",
"init",
" let y = maybeAbs(-4)",
" if y == 4",
" testSuccess()",
"endpackage"
);

String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerMultiReturnFallbackInitComesAfterReturnRewrites_inl.j"), Charsets.UTF_8);
int firstReturnWrite = inlined.indexOf("set inlineRet = x");
int fallbackDefaultWrite = inlined.lastIndexOf("set inlineRet = 0");
assertTrue(firstReturnWrite >= 0, "Expected rewritten return assignment to inlineRet in _inl output.");
assertTrue(fallbackDefaultWrite > firstReturnWrite,
"Expected fallback default assignment to inlineRet after rewritten returns.");
}

@Test
public void inlinerRepeatedTransitiveInliningSingleRun() throws IOException {
testAssertOkLinesWithStdLib(false,
"package test",
"@inline function c(int x) returns int",
" return x + 1",
"@inline function b(int x) returns int",
" return c(x) + 1",
"@inline function a(int x) returns int",
" return b(x) + 1",
"init",
" let y = a(GetRandomInt(1, 10))",
" if y > 0",
" testSuccess()",
"endpackage"
);

String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerRepeatedTransitiveInliningSingleRun_inl.j"), Charsets.UTF_8);
assertFalse(inlined.contains("call a("), "Expected a() to be inlined.");
assertFalse(inlined.contains("call b("), "Expected b() to be inlined transitively.");
assertFalse(inlined.contains("call c("), "Expected c() to be inlined transitively.");
}

@Test
public void inlinerDeepNestedTransitiveInlining() throws IOException {
testAssertOkLinesWithStdLib(false,
"package test",
"@inline function e(int x) returns int",
" return x + 1",
"@inline function d(int x) returns int",
" return e(x) + 1",
"@inline function c(int x) returns int",
" return d(x) + 1",
"@inline function b(int x) returns int",
" return c(x) + 1",
"@inline function a(int x) returns int",
" return b(x) + 1",
"init",
" let y = a(GetRandomInt(1, 10))",
" if y > 0",
" testSuccess()",
"endpackage"
);

String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerDeepNestedTransitiveInlining_inl.j"), Charsets.UTF_8);
assertFalse(inlined.contains("call a("), "Expected a() to be inlined.");
assertFalse(inlined.contains("call b("), "Expected b() to be inlined.");
assertFalse(inlined.contains("call c("), "Expected c() to be inlined.");
assertFalse(inlined.contains("call d("), "Expected d() to be inlined.");
assertFalse(inlined.contains("call e("), "Expected e() to be inlined.");
}

@Test
public void inlinerLocationLocalsAreInitializedBeforeUse() throws IOException {
testAssertOkLinesWithStdLib(true,
"package test",
"@inline function chooseLoc(boolean c, location a, location b) returns location",
" if c",
" return a",
" return b",
"init",
" location la = Location(0., 0.)",
" location lb = Location(1., 1.)",
" location picked = chooseLoc(GetRandomInt(0, 1) == 0, la, lb)",
" RemoveLocation(picked)",
" RemoveLocation(la)",
" RemoveLocation(lb)",
" testSuccess()",
"endpackage"
);

String inlined = Files.toString(new File("test-output/OptimizerTests_inlinerLocationLocalsAreInitializedBeforeUse_inl.j"), Charsets.UTF_8);
assertFalse(inlined.contains("call chooseLoc("), "Expected chooseLoc() to be inlined.");
assertTrue(inlined.contains("local location inlineRet"), "Expected inline return temp for location type.");

int initIdx = inlined.indexOf("set inlineRet = null");
int useIdx = inlined.indexOf("set picked = inlineRet");
assertTrue(initIdx >= 0, "Expected explicit initialization of location inlineRet.");
assertTrue(useIdx > initIdx, "Expected inlineRet to be initialized before use.");
}


@Test
public void moveTowardsBug() { // see #737
Expand Down
Loading