only save A's value if needed for a return value

This commit is contained in:
Irmen de Jong 2020-12-22 05:43:02 +01:00
parent 5f15794c3b
commit 96c700ee46
9 changed files with 118 additions and 69 deletions

View File

@ -758,6 +758,16 @@ class Subroutine(override val name: String,
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
fun shouldKeepA(): Pair<Boolean, Boolean> {
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
if(!isAsmSubroutine)
return Pair(false, false)
// it seems that we never have to save A when calling? will be loaded correctly after setup.
// but on return it depends on wether the routine returns something in A.
val saveAonReturn = asmReturnvaluesRegisters.any { it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.AX }
return Pair(false, saveAonReturn)
}
fun amountOfRtsInAsm(): Int = statements
.asSequence()

View File

@ -547,59 +547,82 @@ internal class AsmGen(private val program: Program,
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
internal fun saveRegister(register: CpuRegister, scope: Subroutine, useStack: Boolean) {
if(useStack) {
when (register) {
CpuRegister.A -> out(" pha")
CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
else out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
}
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
else out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
when (register) {
CpuRegister.A -> {
out(" sta _prog8_regsaveA")
scope.asmGenInfo.usedRegsaveA = true
}
CpuRegister.X -> {
out(" stx _prog8_regsaveX")
scope.asmGenInfo.usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty _prog8_regsaveY")
scope.asmGenInfo.usedRegsaveY = true
}
}
}
internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) {
CpuRegister.A -> out(" pha")
CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
else {
if(keepA)
out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
else
out(" txa | pha")
}
}
} else {
when (register) {
CpuRegister.A -> {
out(" sta _prog8_regsaveA")
scope.asmGenInfo.usedRegsaveA = true
}
CpuRegister.X -> {
out(" stx _prog8_regsaveX")
scope.asmGenInfo.usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty _prog8_regsaveY")
scope.asmGenInfo.usedRegsaveY = true
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
else {
if(keepA)
out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
else
out(" tya | pha")
}
}
}
}
internal fun restoreRegister(register: CpuRegister, useStack: Boolean) {
if(useStack) {
when (register) {
CpuRegister.A -> out(" pla")
CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
else out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
}
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
else out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
}
}
} else {
when (register) {
CpuRegister.A -> out(" lda _prog8_regsaveA")
CpuRegister.X -> out(" ldx _prog8_regsaveX")
CpuRegister.Y -> out(" ldy _prog8_regsaveY")
}
internal fun restoreRegisterLocal(register: CpuRegister) {
when (register) {
CpuRegister.A -> out(" lda _prog8_regsaveA")
CpuRegister.X -> out(" ldx _prog8_regsaveX")
CpuRegister.Y -> out(" ldy _prog8_regsaveY")
}
}
internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) {
CpuRegister.A -> {
if(keepA)
throw AssemblyError("can't set keepA if A is restored")
out(" pla")
}
CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
else {
if(keepA)
out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
else
out(" pla | tax")
}
}
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
else {
if(keepA)
out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
else
out(" pla | tay")
}
}
}
}
internal fun translate(stmt: Statement) {
outputSourceLine(stmt)

View File

@ -120,9 +120,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1)
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.A)
asmgen.saveRegister(CpuRegister.X, scope, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
asmgen.out(" jsr cx16.memory_fill")
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
"memcopy" -> {
val count = fcall.args[2].constValue(program)?.number?.toInt()
@ -138,9 +138,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1)
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.R2)
asmgen.saveRegister(CpuRegister.X, scope, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
asmgen.out(" jsr cx16.memory_copy")
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
"memsetw" -> {
translateArguments(fcall.args, func, scope)

View File

@ -26,8 +26,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = sub.shouldSaveX()
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(saveX)
asmgen.saveRegister(CpuRegister.X, (stmt as Node).definingSubroutine()!!, regSaveOnStack)
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
if(saveX) {
if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, keepAonEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine()!!)
}
val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) {
@ -64,8 +69,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
}
asmgen.out(" jsr $subName")
if(saveX)
asmgen.restoreRegister(CpuRegister.X, regSaveOnStack)
if(saveX) {
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, keepAonReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {

View File

@ -91,7 +91,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
else
{
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegister(CpuRegister.X, scope!!, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {
@ -119,7 +119,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
else -> throw AssemblyError("weird array elt dt")
}
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
}

View File

@ -2000,9 +2000,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.storeByteIntoPointer(addressExpr, null)
}
else -> {
asmgen.saveRegister(register, memoryAddress.definingSubroutine()!!, true)
asmgen.saveRegisterStack(register, false)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegister(CpuRegister.A, true)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
}

View File

@ -1417,7 +1417,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegister(CpuRegister.X, scope, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"**" -> {
asmgen.out("""
@ -1463,7 +1463,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldy #>$name
jsr floats.MOVMF
""")
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) {
@ -1472,7 +1472,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
throw AssemblyError("float variable expected")
val otherName = asmgen.asmVariableName(ident)
asmgen.saveRegister(CpuRegister.X, scope, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"**" -> {
if(CompilationTarget.instance is Cx16Target) {
@ -1545,12 +1545,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldy #>$name
jsr floats.MOVMF
""")
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
val constValueName = asmgen.getFloatAsmConst(value)
asmgen.saveRegister(CpuRegister.X, scope, false)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"**" -> {
if(CompilationTarget.instance is Cx16Target) {
@ -1630,7 +1630,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldy #>$name
jsr floats.MOVMF
""")
asmgen.restoreRegister(CpuRegister.X, false)
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {

View File

@ -69,14 +69,22 @@ _y .byte 0
}}
}
sub start () {
asmsub withasm(uword foo @R0, ubyte arg1 @A, ubyte arg2 @Y) clobbers(X) -> ubyte @A {
%asm {{
sty P8ZP_SCRATCH_REG
clc
adc P8ZP_SCRATCH_REG
rts
}}
}
txt.print("status 8: ")
uword ss = diskio.status(8)
txt.print(ss)
txt.print("\nstatus 9: ")
ss = diskio.status(9)
txt.print(ss)
sub derp(uword aa)-> uword {
return 9999+aa
}
sub start () {
ubyte ss = withasm(derp(1), 33,66)
txt.print_ub(ss)
txt.chrout('\n')
; cx16.r0 = 65535

View File

@ -4,7 +4,6 @@
%import test_stack
%zeropage floatsafe
; TODO WHY is this a couple of 100 bytes larger than a few hours ago?
main {