mirror of
https://github.com/irmen/prog8.git
synced 2024-11-27 03:50:27 +00:00
code size optimization: subroutine calls with 2 byte arg will pass it via A/Y registers instead of separate param assignments at every call site
This commit is contained in:
parent
629117e594
commit
3cf9b9d9a5
@ -1016,14 +1016,23 @@ class AsmGen(private val program: Program,
|
|||||||
clc""")
|
clc""")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(functioncallAsmGen.singleArgViaRegisters(sub)) {
|
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||||
out("; single arg is passed via register(s)")
|
out("; simple int arg(s) passed via register(s)")
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
val dt = sub.parameters[0].type
|
val dt = sub.parameters[0].type
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, dt, sub, variableAsmName = sub.parameters[0].name)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, dt, sub, variableAsmName = sub.parameters[0].name)
|
||||||
if(dt in ByteDatatypes)
|
if(dt in ByteDatatypes)
|
||||||
assignRegister(RegisterOrPair.A, target)
|
assignRegister(RegisterOrPair.A, target)
|
||||||
else
|
else
|
||||||
assignRegister(RegisterOrPair.AY, target)
|
assignRegister(RegisterOrPair.AY, target)
|
||||||
|
} else {
|
||||||
|
require(sub.parameters.size==2)
|
||||||
|
// 2 simple byte args, first in A, second in Y
|
||||||
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
||||||
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
||||||
|
assignRegister(RegisterOrPair.A, target1)
|
||||||
|
assignRegister(RegisterOrPair.Y, target2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!onlyVariables) {
|
if(!onlyVariables) {
|
||||||
|
@ -67,7 +67,9 @@ 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 optimizeIntArgsViaRegisters(sub: Subroutine) =
|
||||||
|
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||||
|
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||||
|
|
||||||
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
|
||||||
@ -79,7 +81,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val subAsmName = asmgen.asmSymbolName(call.target)
|
val subAsmName = asmgen.asmSymbolName(call.target)
|
||||||
|
|
||||||
if(!isExpression && !sub.isAsmSubroutine) {
|
if(!isExpression && !sub.isAsmSubroutine) {
|
||||||
if(!singleArgViaRegisters(sub))
|
if(!optimizeIntArgsViaRegisters(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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +104,19 @@ 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)) {
|
if(optimizeIntArgsViaRegisters(sub)) {
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
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)
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||||
|
} else {
|
||||||
|
// 2 byte params, second in Y, first in A
|
||||||
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
||||||
|
if(!call.args[1].isSimple)
|
||||||
|
asmgen.out(" pha")
|
||||||
|
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
||||||
|
if(!call.args[1].isSimple)
|
||||||
|
asmgen.out(" pla")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
argumentsViaVariables(sub, call)
|
argumentsViaVariables(sub, call)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compilerinterface.BuiltinFunctions
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compilerinterface.CompilationOptions
|
import prog8.compilerinterface.CompilationOptions
|
||||||
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compilerinterface.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
@ -393,11 +394,14 @@ internal class StatementReorderer(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(function.parameters.size==1) {
|
if(function.parameters.size==1) {
|
||||||
// 1 param
|
if(function.parameters[0].type in IntegerDatatypes) {
|
||||||
val dt = function.parameters[0].type
|
// optimization: 1 integer param is passed via register(s) directly, not by assignment to param variable
|
||||||
if(dt in IntegerDatatypes) {
|
return noModifications
|
||||||
// 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
|
}
|
||||||
|
else if(function.parameters.size==2) {
|
||||||
|
if(function.parameters[0].type in ByteDatatypes && function.parameters[1].type in ByteDatatypes) {
|
||||||
|
// optimization: 2 simple byte param is passed via 2 registers directly, not by assignment to param variables
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,6 @@ TODO
|
|||||||
|
|
||||||
For next compiler release (7.6)
|
For next compiler release (7.6)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
optimization in call convention:
|
|
||||||
non-asm subroutines with just a single byte or word parameter:
|
|
||||||
pass the parameter via A or A/Y registers.
|
|
||||||
add code to set the parameter variable in the start of the subroutine itself,
|
|
||||||
rather than requiring the caller to set it there. This is not faster but saves a lot of bytes of code.
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,10 +4,17 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
|
ubyte xx = 0
|
||||||
|
|
||||||
|
singleparamb(123)
|
||||||
singleparamb(123)
|
singleparamb(123)
|
||||||
singleparamw(-9999)
|
singleparamw(-9999)
|
||||||
doubleparamb(123,-99)
|
singleparamw(-9999)
|
||||||
doubleparamw(8888,-9999)
|
doubleparamb(xx+111,-99)
|
||||||
|
doubleparamb(xx+111,-99)
|
||||||
|
doubleparamw(xx+8888,-9999)
|
||||||
|
doubleparamw(xx+8888,-9999)
|
||||||
|
singleparamf(1.23456)
|
||||||
singleparamf(1.23456)
|
singleparamf(1.23456)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user