rts-check for non-inlined subroutines + var init adjustment when noreinit, moved out of codegen

This commit is contained in:
Irmen de Jong 2022-02-06 02:40:04 +01:00
parent 312949f336
commit 77de99b383
4 changed files with 55 additions and 35 deletions

View File

@ -255,6 +255,11 @@ class AsmGen6502(internal val program: Program,
} }
private fun block2asm(block: Block) { private fun block2asm(block: Block) {
// no longer output the initialization assignments as regular statements in the block,
// they will be part of the prog8_init_vars init routine generated below.
val initializers = blockVariableInitializers.getValue(block)
val statements = block.statements.filterNot { it in initializers }
out("\n\n; ---- block: '${block.name}' ----") out("\n\n; ---- block: '${block.name}' ----")
if(block.address!=null) if(block.address!=null)
out("* = ${block.address!!.toHex()}") out("* = ${block.address!!.toHex()}")
@ -267,36 +272,25 @@ class AsmGen6502(internal val program: Program,
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n")) out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
if(options.dontReinitGlobals) {
block.statements.asSequence().filterIsInstance<VarDecl>().forEach {
it.zeropage = ZeropageWish.NOT_IN_ZEROPAGE
it.findInitializer(program)?.let { initializer -> it.value = initializer.value } // put the init value back into the vardecl
}
}
// no longer output the initialization assignments as regular statements in the block!
block.statements.removeAll(blockVariableInitializers.getValue(block))
outputSourceLine(block) outputSourceLine(block)
zeropagevars2asm(block.statements, block) zeropagevars2asm(statements, block)
memdefs2asm(block.statements, block) memdefs2asm(statements, block)
vardecls2asm(block.statements, block) vardecls2asm(statements, block)
block.statements.asSequence().filterIsInstance<VarDecl>().forEach { statements.asSequence().filterIsInstance<VarDecl>().forEach {
if(it.type==VarDeclType.VAR && it.datatype in NumericDatatypes) if(it.type==VarDeclType.VAR && it.datatype in NumericDatatypes)
it.value=null it.value=null // TODO why is this done?
} }
out("\n; subroutines in this block") out("\n; subroutines in this block")
// first translate regular statements, and then put the subroutines at the end. // first translate regular statements, and then put the subroutines at the end.
val (subroutine, stmts) = block.statements.partition { it is Subroutine } val (subroutine, stmts) = statements.partition { it is Subroutine }
stmts.forEach { translate(it) } stmts.forEach { translate(it) }
subroutine.forEach { translateSubroutine(it as Subroutine) } subroutine.forEach { translateSubroutine(it as Subroutine) }
if(!options.dontReinitGlobals) { if(!options.dontReinitGlobals) {
// generate subroutine to initialize block-level (global) variables // generate subroutine to initialize block-level (global) variables
val initializers = blockVariableInitializers.getValue(block)
if (initializers.isNotEmpty()) { if (initializers.isNotEmpty()) {
out("prog8_init_vars\t.proc\n") out("prog8_init_vars\t.proc\n")
initializers.forEach { assign -> translate(assign) } initializers.forEach { assign -> translate(assign) }
@ -1059,10 +1053,6 @@ class AsmGen6502(internal val program: Program,
// (they've been inlined at the call site, remember?) // (they've been inlined at the call site, remember?)
onlyVariables = true onlyVariables = true
} }
else if(sub.amountOfRtsInAsm()==0) {
// make sure the NOT INLINED subroutine actually does a rts at the end
sub.statements.add(Return(null, Position.DUMMY))
}
} }
out("") out("")

View File

@ -49,12 +49,27 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val subs = block.statements.filterIsInstance<Subroutine>() val subs = block.statements.filterIsInstance<Subroutine>()
block.statements.removeAll(subs) block.statements.removeAll(subs)
block.statements.addAll(subs) block.statements.addAll(subs)
// adjust global variables initialization
if(options.dontReinitGlobals) {
block.statements.asSequence().filterIsInstance<VarDecl>().forEach {
if(it.type==VarDeclType.VAR) {
it.zeropage = ZeropageWish.NOT_IN_ZEROPAGE
it.findInitializer(program)?.let { initializer ->
it.value = initializer.value // put the init value back into the vardecl
}
}
}
}
return noModifications return noModifications
} }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type== VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes) if(!options.dontReinitGlobals) {
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") throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
}
rememberSubroutineVar(decl) rememberSubroutineVar(decl)
return noModifications return noModifications
} }
@ -116,6 +131,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
} }
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> { override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val mods = mutableListOf<IAstModification>()
val firstDeclarations = mutableMapOf<String, VarDecl>() val firstDeclarations = mutableMapOf<String, VarDecl>()
val rememberedSubroutineVars = subroutineVariables.getOrDefault(subroutine, mutableListOf()) val rememberedSubroutineVars = subroutineVariables.getOrDefault(subroutine, mutableListOf())
for(decl in rememberedSubroutineVars) { for(decl in rememberedSubroutineVars) {
@ -130,9 +146,8 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine. // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations. // 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) val returnStmt = Return(null, subroutine.position)
if (subroutine.asmAddress == null && !subroutine.inline) { if (!subroutine.isAsmSubroutine && !subroutine.inline) {
if(subroutine.statements.isEmpty() || if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0 (subroutine.amountOfRtsInAsm() == 0
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return && subroutine.statements.lastOrNull { it !is VarDecl } !is Return
@ -155,6 +170,13 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope) mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
} }
} }
if (subroutine.inline && subroutine.isAsmSubroutine && subroutine.amountOfRtsInAsm() == 0) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above)
mods += IAstModification.InsertLast(InlineAssembly(" rts\n", Position.DUMMY), subroutine)
}
return mods return mods
} }

View File

@ -23,14 +23,8 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Ast modifications done in AsmGen, that should be done BEFORE calling asmgen (so that it doesn't have to modify the Ast any longer): Ast modifications done in AsmGen, that should be done BEFORE calling asmgen (so that it doesn't have to modify the Ast any longer):
- translateSubroutine: - block2asm: after vardecls2asm it clears the vardecl.value of all variables (why?)
if subroutine marked as inline but optimizations are disabled, make sure the NOT INLINED subroutine actually has a Return statement at the end - block2asm: removes init-assignments to no longer output the initialization assignments as regular statements (is done separately in block initialization routine)
- block2asm:
if(options.dontReinitGlobals) -> currently modifies the zeropage and init value of vardecl's.
it removes init-assignments to no longer output the initialization assignments as regular statements (is done separately in block initialization routine)
after vardecls2asm it clears the vardecl.value of all variables (why?)
- Maybe don't rely on vardecls at all any longer but figure out the variable allocations (including ZP allocations) beforehand - Maybe don't rely on vardecls at all any longer but figure out the variable allocations (including ZP allocations) beforehand
and pass that via a new datastructure to asmgen? So that asmgen is no longer tasked with doing the allocations. and pass that via a new datastructure to asmgen? So that asmgen is no longer tasked with doing the allocations.
This could perhaps make it easer for the codegen as well to deal with sections, if any, in the future. This could perhaps make it easer for the codegen as well to deal with sections, if any, in the future.

View File

@ -1,9 +1,23 @@
%import textio %import textio
%zeropage basicsafe
main { main {
ubyte mainglobal1 = 10
ubyte mainglobal2 = 20
ubyte mainglobal3 = 30
ubyte mainglobal4 = 40
sub start() { sub start() {
txt.print("ok") ubyte startval1 = 100
ubyte startval2 = 110
ubyte startval3 = 120
ubyte startval4 = 130
txt.print_ub(mainglobal1)
txt.nl() txt.nl()
sys.wait(999) txt.print_ub(startval1)
txt.nl()
startval1++
mainglobal1++
} }
} }