diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt index d6475cb7a..c59f0417e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmsubHelpers.kt @@ -1,9 +1,8 @@ package prog8.codegen.cpu6502 -import prog8.code.ast.* +import prog8.code.ast.PtAsmSub import prog8.code.core.Cx16VirtualRegisters import prog8.code.core.RegisterOrPair -import prog8.code.core.RegisterOrStatusflag fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List { @@ -11,50 +10,19 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List { // order is: // 1) cx16 virtual word registers, // 2) paired CPU registers, - // 3) single CPU registers (X last), except A, + // 3) single CPU registers (order Y,X,A), // 4) CPU Carry status flag - // 5) the A register itself last (so everything before it can use the accumulator without having to save its value) val args = sub.parameters.withIndex() val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters } val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters } - val (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A } - val (regA, rest) = args4.partition { it.value.first.registerOrPair != null } + val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null } cx16regs.forEach { order += it.index } pairedRegs.forEach { order += it.index } - regsWithoutA.forEach { - if(it.value.first.registerOrPair != RegisterOrPair.X) - order += it.index - } - regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index} + singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index } + require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null}) rest.forEach { order += it.index } - regA.forEach { order += it.index } require(order.size==sub.parameters.size) return order } - -fun asmsub6502ArgsHaveRegisterClobberRisk( - args: List, - params: List> -): Boolean { - fun isClobberRisk(expr: PtExpression): Boolean { - when (expr) { - is PtArrayIndexer -> { - return params.any { - it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) - } - } - is PtBuiltinFunctionCall -> { - if (expr.name == "lsb" || expr.name == "msb") - return isClobberRisk(expr.args[0]) - if (expr.name == "mkword") - return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1]) - return !expr.isSimple() - } - else -> return !expr.isSimple() - } - } - - return args.size>1 && args.any { isClobberRisk(it) } -} diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index e1d88b83f..86b9c7215 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -69,37 +69,60 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller } - private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) { - if(sub.parameters.size==1) { - argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0]) - } else { - if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) { - registerArgsViaCpuStackEvaluation(call, sub) - } else { - asmsub6502ArgsEvalOrder(sub).forEach { - val param = sub.parameters[it] - val arg = call.args[it] - argumentViaRegister(sub, IndexedValue(it, param.second), arg) - } + + private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean { + return when(arg) { + is PtBuiltinFunctionCall -> { + if (arg.name == "lsb" || arg.name == "msb") + return usesOtherRegistersWhileEvaluating(arg.args[0]) + if (arg.name == "mkword") + return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) + return !arg.isSimple() } + is PtAddressOf -> false + is PtIdentifier -> false + is PtMachineRegister -> false + is PtMemoryByte -> false + is PtNumber -> false + else -> true } } - private fun registerArgsViaCpuStackEvaluation(call: PtFunctionCall, callee: PtAsmSub) { - // this is called when one or more of the arguments are 'complex' and - // cannot be assigned to a register easily or risk clobbering other registers. + private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) { + val cpuRegisters = setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) + val registersUsed = mutableListOf(); - if(callee.parameters.isEmpty()) - return + fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY} + fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY} + fun usedY() = registersUsed.any {it.registerOrPair==RegisterOrPair.Y || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.XY} - // use the cpu hardware stack as intermediate storage for the arguments. - val argOrder = asmsub6502ArgsEvalOrder(callee) - argOrder.reversed().forEach { - asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it]) - } - argOrder.forEach { - val param = callee.parameters[it] - asmgen.popCpuStack(callee, param.second, param.first) + if(sub.parameters.size==1) { + argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0]) + } else { + val optimalEvalOrder = asmsub6502ArgsEvalOrder(sub) + optimalEvalOrder.forEach { + val param = sub.parameters[it] + val arg = call.args[it] + registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) { + if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in cpuRegisters}) + argumentViaRegister(sub, IndexedValue(it, param.second), arg) + else if(registersUsed.any {it.statusflag!=null}) { + throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}") + } + else { + if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, false) + if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, false) + if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, false) + val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg) + if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false) + if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true) + if(usedX()) asmgen.restoreRegisterStack(CpuRegister.X, true) + used + } + } else { + argumentViaRegister(sub, IndexedValue(it, param.second), arg) + } + } } } @@ -112,7 +135,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as asmgen.assignExpressionToVariable(value, varName, parameter.type) } - private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue, value: PtExpression, registerOverride: RegisterOrPair? = null) { + private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag { // pass argument via a register parameter if(!isArgumentTypeCompatible(value.type, parameter.value.type)) throw AssemblyError("argument type incompatible") @@ -156,6 +179,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as } } } else throw AssemblyError("can only use Carry as status flag parameter") + return RegisterOrStatusflag(null, statusflag) } else { // via register or register pair @@ -188,6 +212,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as } asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope) } + return RegisterOrStatusflag(register, null) } } @@ -209,3 +234,5 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as return false } } + + diff --git a/examples/test.p8 b/examples/test.p8 index 3916c7d73..f9f02a13b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,6 +7,6 @@ main { sub start() { uword sprite_reg cx16.r0 = sprite_reg+1 - cx16.vpoke(1, sprite_reg+1, 42) ; TODO dit gebruikt onnodig pha/pla bij het assign van sprite_reg+1 ???? + cx16.vpoke(1, sprite_reg+1, 42) } }