From 629117e5942aa8048391b269b4450caa1aef7019 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 16 Dec 2021 00:56:51 +0100 Subject: [PATCH] code size optimization: subroutine calls with 1 int arg will pass it via register instead of separate param assignment at every call site --- .../compiler/target/cpu6502/codegen/AsmGen.kt | 11 +++++++ .../cpu6502/codegen/FunctionCallAsmGen.kt | 19 ++++++++--- .../astprocessing/StatementReorderer.kt | 14 +++++--- compiler/test/TestSubroutines.kt | 4 +-- examples/test.p8 | 32 +++++++++++++++---- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index c6328d795..6295e3909 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -16,6 +16,7 @@ import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen import prog8.compiler.target.cpu6502.codegen.assignment.SourceStorageKind +import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind import prog8.compilerinterface.* import prog8.parser.SourceCode import java.nio.file.Path @@ -1015,6 +1016,16 @@ class AsmGen(private val program: Program, clc""") } + if(functioncallAsmGen.singleArgViaRegisters(sub)) { + out("; single arg is passed via register(s)") + val dt = sub.parameters[0].type + val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, dt, sub, variableAsmName = sub.parameters[0].name) + if(dt in ByteDatatypes) + assignRegister(RegisterOrPair.A, target) + else + assignRegister(RegisterOrPair.AY, target) + } + if(!onlyVariables) { out("; statements") sub.statements.forEach { translate(it) } diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt index a5d8c0fc1..e8c8ff55d 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt @@ -67,6 +67,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } } + internal fun singleArgViaRegisters(sub: Subroutine) = sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes + internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // Output only the code to set up the parameters and perform the actual call // NOTE: does NOT output the code to deal with the result values! @@ -76,8 +78,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}") val subAsmName = asmgen.asmSymbolName(call.target) - if(!isExpression && !sub.isAsmSubroutine) - throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call") + if(!isExpression && !sub.isAsmSubroutine) { + if(!singleArgViaRegisters(sub)) + throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call") + } if(sub.isAsmSubroutine) { argumentsViaRegisters(sub, call) @@ -98,7 +102,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg if(sub.inline) throw AssemblyError("can only reliably inline asmsub routines at this time") - argumentsViaVariables(sub, call) + if(singleArgViaRegisters(sub)) { + val register = if(sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY + argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register) + } else { + argumentsViaVariables(sub, call) + } asmgen.out(" jsr $subAsmName") } @@ -262,14 +271,14 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub) } - private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue, value: Expression) { + private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue, value: Expression, registerOverride: RegisterOrPair? = null) { // pass argument via a register parameter val valueIDt = value.inferType(program) val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") - val paramRegister = sub.asmParameterRegisters[parameter.index] + val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null) val statusflag = paramRegister.statusflag val register = paramRegister.registerOrPair val requiredDt = parameter.value.type diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index affab67c7..289c32c4e 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -392,11 +392,15 @@ internal class StatementReorderer(val program: Program, return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent)) } -// if(function.parameters.size==1) { -// // 1 param -// val dt = function.parameters[0].type -// if(dt in IntegerDatatypes) -// } + if(function.parameters.size==1) { + // 1 param + val dt = function.parameters[0].type + if(dt in IntegerDatatypes) { + // optimization: 1 integer param is passed via registers directly, not by assignment to param variable + // TODO also do this for 2x byte param , can be put in A and Y + return noModifications + } + } val assignParams = function.parameters.zip(call.args).map { diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 5ad7310a2..ac56cdae0 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -324,9 +324,9 @@ class TestSubroutines: FunSpec({ val text = """ main { sub start() { - func(1) + func(1, 2, 3) - sub func(ubyte a) { + sub func(ubyte a, ubyte b, ubyte c) { a++ } } diff --git a/examples/test.p8 b/examples/test.p8 index 93a96b212..6a2f173d9 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,22 +1,42 @@ +%import textio %import floats +%zeropage basicsafe main { sub start() { - - singleparamb(10) - singleparamw(2000) + singleparamb(123) + singleparamw(-9999) + doubleparamb(123,-99) + doubleparamw(8888,-9999) singleparamf(1.23456) } sub singleparamb(ubyte bb) { - bb++ + txt.print_ub(bb) + txt.nl() + } + + sub doubleparamb(ubyte bb, byte bs) { + txt.print_ub(bb) + txt.spc() + txt.print_b(bs) + txt.nl() } sub singleparamw(word ww) { - ww++ + txt.print_w(ww) + txt.nl() + } + + sub doubleparamw(uword uw, word ww) { + txt.print_uw(uw) + txt.spc() + txt.print_w(ww) + txt.nl() } sub singleparamf(float ff) { - ff++ + floats.print_f(ff) + txt.nl() } }