code size optimization: subroutine calls with 1 int arg will pass it via register instead of separate param assignment at every call site

This commit is contained in:
Irmen de Jong 2021-12-16 00:56:51 +01:00
parent 08f87c321f
commit 629117e594
5 changed files with 62 additions and 18 deletions

View File

@ -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.AsmAssignment
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
import prog8.compiler.target.cpu6502.codegen.assignment.SourceStorageKind import prog8.compiler.target.cpu6502.codegen.assignment.SourceStorageKind
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
import prog8.compilerinterface.* import prog8.compilerinterface.*
import prog8.parser.SourceCode import prog8.parser.SourceCode
import java.nio.file.Path import java.nio.file.Path
@ -1015,6 +1016,16 @@ class AsmGen(private val program: Program,
clc""") 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) { if(!onlyVariables) {
out("; statements") out("; statements")
sub.statements.forEach { translate(it) } sub.statements.forEach { translate(it) }

View File

@ -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) { internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
// Output only the code to set up the parameters and perform the actual call // 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! // 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 sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
val subAsmName = asmgen.asmSymbolName(call.target) val subAsmName = asmgen.asmSymbolName(call.target)
if(!isExpression && !sub.isAsmSubroutine) if(!isExpression && !sub.isAsmSubroutine) {
if(!singleArgViaRegisters(sub))
throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call") throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call")
}
if(sub.isAsmSubroutine) { if(sub.isAsmSubroutine) {
argumentsViaRegisters(sub, call) argumentsViaRegisters(sub, call)
@ -98,7 +102,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
if(sub.inline) if(sub.inline)
throw AssemblyError("can only reliably inline asmsub routines at this time") throw AssemblyError("can only reliably inline asmsub routines at this time")
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) argumentsViaVariables(sub, call)
}
asmgen.out(" jsr $subAsmName") 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) asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub)
} }
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) { private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) {
// pass argument via a register parameter // pass argument via a register parameter
val valueIDt = value.inferType(program) val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
throw AssemblyError("argument type incompatible") 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 statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type val requiredDt = parameter.value.type

View File

@ -392,11 +392,15 @@ internal class StatementReorderer(val program: Program,
return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent)) return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent))
} }
// if(function.parameters.size==1) { if(function.parameters.size==1) {
// // 1 param // 1 param
// val dt = function.parameters[0].type val dt = function.parameters[0].type
// if(dt in IntegerDatatypes) 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 = val assignParams =
function.parameters.zip(call.args).map { function.parameters.zip(call.args).map {

View File

@ -324,9 +324,9 @@ class TestSubroutines: FunSpec({
val text = """ val text = """
main { main {
sub start() { sub start() {
func(1) func(1, 2, 3)
sub func(ubyte a) { sub func(ubyte a, ubyte b, ubyte c) {
a++ a++
} }
} }

View File

@ -1,22 +1,42 @@
%import textio
%import floats %import floats
%zeropage basicsafe
main { main {
sub start() { sub start() {
singleparamb(123)
singleparamb(10) singleparamw(-9999)
singleparamw(2000) doubleparamb(123,-99)
doubleparamw(8888,-9999)
singleparamf(1.23456) singleparamf(1.23456)
} }
sub singleparamb(ubyte bb) { 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) { 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) { sub singleparamf(float ff) {
ff++ floats.print_f(ff)
txt.nl()
} }
} }