From b40e1eabb9782bc3b49e3ae150217d02bf7dc7ad Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 27 Dec 2020 02:22:18 +0100 Subject: [PATCH] added memory() function for memory slab allocations --- .../src/prog8/ast/processing/AstChecker.kt | 13 +++--- .../src/prog8/ast/statements/AstStatements.kt | 1 + .../compiler/target/c64/codegen/AsmGen.kt | 16 +++++++ .../c64/codegen/BuiltinFunctionsAsmGen.kt | 30 +++++++++++++ .../src/prog8/functions/BuiltinFunctions.kt | 3 +- docs/source/programming.rst | 8 ++++ docs/source/todo.rst | 1 + examples/test.p8 | 43 ++++++++++++++----- syntax-files/IDEA/Prog8.xml | 4 +- 9 files changed, 101 insertions(+), 18 deletions(-) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 40358495a..a06eb908a 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -964,11 +964,14 @@ internal class AstChecker(private val program: Program, val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement) if(targetStatement!=null) checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position) - if(!functionCallStatement.void && targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) { - if(targetStatement.returntypes.size==1) - errors.warn("result value of subroutine call is discarded (use void?)", functionCallStatement.position) - else - errors.warn("result values of subroutine call are discarded (use void?)", functionCallStatement.position) + if (!functionCallStatement.void) { + // check for unused return values + if (targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) { + if(targetStatement.returntypes.size==1) + errors.warn("result value of subroutine call is discarded (use void?)", functionCallStatement.position) + else + errors.warn("result values of subroutine call are discarded (use void?)", functionCallStatement.position) + } } if(functionCallStatement.target.nameInSource.last() == "sort") { diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 0a5f2801d..ffc785049 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -697,6 +697,7 @@ class AsmGenInfo { var usedRegsaveY = false var usedFloatEvalResultVar1 = false var usedFloatEvalResultVar2 = false + val removals = mutableListOf() class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index b833a838e..4d01c448e 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -50,6 +50,7 @@ internal class AsmGen(private val program: Program, private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen) internal val loopEndLabels = ArrayDeque() private val blockLevelVarInits = mutableMapOf>() + internal val slabs = mutableMapOf() override fun compileToAssembly(): IAssemblyProgram { assemblyLines.clear() @@ -63,6 +64,7 @@ internal class AsmGen(private val program: Program, throw AssemblyError("first block should be 'main'") for(b in program.allBlocks()) block2asm(b) + slaballocations() footer() val outputFile = outputDir.resolve("${program.name}.asm").toFile() @@ -152,6 +154,14 @@ internal class AsmGen(private val program: Program, out(" jmp main.start ; start program / force start proc to be included") } + private fun slaballocations() { + out("; memory slabs") + out("prog8_slabs\t.block") + for((name, size) in slabs) + out("$name\t.fill $size") + out("\t.bend") + } + private fun footer() { // the global list of all floating point constants for the whole program out("; global float constants") @@ -834,6 +844,12 @@ internal class AsmGen(private val program: Program, out("; statements") sub.statements.forEach{ translate(it) } + + for(stmt in sub.asmGenInfo.removals) { + sub.remove(stmt) + } + sub.asmGenInfo.removals.clear() + out("; variables") out("; register saves") if(sub.asmGenInfo.usedRegsaveA) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 86fe209d0..2ba1136e0 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -108,10 +108,40 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val else asmgen.out(" lda #prog8_program_end") } + "memory" -> funcMemory(fcall, discardResult, resultToStack) else -> TODO("missing asmgen for builtin func ${func.name}") } } + private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean) { + if(discardResult || fcall !is FunctionCall) + throw AssemblyError("should not discard result of memory allocation at $fcall") + val scope = fcall.definingScope() + val nameRef = fcall.args[0] as IdentifierReference + val name = (nameRef.targetVarDecl(program.namespace)!!.value as StringLiteralValue).value + val size = (fcall.args[1] as NumericLiteralValue).number.toInt() + + val existingSize = asmgen.slabs[name] + if(existingSize!=null && existingSize!=size) + throw AssemblyError("memory slab '$name' already exists with a different size ($size) at ${fcall.position}") + + val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position) + slabname.linkParents(fcall) + val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position)) + val target = + if(resultToStack) + AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null) + else + AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen) + val assign = AsmAssignment(src, target, false, fcall.position) + asmgen.translateNormalAssignment(assign) + + // remove the variable for the name, it's not used as a variable only as a tag for the assembler. + val nameDecl = scope.statements.single { it is VarDecl && it.name==nameRef.nameInSource.single() } + (scope as Subroutine).asmGenInfo.removals.add(nameDecl) + asmgen.slabs[name] = size + } + private fun funcMemSetCopy(fcall: IFunctionCall, func: FSignature, scope: Subroutine) { if(CompilationTarget.instance is Cx16Target) { when(func.name) { diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 967496b6e..ae7aaaf4d 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -143,10 +143,11 @@ private val functionSignatures: List = listOf( FSignature("clear_carry" , false, emptyList(), null), FSignature("set_irqd" , false, emptyList(), null), FSignature("clear_irqd" , false, emptyList(), null), - FSignature("read_flags" , false, emptyList(), DataType.UBYTE), + FSignature("read_flags" , true, emptyList(), DataType.UBYTE), FSignature("progend" , true, emptyList(), DataType.UWORD), FSignature("target" , true, emptyList(), DataType.UBYTE, ::builtinTarget), FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null), + FSignature("memory" , false, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD), FSignature("memcopy" , false, listOf( FParam("from", IterableDatatypes + DataType.UWORD), FParam("to", IterableDatatypes + DataType.UWORD), diff --git a/docs/source/programming.rst b/docs/source/programming.rst index cc63cd68e..c2e06a4dc 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -931,6 +931,14 @@ target() - 16 = compiled for CommanderX16 with 65C02 CPU - 64 = compiled for Commodore-64 with 6502/6510 CPU +memory(name, size) + Statically allocates a fixed portion of memory of the given size in bytes, and returns its address. + Slabs are considered identical if their name and size are the same. + This can be used to allocate parts of the memory where a normal byte array would + not suffice for instance if you need more than 256 bytes, and/or don't want to work + with fixed memory addresses for buffers. + The portion of memory cannot be used as an array, you only have the address of the first byte. + progend() Returns the last address of the program in memory + 1. Can be used to load dynamic data after the program, instead of hardcoding something. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index af68cc74d..78a02761a 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,7 @@ TODO ==== +- detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' - option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging) diff --git a/examples/test.p8 b/examples/test.p8 index 98009e197..db52e689c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,19 +1,42 @@ -;%import test_stack -;%import textio -%import gfx2 +%import test_stack +%import textio %zeropage basicsafe %option no_sysinit main { sub start () { -; txt.lowercase() -; txt.print_ub(txt.width()) -; txt.chrout('\n') -; txt.print_ub(txt.height()) -; txt.chrout('\n') - gfx2.text(0,0,2, "sdafsdf") -; test_stack.test() + + thing() + thing() + thing() + thing() + thing() + test_stack.test() + + sub thing() -> ubyte { + uword buffer = memory("buffer", 512) + uword buffer2 = memory("buffer", 512) + uword buffer3 = memory("cache", 20) + + txt.print_uwhex(buffer, true) + txt.chrout('\n') + txt.print_uwhex(buffer2, true) + txt.chrout('\n') + txt.print_uwhex(buffer3, true) + txt.chrout('\n') + buffer+=$1111 + buffer2+=$1111 + buffer3+=$1111 + txt.print_uwhex(buffer, true) + txt.chrout('\n') + txt.print_uwhex(buffer2, true) + txt.chrout('\n') + txt.print_uwhex(buffer3, true) + txt.chrout('\n') + txt.chrout('\n') + return 0 + } } } diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index dd2011573..93da5721e 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -11,10 +11,10 @@