fix: correctly insert return statement if needed to prevent 'fall through' into following subroutine

this wasn't working correctly anymore when the last statement before the subroutine was a jump/goto
This commit is contained in:
Irmen de Jong 2021-12-07 21:07:16 +01:00
parent 547b1d3720
commit dcf487bdc1
3 changed files with 36 additions and 28 deletions

View File

@ -31,6 +31,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
varsList.add(decl.name to decl)
}
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
// move all subroutines to the bottom of the block
val subs = block.statements.filterIsInstance<Subroutine>()
block.statements.removeAll(subs)
block.statements.addAll(subs)
return noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type==VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
@ -116,12 +124,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
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)
if (subroutineStmtIdx > 0) {
val prevStmt = outerStatements[subroutineStmtIdx-1]
if(outerScope !is Block
&& (prevStmt !is Jump || prevStmt.isGosub)
&& prevStmt !is Subroutine
&& prevStmt !is Return) {
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
}
}
return mods
}

View File

@ -325,4 +325,24 @@ class TestSubroutines: FunSpec({
errors.errors[0] shouldContain "cannot use arguments"
errors.errors[1] shouldContain "invalid number of arguments"
}
test("fallthrough prevented") {
val text = """
main {
sub start() {
func(1)
sub func(ubyte a) {
a++
}
}
}
"""
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
val stmts = result.program.entrypoint.statements
stmts.last() shouldBe instanceOf<Subroutine>()
stmts.dropLast(1).last() shouldBe instanceOf<Return>() // this prevents the fallthrough
stmts.dropLast(2).last() shouldBe instanceOf<Jump>()
}
})

View File

@ -1,30 +1,8 @@
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
ubyte @shared bb
if bb + sin8u(bb) > 100-bb {
bb++
}
while bb + sin8u(bb) > 100-bb {
bb++
}
do {
bb++
} until bb + sin8u(bb) > 100-bb
const ubyte EN_TYPE=2
uword eRef = $c000
ubyte chance = rnd() % 100
if eRef[EN_TYPE] and chance < (eRef[EN_TYPE] << 1) {
bb++
}
}
}