cleaned up the way return statements are added to avoid code falling through in/out of subroutines

This commit is contained in:
Irmen de Jong 2020-03-24 22:02:50 +01:00
parent f2bb238e9b
commit 8805693ed2
4 changed files with 62 additions and 10 deletions

View File

@ -1058,12 +1058,12 @@ internal class AstChecker(private val program: Program,
&& stmt.target.nameInSource.last()=="exit"
&& index < statements.lastIndex) {
println("STMT AFTER EXIT ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
errors.warn("unreachable code, exit call above never returns", statements[index + 1].position)
errors.warn("unreachable code, preceding exit call will never return", statements[index + 1].position)
}
if(stmt is Return && index < statements.lastIndex) {
println("STMT AFTER RETURN ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
errors.warn("unreachable code, return statement above", statements[index + 1].position)
errors.warn("unreachable code, preceding return statement", statements[index + 1].position)
}
stmt.accept(this)

View File

@ -30,10 +30,21 @@ interface IAstModification {
}
}
class Insert(val after: Statement?, val stmt: Statement, val parent: Node) : IAstModification {
class InsertFirst(val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() {
if(parent is INameScope) {
val idx = if(after==null) 0 else parent.statements.indexOf(after)
parent.statements.add(0, stmt)
stmt.linkParents(parent)
} else {
throw FatalAstException("parent of an insert modification is not an INameScope")
}
}
}
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() {
if(parent is INameScope) {
val idx = parent.statements.indexOf(after)+1
parent.statements.add(idx, stmt)
stmt.linkParents(parent)
} else {

View File

@ -41,17 +41,40 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position)
val assign = Assignment(target, null, initValue, it.position)
IAstModification.Insert(null, assign, scope)
IAstModification.InsertFirst(assign, scope)
} +
decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +
decls.map { IAstModification.Insert(null, it, sub) } // move it up to the subroutine
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
}
}
return emptyList()
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
TODO("insert Return statements at the required places such as at the end of a subroutine if they're missing")
return emptyList()
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations.
val mods = mutableListOf<IAstModification>()
val returnStmt = Return(null, subroutine.position)
if(subroutine.asmAddress==null
&& subroutine.statements.isNotEmpty()
&& subroutine.amountOfRtsInAsm()==0
&& subroutine.statements.lastOrNull {it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine) {
mods += IAstModification.InsertAfter(subroutine.statements.last(), returnStmt, subroutine)
}
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
val outerScope = subroutine.definingScope()
val outerStatements = outerScope.statements
val subroutineStmtIdx = outerStatements.indexOf(subroutine)
if(subroutineStmtIdx>0
&& outerStatements[subroutineStmtIdx-1] !is Jump
&& outerStatements[subroutineStmtIdx-1] !is Subroutine
&& outerStatements[subroutineStmtIdx-1] !is Return
&& outerScope !is Block) {
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx-1], returnStmt, outerScope as Node)
}
return mods
}
}

View File

@ -14,12 +14,30 @@ main {
v1 = 100
v2 = 127
A=5
return
sub bla () {
A=99
bla2()
sub bla2 () {
A=100
foo.ding()
foo.ding2()
}
}
}
}
foo {
ubyte derp=99
sub ding() {
A=derp
}
sub ding2() {
A=derp
}
}