From 823eaa89181f3785247855e886146849d43fc9ac Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 4 Jan 2025 16:06:46 +0100 Subject: [PATCH] when using @requirezp in a subroutine that is unused (and is pruned), don't give an error there when using %zeropage dontuse give warning when a pointer var is declared as @nozp (very inefficient) --- .../compiler/astprocessing/AstChecker.kt | 43 +++++++++++++++++-- .../astprocessing/VerifyFunctionArgTypes.kt | 7 +++ docs/source/todo.rst | 19 ++++++++ examples/test.p8 | 26 ++++++----- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index b04b98dc9..eb90b85e3 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -912,9 +912,6 @@ internal class AstChecker(private val program: Program, } } - if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE) - err("zeropage usage has been disabled by options") - if(decl.datatype.isSplitWordArray) { if (!decl.datatype.isWordArray) { errors.err("split can only be used on word arrays", decl.position) @@ -1415,6 +1412,15 @@ internal class AstChecker(private val program: Program, } } + if(functionCallExpr.target.nameInSource.singleOrNull() in listOf("peek", "peekw")) { + val pointervar = functionCallExpr.args[0] as? IdentifierReference + if(pointervar!=null) + checkPointer(pointervar) + val binexpr = functionCallExpr.args[0] as? BinaryExpression + if(binexpr?.left is IdentifierReference && binexpr.right is NumericLiteral) + checkPointer(binexpr.left as IdentifierReference) + } + super.visit(functionCallExpr) } @@ -1489,6 +1495,15 @@ internal class AstChecker(private val program: Program, if(error!=null) errors.err(error.first, error.second) + if(functionCallStatement.target.nameInSource.singleOrNull() in listOf("poke", "pokew")) { + val pointervar = functionCallStatement.args[0] as? IdentifierReference + if(pointervar!=null) + checkPointer(pointervar) + val binexpr = functionCallStatement.args[0] as? BinaryExpression + if(binexpr?.left is IdentifierReference && binexpr.right is NumericLiteral) + checkPointer(binexpr.left as IdentifierReference) + } + super.visit(functionCallStatement) } @@ -1559,6 +1574,12 @@ internal class AstChecker(private val program: Program, } } + private fun checkPointer(pointervar: IdentifierReference) { + val vardecl = pointervar.targetVarDecl(program) + if(vardecl?.zeropage == ZeropageWish.NOT_IN_ZEROPAGE) + errors.info("pointer variable should preferrably be in zeropage but is marked nozp", vardecl.position) + } + override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { checkLongType(arrayIndexedExpression) val target = arrayIndexedExpression.arrayvar.targetStatement(program) @@ -1687,6 +1708,14 @@ internal class AstChecker(private val program: Program, errors.err("address for memory access isn't uword", memread.position) } } + + val pointervar = memread.addressExpression as? IdentifierReference + if(pointervar!=null) + checkPointer(pointervar) + val binexpr = memread.addressExpression as? BinaryExpression + if(binexpr?.left is IdentifierReference && binexpr.right is NumericLiteral) + checkPointer(binexpr.left as IdentifierReference) + super.visit(memread) } @@ -1700,6 +1729,14 @@ internal class AstChecker(private val program: Program, errors.err("address for memory access isn't uword", memwrite.position) } } + + val pointervar = memwrite.addressExpression as? IdentifierReference + if(pointervar!=null) + checkPointer(pointervar) + val binexpr = memwrite.addressExpression as? BinaryExpression + if(binexpr?.left is IdentifierReference && binexpr.right is NumericLiteral) + checkPointer(binexpr.left as IdentifierReference) + super.visit(memwrite) } diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index aa43db879..e42994d77 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -70,6 +70,13 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila super.visit(functionCallStatement) } + override fun visit(decl: VarDecl) { + if(options.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE) + errors.err("zeropage usage has been disabled by options", decl.position) + + super.visit(decl) + } + companion object { private fun argTypeCompatible(argDt: DataType, paramDt: DataType): Boolean { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a0202551f..9d79c8dd1 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -78,6 +78,25 @@ Libraries Optimizations ------------- + +- optimize word*$0080 (also *$0040); don't shift 7 times, but swap lsb/msb (rockrunner) +- if abs(dx) < cave.VISIBLE_CELLS_H looks like it treats abs(dx) as signed word still in codegen? (rockrunner) +- word offset = (row as uword) * 128 + col*2 inefficient code for col*2 (rockrunner) +- is @(cells+offset) same code as cells[offset] ? +- if magicwall_enabled and (jiffy_counter & 3 == 1) sounds.magicwall() -> generates shortcut jump to another jump, why not immediately after the if +- explode(x, y+1) pushes x on the stack and pops it, could simply load it in reverse order and not use the stack.normal +- return mkword(attrs[cx16.r2L], object[cx16.r2L]) same as the explode() above +- @(cell_ptr-1) = objects.amoeba uses temp zp pointer, also when cell_ptr is zp already? +- x = y + z more efficient if rewritten to x=y; x+=z ? +- return peekw(table+64+pos*2) .... or rather .. return -> can this be optimized by using a temporary variable and chop up the expression? + likewise cx16.r0 = (gfx_lores.WIDTH-bmx.width)/2 + (gfx_lores.HEIGHT-bmx.height)/2*gfx_lores.WIDTH a lot of register juggling +- is there a trick to make @(pointer-1) = v more efficient? (like @(pointer+1)=v using Y indexed) +- if sv=="aa" else if sv=="bb" else if sv=="cc" -> needs much code, allow when(stringvar) too to avoid reloading both strings for every case (rockrunner bdcff.p8) +- if cx16.r0L>=97 and cx16.r0L<=122 {...} -> treats the boolean condition as a byte 0/1 result , can't it somehow just act on the carry bit alone? + same with if x1!=x2 or y1!=y2..... but it's because of the way boolean expressions are handled... can this be optimized? +- min(x1,x2) lots of code in a temp word, can just use the existing variables x1 and x2 + + - Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression" - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served? for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest diff --git a/examples/test.p8 b/examples/test.p8 index 24fb36635..e1f536b51 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,22 +1,20 @@ %import textio %option no_sysinit -%zeropage basicsafe +%zeropage dontuse main { sub start() { - txt.print("\n\n\n\n\n\n !") + uword @nozp pointer + pointer++ - ubyte column, row - - column, row = txt.get_cursor() - txt.nl() - txt.print_ub(column) - txt.spc() - txt.print_ub(row) - txt.nl() - - - repeat { - } + cx16.r0L = @(pointer) + cx16.r1L = @(pointer+100) + cx16.r1 = peekw(pointer) + cx16.r2 = peekw(pointer+100) + cx16.r3L = peek(pointer+100) + @(pointer) = 99 + @(pointer+100) = 99 + poke(pointer, 99) + pokew(pointer, 99) } }