refactor fuction arguments codegen a bit

This commit is contained in:
Irmen de Jong
2021-11-27 20:57:24 +01:00
parent 8b4ac7801f
commit c838821615
11 changed files with 121 additions and 57 deletions
@@ -5,11 +5,14 @@ import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.Expression
import prog8.ast.statements.Subroutine
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.cbm.Petscii
import prog8.compiler.target.cpu6502.codegen.asmsub6502ArgsEvalOrder
import prog8.compiler.target.cpu6502.codegen.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.ICompilationTarget
object C64Target: ICompilationTarget {
override val name = "c64"
override val machine = C64MachineDefinition
@@ -23,6 +26,9 @@ object C64Target: ICompilationTarget {
override fun decodeString(bytes: List<UByte>, altEncoding: Boolean) =
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> = asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>): Boolean = asmsub6502ArgsHaveRegisterClobberRisk(args)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
@@ -5,7 +5,11 @@ import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.Expression
import prog8.ast.statements.Subroutine
import prog8.compiler.target.cbm.Petscii
import prog8.compiler.target.cpu6502.codegen.asmsub6502ArgsEvalOrder
import prog8.compiler.target.cpu6502.codegen.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compiler.target.cx16.CX16MachineDefinition
import prog8.compilerinterface.ICompilationTarget
@@ -23,6 +27,9 @@ object Cx16Target: ICompilationTarget {
override fun decodeString(bytes: List<UByte>, altEncoding: Boolean) =
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> = asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>): Boolean = asmsub6502ArgsHaveRegisterClobberRisk(args)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
@@ -104,6 +104,9 @@ class AsmGen(private val program: Program,
internal fun isTargetCpu(cpu: CpuType) = compTarget.machine.cpu == cpu
internal fun haveFPWR() = compTarget is Cx16Target
internal fun asmsubArgsEvalOrder(sub: Subroutine) = compTarget.asmsubArgsEvalOrder(sub)
internal fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>) = compTarget.asmsubArgsHaveRegisterClobberRisk(args)
private fun header() {
val ourName = this.javaClass.name
val cpu = when(compTarget.machine.cpu) {
@@ -0,0 +1,37 @@
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.base.Cx16VirtualRegisters
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
internal fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
val order = mutableListOf<Int>()
// order is: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
val (regs, rest) = args2.partition { it.value.second.registerOrPair != null }
cx16regs.forEach { order += it.index }
regs.forEach { order += it.index }
rest.forEach { order += it.index }
require(order.size==sub.parameters.size)
return order
}
internal fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>): Boolean {
fun isClobberRisk(expr: Expression): Boolean {
if (expr.isSimple && expr !is PrefixExpression)
return false
if (expr is FunctionCall) {
if (expr.target.nameInSource == listOf("lsb") || expr.target.nameInSource == listOf("msb"))
return isClobberRisk(expr.args[0])
if (expr.target.nameInSource == listOf("mkword"))
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
}
return true
}
return args.size>1 && args.any { isClobberRisk(it) }
}
@@ -111,45 +111,16 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) {
fun isNoClobberRisk(expr: Expression): Boolean {
if(expr is AddressOf ||
expr is NumericLiteralValue ||
expr is StringLiteralValue ||
expr is ArrayLiteralValue ||
expr is IdentifierReference)
return true
if(expr is FunctionCall) {
if(expr.target.nameInSource==listOf("lsb") || expr.target.nameInSource==listOf("msb"))
return isNoClobberRisk(expr.args[0])
if(expr.target.nameInSource==listOf("mkword"))
return isNoClobberRisk(expr.args[0]) && isNoClobberRisk(expr.args[1])
}
return false
}
if(sub.parameters.size==1) {
// just a single parameter, no risk of clobbering registers
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
} else {
when {
call.args.all {isNoClobberRisk(it)} -> {
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
val argsInfo = sub.parameters.withIndex().zip(call.args).zip(sub.asmParameterRegisters)
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
for(arg in cx16virtualRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second)
for(arg in cpuRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second)
for(arg in statusRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second)
}
else -> {
// Risk of clobbering due to complex expression args. Evaluate first, then assign registers.
registerArgsViaStackEvaluation(call, sub)
if(asmgen.asmsubArgsHaveRegisterClobberRisk(call.args)) {
registerArgsViaStackEvaluation(call, sub)
} else {
asmgen.asmsubArgsEvalOrder(sub).forEach {
val param = sub.parameters[it]
val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param), arg)
}
}
}