GenASM+Void: Generate an implicit return

Void functions are allowed to be completely free of return statements. The problem is that this does not work on its own in assembler. We have to generate a return statement at the end of the function body if there is no return statement already.
This commit is contained in:
2023-03-23 22:49:36 +01:00
parent aef2c84fdc
commit 5f0e84198a

View File

@@ -71,7 +71,7 @@ public class GenASM implements Visitor<Void> {
private int lCount = 0; private int lCount = 0;
private int currentFunctionStartLabel = 0; private int currentFunctionStartLabel = 0;
private long bytesToClearFromTheStack = 0; private long bytesToClearFromTheStack = 0;
private Parameter[] currentFunctionParams; private FunctionDefinition currentFunctionDef;
public GenASM(String mainName, Map<String, StructDefinition> structs) { public GenASM(String mainName, Map<String, StructDefinition> structs) {
this.mainName = mainName; this.mainName = mainName;
@@ -543,9 +543,23 @@ public class GenASM implements Visitor<Void> {
@Override @Override
public Void visit(Block e) { public Void visit(Block e) {
for (var statement : e.statementsOrFunctionCalls) { for (var statementOrFunctionCall : e.statementsOrFunctionCalls) {
statement.welcome(this); statementOrFunctionCall.welcome(this);
} }
// It's possible (and allowed -> void functions) that there is no return statement
// in the outermost block of a function body. In this case, no
// direct descendant of Block will be a return statement.
// This means we have to generate an implicit return, otherwise
// the code would fall through to the next function below :D
// We also have to clean up the stack which is the ReturnStatement's job.
// Check if we have to generate an implicit return
var lastStatementOrFunctionCall = e.statementsOrFunctionCalls[e.statementsOrFunctionCalls.length - 1];
if (currentFunctionDef.block == e && !(lastStatementOrFunctionCall instanceof ReturnStatement)) {
visit(new ReturnStatement());
}
return null; return null;
} }
@@ -558,9 +572,11 @@ public class GenASM implements Visitor<Void> {
e.name = "main_by_user"; e.name = "main_by_user";
} }
// Remember the current function definition so everyone below us knows where they belong to.
currentFunctionDef = e;
var lblStart = ++lCount; var lblStart = ++lCount;
currentFunctionStartLabel = lblStart; currentFunctionStartLabel = lblStart;
currentFunctionParams = e.parameters;
asm.functionHead(e.name); asm.functionHead(e.name);
asm.push("q", "%rbp"); asm.push("q", "%rbp");
asm.mov("q", "%rsp", "%rbp"); asm.mov("q", "%rsp", "%rbp");
@@ -675,10 +691,10 @@ public class GenASM implements Visitor<Void> {
// push args into local var locations, last arg is on top of the stack // push args into local var locations, last arg is on top of the stack
for (int i = e.arguments.length - 1; i >= 0; i--) { for (int i = e.arguments.length - 1; i >= 0; i--) {
asm.pop("q", this.env.get(this.currentFunctionParams[i].name) + "(%rbp)"); asm.pop("q", env.get(currentFunctionDef.parameters[i].name) + "(%rbp)");
} }
asm.jmp(this.currentFunctionStartLabel); asm.jmp(currentFunctionStartLabel);
return null; return null;
} }