From 491e5dbcfbc6bb9c0a13aa2c4ae3460f62bbcf1c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 5 Nov 2024 22:12:25 +0100 Subject: [PATCH] move the program startup and cleanup machinery to the front of the program to keep it in system ram --- .../codegen/cpu6502/ProgramAndVarsGen.kt | 6 +---- compiler/src/prog8/compiler/Compiler.kt | 2 +- .../compiler/astprocessing/AstExtensions.kt | 23 +++++++++++++++++-- compiler/test/TestCallgraph.kt | 4 ++-- compiler/test/TestOptimization.kt | 6 ++--- compiler/test/TestScoping.kt | 6 ++--- compiler/test/TestSubroutines.kt | 9 +++----- docs/source/todo.rst | 3 --- examples/test.p8 | 12 +++++----- 9 files changed, 38 insertions(+), 33 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 62def99ed..53ed04948 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -32,10 +32,6 @@ internal class ProgramAndVarsGen( internal fun generate() { header() - val allBlocks = program.allBlocks() - - if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main") - throw AssemblyError("first block should be 'main' or 'p8b_main'") if(errors.noErrors()) { program.allBlocks().forEach { block2asm(it) } @@ -114,7 +110,7 @@ internal class ProgramAndVarsGen( asmgen.out(" .word (+), $year") asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'") asmgen.out("+\t.word 0") - asmgen.out("prog8_entrypoint\t; assembly code starts here") + asmgen.out("prog8_entrypoint") asmgen.out(" cld") asmgen.out(" tsx ; save stackpointer for sys.exit()") asmgen.out(" stx prog8_lib.orig_stackpointer") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index a11e17cb5..77ac98b24 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -481,7 +481,7 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt callGraph.checkRecursiveCalls(errors) program.verifyFunctionArgTypes(errors, compilerOptions) errors.report() - program.moveMainBlockAsFirst() + program.moveMainBlockAsFirst(compilerOptions.compTarget) program.checkValid(errors, compilerOptions) // check if final tree is still valid errors.report() } diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 61b5efcd9..9de3110f4 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -1,5 +1,6 @@ package prog8.compiler.astprocessing +import prog8.ast.IStatementContainer import prog8.ast.Node import prog8.ast.Program import prog8.ast.expressions.CharLiteral @@ -10,6 +11,7 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* +import prog8.code.target.VMTarget import java.io.CharConversionException @@ -136,9 +138,10 @@ internal fun Program.variousCleanups(errors: IErrorReporter, options: Compilatio } } -internal fun Program.moveMainBlockAsFirst() { +internal fun Program.moveMainBlockAsFirst(target: ICompilationTarget) { // The module containing the program entrypoint is moved to the first in the sequence. - // the "main" block containing the entrypoint is moved to the top in there. + // The "main" block containing the entrypoint is moved to the top in there. + // The startup and cleanup machinery is moved to the front as well. val module = this.entrypoint.definingModule val block = this.entrypoint.definingBlock @@ -149,6 +152,22 @@ internal fun Program.moveMainBlockAsFirst() { module.statements.add(block) else module.statements.add(afterDirective, block) + + + if(target.name != VMTarget.NAME) { + // the program startup and cleanup machinery needs to be located in system ram + // so in an attempt to not be pushed into ROM space at the end of the program, + // this moves that block to the beginning of the program as much as possible. + val startupBlock = this.allBlocks.single { it.name == "p8_sys_startup" } + val mainBlockIdx = module.statements.indexOf(block) + (startupBlock.parent as IStatementContainer).remove(startupBlock) + if (block.address == null) { + module.statements.add(mainBlockIdx, startupBlock) + } else { + module.statements.add(mainBlockIdx + 1, startupBlock) + } + startupBlock.parent = module + } } internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean { diff --git a/compiler/test/TestCallgraph.kt b/compiler/test/TestCallgraph.kt index 3443ab8c6..88d1ec03a 100644 --- a/compiler/test/TestCallgraph.kt +++ b/compiler/test/TestCallgraph.kt @@ -44,7 +44,7 @@ class TestCallgraph: FunSpec({ graph.unused(toplevelModule) shouldBe false graph.unused(importedModule) shouldBe false - val mainBlock = toplevelModule.statements.filterIsInstance().single() + val mainBlock = toplevelModule.statements.filterIsInstance().single{it.name=="main"} for(stmt in mainBlock.statements) { val sub = stmt as Subroutine graph.calls shouldNotContainKey sub @@ -85,7 +85,7 @@ class TestCallgraph: FunSpec({ graph.unused(toplevelModule) shouldBe false graph.unused(importedModule) shouldBe false - val mainBlock = toplevelModule.statements.filterIsInstance().single() + val mainBlock = toplevelModule.statements.filterIsInstance().single{it.name=="main"} val startSub = mainBlock.statements.filterIsInstance().single{it.name=="start"} val emptySub = mainBlock.statements.filterIsInstance().single{it.name=="empty"} diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 4a80cb5ed..404663052 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -35,8 +35,7 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), true, sourcecode)!! - val toplevelModule = result.compilerAst.toplevelModule - val mainBlock = toplevelModule.statements.single() as Block + val mainBlock = result.compilerAst.entrypoint.definingBlock val startSub = mainBlock.statements.single() as Subroutine result.compilerAst.entrypoint shouldBeSameInstanceAs startSub withClue("only start sub should remain") { @@ -85,8 +84,7 @@ main { } """ val result = compileText(C64Target(), true, sourcecode)!! - val toplevelModule = result.compilerAst.toplevelModule - val mainBlock = toplevelModule.statements.single() as Block + val mainBlock = result.compilerAst.entrypoint.definingBlock val startSub = mainBlock.statements[0] as Subroutine val emptySub = mainBlock.statements[1] as Subroutine result.compilerAst.entrypoint shouldBeSameInstanceAs startSub diff --git a/compiler/test/TestScoping.kt b/compiler/test/TestScoping.kt index d83b36529..5ac75fa22 100644 --- a/compiler/test/TestScoping.kt +++ b/compiler/test/TestScoping.kt @@ -46,8 +46,7 @@ class TestScoping: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! - val module = result.compilerAst.toplevelModule - val mainBlock = module.statements.single() as Block + val mainBlock = result.compilerAst.entrypoint.definingBlock val start = mainBlock.statements.single() as Subroutine val repeatbody = start.statements.filterIsInstance().single().body withClue("no vars moved to main block") { @@ -120,8 +119,7 @@ class TestScoping: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = true)!! - val module = result.compilerAst.toplevelModule - val mainBlock = module.statements.single() as Block + val mainBlock = result.compilerAst.entrypoint.definingBlock val start = mainBlock.statements.single() as Subroutine val labels = start.statements.filterIsInstance