fix invalid handling of X register functioncall result value

This commit is contained in:
Irmen de Jong 2021-01-05 01:41:32 +01:00
parent 8dcb43ad1c
commit 3550e1214c
6 changed files with 65 additions and 55 deletions

View File

@ -1029,8 +1029,6 @@ internal class AstChecker(private val program: Program,
} }
} }
} else if(target is Subroutine) { } else if(target is Subroutine) {
if(target.regXasResult())
errors.warn("subroutine call return value in X register is discarded and replaced by 0", position)
if(target.isAsmSubroutine) { if(target.isAsmSubroutine) {
for (arg in args.zip(target.parameters)) { for (arg in args.zip(target.parameters)) {
val argIDt = arg.first.inferType(program) val argIDt = arg.first.inferType(program)

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.c64.codegen
import prog8.ast.IFunctionCall
import prog8.ast.INameScope import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
@ -792,6 +793,12 @@ internal class AsmGen(private val program: Program,
internal fun translateFunctionCall(functionCall: FunctionCall) = internal fun translateFunctionCall(functionCall: FunctionCall) =
functioncallAsmGen.translateFunctionCall(functionCall) functioncallAsmGen.translateFunctionCall(functionCall)
internal fun saveXbeforeCall(functionCall: IFunctionCall) =
functioncallAsmGen.saveXbeforeCall(functionCall)
internal fun restoreXafterCall(functionCall: IFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall)
internal fun translateNormalAssignment(assign: AsmAssignment) = internal fun translateNormalAssignment(assign: AsmAssignment) =
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)

View File

@ -1324,49 +1324,34 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
+""") +""")
} }
private fun translateFunctionCallResultOntoStack(expression: FunctionCall) { private fun translateFunctionCallResultOntoStack(call: FunctionCall) {
// only for use in nested expression evaluation // only for use in nested expression evaluation
val sub = expression.target.targetStatement(program.namespace) val sub = call.target.targetStatement(program.namespace)
if(sub is BuiltinFunctionStatementPlaceholder) { if(sub is BuiltinFunctionStatementPlaceholder) {
val builtinFunc = BuiltinFunctions.getValue(sub.name) val builtinFunc = BuiltinFunctions.getValue(sub.name)
asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true) asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true)
} else { } else {
sub as Subroutine sub as Subroutine
asmgen.translateFunctionCall(expression) asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call)
if(sub.regXasResult()) {
// store the return value in X somewhere that we can acces again below
asmgen.out(" stx P8ZP_SCRATCH_REG")
}
asmgen.restoreXafterCall(call)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((_, reg) in returns) { for ((_, reg) in returns) {
// result value in cpu or status registers, put it on the stack // result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
if (reg.registerOrPair != null) { if (reg.registerOrPair != null) {
when (reg.registerOrPair) { when (reg.registerOrPair) {
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex") RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex") RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
RegisterOrPair.X -> { RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
// return value in X register has been discarded, just push a zero RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
asmgen.out(" stz P8ESTACK_LO,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
asmgen.out(" dex")
}
RegisterOrPair.AX -> {
// return value in X register has been discarded, just push a zero in this place
asmgen.out(" sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
asmgen.out(" dex")
}
RegisterOrPair.XY -> {
// return value in X register has been discarded, just push a zero in this place
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_LO,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
asmgen.out(" tya | sta P8ESTACK_HI,x | dex")
}
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1") RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2") RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
RegisterOrPair.R0, RegisterOrPair.R0,

View File

@ -7,7 +7,6 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.CompilationOptions
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.* import prog8.compiler.target.c64.codegen.assignment.*
@ -16,25 +15,44 @@ import prog8.compiler.target.c64.codegen.assignment.*
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translateFunctionCallStatement(stmt: IFunctionCall) { internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt) translateFunctionCall(stmt)
// functioncalls no longer return results on the stack, so simply ignore the results in the registers restoreXafterCall(stmt)
// just ignore any result values from the function call.
} }
internal fun saveXbeforeCall(stmt: IFunctionCall) {
internal fun translateFunctionCall(stmt: IFunctionCall) {
// output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = sub.shouldSaveX() if(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 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
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA() val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
if(saveX) {
if(regSaveOnStack) if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, keepAonEntry) asmgen.saveRegisterStack(CpuRegister.X, keepAonEntry)
else else
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine()!!) asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine()!!)
} }
}
internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(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
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, keepAonReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun translateFunctionCall(stmt: IFunctionCall) {
// Output only the code to setup the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val subName = asmgen.asmSymbolName(stmt.target) val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) { if(stmt.args.isNotEmpty()) {
if(sub.asmParameterRegisters.isEmpty()) { if(sub.asmParameterRegisters.isEmpty()) {
@ -84,12 +102,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName") asmgen.out(" jsr $subName")
} }
if(saveX) { // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, keepAonReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
} }
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) { private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {

View File

@ -142,7 +142,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is FunctionCall -> { is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) { when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> { is Subroutine -> {
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value) asmgen.translateFunctionCall(value)
if(!sub.regXasResult())
asmgen.restoreXafterCall(value)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null } sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
when (returnValue.first) { when (returnValue.first) {
@ -186,6 +189,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
throw AssemblyError("should be just one register byte result value") throw AssemblyError("should be just one register byte result value")
} }
} }
// we've processed the result value in the X register by now, so it's now safe to restore it:
asmgen.restoreXafterCall(value)
} }
} }
} }

View File

@ -6,14 +6,16 @@
main { main {
sub start () { sub start () {
uword current_time uword mem
mem = 5*mem*c64.MEMTOP(0,1)
txt.print_uwhex(mem, 1)
mem = 5*mem*c64.CHRIN()
txt.print_uwhex(mem,1)
test_stack.test()
repeat 6 {
current_time = c64.RDTIM16()
txt.print_uw(current_time)
txt.chrout('\n')
sys.wait(30)
}
; found = strfind("irmen de jong", ' ') ; found = strfind("irmen de jong", ' ')