From 729efb04e1f02751a6059c8ac0494ed47f92b42f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 14 Aug 2025 23:37:59 +0200 Subject: [PATCH] fix code size regressions --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 4 ++-- .../codegen/cpu6502/FunctionCallAsmGen.kt | 2 +- .../astprocessing/BeforeAsmTypecastCleaner.kt | 5 +++++ .../prog8/ast/expressions/AstExpressions.kt | 4 +++- docs/source/technical.rst | 7 +++++-- docs/source/todo.rst | 4 +--- examples/test.p8 | 20 ++++++++++++------- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 373d3ab7e..cb80acfd2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -1223,11 +1223,11 @@ $repeatLabel""") return null val leftDt = left.type val rightDt = right.type - if(leftDt.isUnsignedWord && rightDt.isUnsignedByte) + if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedByte) return Pair(left, right) if(leftDt.isUnsignedByte && rightDt.isUnsignedWord) return Pair(right, left) - if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) { + if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedWord) { // could be that the index was a constant numeric byte but converted to word, check that val constIdx = right as? PtNumber if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index 1df4c4fa2..1d010323a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as // we consider them NOT to be optimized into (possibly different) CPU registers. // Just load them in whatever the register spec says. return when (params.size) { - 1 -> params[0].type.isIntegerOrBool && params[0].register == null + 1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer) 2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null else -> false } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt index 6da17d12f..6ce7bc7b2 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt @@ -53,6 +53,11 @@ internal class BeforeAsmTypecastCleaner(val program: Program, } } + if(typecast.type.isUnsignedWord && sourceDt.isPointer) { + // remove all typecasts of pointers to unsigned words. + return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) + } + return noModifications } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 98b3a3896..ae388afd8 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -1637,7 +1637,9 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal override fun inferType(program: Program): InferredTypes.InferredType { val t1 = truevalue.inferType(program) val t2 = falsevalue.inferType(program) - return if(t1==t2) t1 else InferredTypes.unknown() + if(t1==t2) return t1 + if(t1.isPointer && t2.isUnsignedWord || t1.isUnsignedWord && t2.isPointer) return InferredTypes.knownFor(BaseDataType.UWORD) + return InferredTypes.unknown() } override fun copy(): Expression = IfExpression(condition.copy(), truevalue.copy(), falsevalue.copy(), position) diff --git a/docs/source/technical.rst b/docs/source/technical.rst index 1bb5ad80f..3fca9c135 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -153,7 +153,7 @@ Regular subroutines - A byte value will be put in ``A`` . - A boolean value will be put in ``A`` too, as 0 or 1. - - A word value will be put in ``A`` + ``Y`` register pair (lsb in A, msb in Y). + - A word or pointer value will be put in ``A`` + ``Y`` register pair (lsb in A, msb in Y). - A float value will be put in the ``FAC1`` float 'register'. - In case of *multiple* return values: @@ -171,7 +171,7 @@ Regular subroutines some builtin functions are special and won't exactly follow these rules. **Some arguments will be passed in registers:** -For single byte and word arguments, the values are simply loaded in cpu registers by the caller before calling the subroutine. +For single byte, word, and pointer arguments, the values are simply loaded in cpu registers by the caller before calling the subroutine. *The subroutine itself will take care of putting the values into the parameter variables.* This saves on code size because otherwise all callers would have to store the values in those variables themselves. Note that his convention is also still used for subroutines that specify parameters to be put into @@ -187,6 +187,9 @@ Two byte parameters: ``sub foo(ubyte bar, ubyte baz) { ... }`` Single word parameter: ``sub foo(uword bar) { ... }`` gets bar in the register pair A + Y (lsb in A, msb in Y), *subroutine* stores it into parameter variable +Single pointer parameter: ``sub foo(^^ubyte bar) { ... }`` + gets bar in the register pair A + Y (lsb in A, msb in Y), *subroutine* stores it into parameter variable + Floating point parameter: ``sub foo(float bar) { ... }`` value for bar gets copied into the parameter variable *by the caller* diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 122484e11..40ffa580d 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,8 +5,6 @@ TODO STRUCTS and TYPED POINTERS (6502 codegen specific) -------------------------------------------------- -- fix code size regression. (for instance rockrunner is 200 bytes larger than before) - - fix struct allocations/inits. - prefixSymbols(): what to do with prefixing struct fields? Should they be prefixed with something or no? @@ -17,4 +15,4 @@ STRUCTS and TYPED POINTERS (6502 codegen specific) - update structpointers.rst docs with 6502 specific things? - scan through 6502 library modules to change untyped uword pointers to typed pointers - scan through 6502 examples to change untyped uword pointers to typed pointers - +- fix code size regressions (if any) diff --git a/examples/test.p8 b/examples/test.p8 index 10abe13fe..df3fe1fb4 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,11 +1,17 @@ main { sub start() { - struct Node { - ubyte weight - } - ^^Node nodes - nodes^^.zzz1 = 99 - cx16.r0L = nodes^^.zzz2 - cx16.r0L = nodes[2].zzz3 + void derp("a") + } + + sub derp(str arg) -> ubyte { + if arg==0 + return 42 + if arg==4444 + return 42 + if arg!=0 + return 42 + if arg!=4444 + return 42 + return 1 } }