mirror of
https://github.com/irmen/prog8.git
synced 2024-09-09 02:54:28 +00:00
fixed possible register subroutine arg clobbering
This commit is contained in:
parent
3050156325
commit
71e678b382
@ -5,6 +5,7 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.RegisterOrStatusflag
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
@ -25,8 +26,48 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
|
|
||||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// via variables
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
translateFuncArguments(arg.first, arg.second, sub)
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// via registers
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
// just a single parameter, no risk of clobbering registers
|
||||||
|
argumentViaRegister(sub, sub.parameters.withIndex().single(), stmt.args[0])
|
||||||
|
} 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(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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
@ -35,15 +76,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
val sourceIDt = value.inferType(program)
|
|
||||||
if(!sourceIDt.isKnown)
|
|
||||||
throw AssemblyError("arg type unknown")
|
|
||||||
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
|
||||||
throw AssemblyError("argument type incompatible")
|
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
|
||||||
// pass parameter via a regular variable (not via registers)
|
// pass parameter via a regular variable (not via registers)
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramVar = parameter.value
|
val paramVar = parameter.value
|
||||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
@ -93,8 +134,17 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.assignFromEvalResult(target)
|
asmgen.assignFromEvalResult(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// pass parameter via a register parameter
|
|
||||||
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
|
// pass argument via a register parameter
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
val statusflag = paramRegister.statusflag
|
val statusflag = paramRegister.statusflag
|
||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
@ -143,12 +193,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
|
pha
|
||||||
lda $ESTACK_LO_HEX,x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+ pla
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +254,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
if(valueDt in PassByReferenceDatatypes) {
|
||||||
when (register) {
|
when (register) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
@ -230,7 +281,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
if(argType isAssignableTo paramType)
|
if(argType isAssignableTo paramType)
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
- BUG FIX: fix register argument clobbering when calling asmsubs. (see fixme_argclobber.p8)
|
|
||||||
|
|
||||||
|
|
||||||
- finalize (most) of the still missing "new" assignment asm code generation
|
- finalize (most) of the still missing "new" assignment asm code generation
|
||||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
||||||
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
||||||
|
@ -5,15 +5,23 @@
|
|||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
|
||||||
; TODO: fix register argument clobbering when calling asmsubs.
|
; TODO: optimize register arg value passing when there is no risk of register clobbering
|
||||||
; for instance if the first arg goes into Y, and the second in A,
|
|
||||||
; but when calculating the second argument clobbers Y, the first argument gets destroyed.
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
function(20, calculate())
|
function(20, calculate())
|
||||||
asmfunction(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')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
@ -38,6 +46,37 @@ main {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
sub calculate() -> ubyte {
|
||||||
Y = 99
|
Y = 99
|
||||||
return Y
|
return Y
|
||||||
|
@ -8,10 +8,6 @@
|
|||||||
; some simple sound effects
|
; some simple sound effects
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; TODO fix noCollision() at bottom when compiled without optimizations (codegen issue).
|
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
const ubyte boardOffsetX = 14
|
const ubyte boardOffsetX = 14
|
||||||
@ -549,7 +545,6 @@ blocklogic {
|
|||||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in 15 downto 0 {
|
for i in 15 downto 0 {
|
||||||
; TODO FIX THIS when compiling without optimizations (codegen problem: clobbering register arguments, see fixme_argclobber):
|
|
||||||
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -5,32 +5,8 @@
|
|||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
|
||||||
; TODO: fix register argument clobbering when calling asmsubs.
|
|
||||||
; for instance if the first arg goes into Y, and the second in A,
|
|
||||||
; but when calculating the second argument clobbers Y, the first argument gets destroyed.
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
turtle.pu()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
turtle {
|
|
||||||
ubyte pendown
|
|
||||||
|
|
||||||
sub pu() {
|
|
||||||
pendown = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user