mirror of
https://github.com/irmen/prog8.git
synced 2025-01-12 19:29:50 +00:00
optimized arg passing if all args are registers
This commit is contained in:
parent
71e678b382
commit
cdcb652033
@ -178,8 +178,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
private fun translateExpression(expr: RegisterExpr) {
|
||||
when(expr.register) {
|
||||
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
||||
Register.X -> asmgen.out(" txa | sta $ESTACK_LO_HEX,x | dex")
|
||||
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
||||
Register.X -> asmgen.out(" pha | txa | sta $ESTACK_LO_HEX,x | dex | pla")
|
||||
Register.Y -> asmgen.out(" pha | tya | sta $ESTACK_LO_HEX,x | dex | pla")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.RegisterOrStatusflag
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.compiler.AssemblyError
|
||||
@ -39,32 +38,29 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
} else {
|
||||
// multiple register arguments, risk of register clobbering.
|
||||
// evaluate arguments onto the stack, and load the registers from the evaluated values on the stack.
|
||||
// TODO optimize this for the cases where all args aren't expressions (no risk of clobbering in that case)
|
||||
for(arg in stmt.args.reversed())
|
||||
asmgen.translateExpression(arg)
|
||||
for(regparam in sub.asmParameterRegisters) {
|
||||
when(regparam.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||
RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
null -> {}
|
||||
when {
|
||||
stmt.args.all {it is AddressOf ||
|
||||
it is NumericLiteralValue ||
|
||||
it is StructLiteralValue ||
|
||||
it is StringLiteralValue ||
|
||||
it is ArrayLiteralValue ||
|
||||
it is IdentifierReference} -> {
|
||||
// no risk of clobbering for these simple argument types. Optimize the register loading.
|
||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||
argumentViaRegister(sub, arg.first, arg.second)
|
||||
}
|
||||
}
|
||||
when(regparam.statusflag) {
|
||||
Statusflag.Pc -> asmgen.out("""
|
||||
inx
|
||||
pha
|
||||
lda $ESTACK_LO_HEX,x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ pla
|
||||
""")
|
||||
null -> {}
|
||||
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
stmt.args.all {it is RegisterExpr} -> {
|
||||
val argRegisters = stmt.args.map {(it as RegisterExpr).register.toString()}
|
||||
val paramRegisters = sub.asmParameterRegisters.map { it.registerOrPair?.toString() }
|
||||
if(argRegisters != paramRegisters) {
|
||||
// all args are registers but differ from the function params. Can't pass directly, work via stack.
|
||||
argsViaStackEvaluation(stmt, sub)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||
argsViaStackEvaluation(stmt, sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,6 +72,38 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||
}
|
||||
|
||||
private fun argsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
for (arg in stmt.args.reversed())
|
||||
asmgen.translateExpression(arg)
|
||||
for (regparam in sub.asmParameterRegisters) {
|
||||
when (regparam.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||
RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
null -> {
|
||||
}
|
||||
}
|
||||
when (regparam.statusflag) {
|
||||
Statusflag.Pc -> asmgen.out("""
|
||||
inx
|
||||
pha
|
||||
lda $ESTACK_LO_HEX,x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ pla
|
||||
""")
|
||||
null -> {
|
||||
}
|
||||
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
// pass parameter via a regular variable (not via registers)
|
||||
val valueIDt = value.inferType(program)
|
||||
|
@ -1,86 +0,0 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
%option enable_floats
|
||||
|
||||
|
||||
; TODO: optimize register arg value passing when there is no risk of register clobbering
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
function(20, calculate())
|
||||
asmfunction(20, calculate())
|
||||
asmfunction2(1, 2) ; TODO optimize
|
||||
ubyte arg = 3
|
||||
ubyte arg2 = 4
|
||||
asmfunction3(arg, arg2) ; TODO optimize
|
||||
Y=5
|
||||
A=6
|
||||
asmfunction4(Y,A) ; TODO optimize
|
||||
A=7
|
||||
Y=8
|
||||
asmfunction5(A,Y) ; TODO cannot optimize, fix result
|
||||
|
||||
c64.CHROUT('\n')
|
||||
|
||||
if @($0400)==@($0402) and @($0401) == @($0403) {
|
||||
c64scr.print("ok: results are same\n")
|
||||
} else {
|
||||
c64scr.print("error: result differ; arg got clobbered\n")
|
||||
}
|
||||
}
|
||||
|
||||
sub function(ubyte a1, ubyte a2) {
|
||||
; non-asm function passes via stack, this is ok
|
||||
@($0400) = a1
|
||||
@($0401) = a2
|
||||
}
|
||||
|
||||
asmsub asmfunction(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $0402
|
||||
sta $0403
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub asmfunction2(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $0404
|
||||
sta $0405
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub asmfunction3(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $0406
|
||||
sta $0407
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub asmfunction4(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $0408
|
||||
sta $0409
|
||||
}}
|
||||
}
|
||||
asmsub asmfunction5(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $040a
|
||||
sta $040b
|
||||
}}
|
||||
}
|
||||
|
||||
sub calculate() -> ubyte {
|
||||
Y = 99
|
||||
return Y
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user