some cleanups about asmsub return registers and types

This commit is contained in:
Irmen de Jong 2023-02-09 03:13:52 +01:00
parent 6aabbffc62
commit 694d088160
14 changed files with 49 additions and 52 deletions

View File

@ -215,7 +215,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
class StRomSub(name: String,
val address: UInt,
val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>,
val returns: List<StRomSubParameter>,
astNode: PtNode,
position: Position) :
StNode(name, StNodeType.ROMSUB, position, astNode)

View File

@ -40,11 +40,12 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
val stNode = when(node) {
is PtAsmSub -> {
if(node.address==null) {
val params = node.parameters.map { StSubroutineParameter(it.first.name, it.first.type) }
StSub(node.name, params, node.returnTypes.singleOrNull(), node, node.position)
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
StSub(node.name, params, node.returns.singleOrNull()?.second, node, node.position)
} else {
val parameters = node.parameters.map { StRomSubParameter(it.second, it.first.type) }
StRomSub(node.name, node.address, parameters, node.retvalRegisters, node, node.position)
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node, node.position)
}
}
is PtBlock -> {

View File

@ -57,7 +57,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
is PtAsmSub -> {
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
val returns = if (node.returnTypes.isEmpty()) "" else (if (node.returnTypes.size == 1) "-> ${node.returnTypes[0].name.lowercase()}" else "-> ${node.returnTypes.map { it.name.lowercase() }}")
val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
val str = if (node.inline) "inline " else ""
if(node.address==null) {
str + "asmsub ${node.name}($params) $clobbers $returns"

View File

@ -11,9 +11,8 @@ class PtAsmSub(
name: String,
val address: UInt?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
val returnTypes: List<DataType>, // TODO join with registers below, as Pairs ?
val retvalRegisters: List<RegisterOrStatusflag>,
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position), IPtSubroutine {

View File

@ -2886,7 +2886,7 @@ $repeatLabel lda $counterVar
}
internal fun popCpuStack(asmsub: PtAsmSub, parameter: PtSubroutineParameter, reg: RegisterOrStatusflag) {
val shouldKeepA = asmsub.parameters.any { it.second.registerOrPair==RegisterOrPair.AX || it.second.registerOrPair==RegisterOrPair.AY}
val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY}
if(reg.statusflag!=null) {
if(shouldKeepA)
out(" sta P8ZP_SCRATCH_REG")
@ -2961,8 +2961,8 @@ $repeatLabel lda $counterVar
// note: because A is pushed first so popped last, saving A is often not required here.
val targetAsmSub = (target as PtNode).definingAsmSub()
if(targetAsmSub != null) {
val parameter = targetAsmSub.parameters.first { it.first.name==target.name }
popCpuStack(targetAsmSub, parameter.first, parameter.second)
val parameter = targetAsmSub.parameters.first { it.second.name==target.name }
popCpuStack(targetAsmSub, parameter.second, parameter.first)
return
}
val scopedName = when(target) {

View File

@ -15,19 +15,19 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
// 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.second.registerOrPair in Cx16VirtualRegisters }
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.second.registerOrPair in pairedRegisters }
val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A }
val (regA, rest) = args4.partition { it.value.second.registerOrPair != null }
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 }
cx16regs.forEach { order += it.index }
pairedRegs.forEach { order += it.index }
regsWithoutA.forEach {
if(it.value.second.registerOrPair != RegisterOrPair.X)
if(it.value.first.registerOrPair != RegisterOrPair.X)
order += it.index
}
regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
rest.forEach { order += it.index }
regA.forEach { order += it.index }
require(order.size==sub.parameters.size)
@ -36,13 +36,13 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
fun asmsub6502ArgsHaveRegisterClobberRisk(
args: List<PtExpression>,
params: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>
params: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>
): Boolean {
fun isClobberRisk(expr: PtExpression): Boolean {
when (expr) {
is PtArrayIndexer -> {
return params.any {
it.second.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
}
}
is PtBuiltinFunctionCall -> {

View File

@ -54,8 +54,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
asmgen.restoreXafterCall(call)
val returns: List<Pair<DataType, RegisterOrStatusflag>> = sub.returnsWhatWhere()
for ((_, reg) in returns) {
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
for ((reg, _) in returns) {
// 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) {
when (reg.registerOrPair!!) {

View File

@ -59,13 +59,13 @@ fun PtExpression.isSimple(): Boolean {
}
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.retvalRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal fun IPtSubroutine.shouldSaveX(): Boolean =
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
internal fun PtAsmSub.regXasParam(): Boolean =
parameters.any { it.second.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
@ -74,14 +74,14 @@ internal fun PtAsmSub.shouldKeepA(): KeepAresult {
// 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 = retvalRegisters.any { it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.AX }
val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX }
return KeepAresult(false, saveAonReturn)
}
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<DataType, RegisterOrStatusflag>> {
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
when(this) {
is PtAsmSub -> {
return returnTypes.zip(this.retvalRegisters)
return returns
}
is PtSub -> {
// for non-asm subroutines, determine the return registers based on the type of the return value
@ -94,7 +94,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<DataType, RegisterOrSta
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
listOf(Pair(returntype!!, register))
listOf(Pair(register, returntype!!))
}
}
}

View File

@ -106,7 +106,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().first), call.args[0])
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
} else {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
registerArgsViaCpuStackEvaluation(call, sub)
@ -114,7 +114,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it]
val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param.first), arg)
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
}
}
}
@ -130,11 +130,11 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].first.type, call.args[it])
asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it])
}
argOrder.forEach {
val param = callee.parameters[it]
asmgen.popCpuStack(callee, param.first, param.second)
asmgen.popCpuStack(callee, param.second, param.first)
}
}
@ -153,7 +153,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
throw AssemblyError("argument type incompatible")
val paramRegister: RegisterOrStatusflag = when(sub) {
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].second else RegisterOrStatusflag(registerOverride, null)
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
is PtSub -> RegisterOrStatusflag(registerOverride!!, null)
}
val statusflag = paramRegister.statusflag

View File

@ -60,7 +60,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
if (parameter!=null) {
val sub = parameter.definingAsmSub()
if (sub!=null) {
val reg = sub.parameters.single { it.first===parameter }.second
val reg = sub.parameters.single { it.second===parameter }.first
if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly")
else
@ -160,7 +160,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)

View File

@ -181,8 +181,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value)
val returnValue = sub.returnsWhatWhere().singleOrNull() { it.second.registerOrPair!=null } ?: sub.returnsWhatWhere().single() { it.second.statusflag!=null }
when (returnValue.first) {
val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
when (returnValue.second) {
DataType.STR -> {
asmgen.restoreXafterCall(value)
when(assign.target.datatype) {
@ -203,7 +203,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
else -> {
// do NOT restore X register before assigning the result values first
when (returnValue.second.registerOrPair) {
when (returnValue.first.registerOrPair) {
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
@ -227,7 +227,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14)
RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15)
else -> {
val sflag = returnValue.second.statusflag
val sflag = returnValue.first.statusflag
if(sflag!=null)
assignStatusFlagByte(assign.target, sflag)
else

View File

@ -988,20 +988,20 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(fcall.type==DataType.FLOAT)
throw AssemblyError("doesn't support float register result in asm romsub")
val returns = callTarget.returns.single()
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
}
else -> {
val returnRegister = callTarget.returns.singleOrNull{ it.registerOrPair!=null }
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
if(returnRegister!=null) {
// we skip the other values returned in the status flags.
val regStr = returnRegister.registerOrPair.toString()
val regStr = returnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
} else {
val firstReturnRegister = callTarget.returns.firstOrNull{ it.registerOrPair!=null }
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
if(firstReturnRegister!=null) {
// we just take the first register return value and ignore the rest.
val regStr = firstReturnRegister.registerOrPair.toString()
val regStr = firstReturnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
} else {
throw AssemblyError("invalid number of return values from call")

View File

@ -1203,8 +1203,8 @@ class IRCodeGen(
child.name,
child.address,
child.clobbers,
child.parameters.map { IRAsmSubroutine.IRAsmParam(it.second, it.first.type) }, // note: the name of the asmsub param is not used anymore.
child.returnTypes.zip(child.retvalRegisters).map { IRAsmSubroutine.IRAsmParam(it.second, it.first) },
child.parameters.map { IRAsmSubroutine.IRAsmParam(it.first, it.second.type) }, // note: the name of the asmsub param is not used here anymore
child.returns.map { IRAsmSubroutine.IRAsmParam(it.first, it.second)},
asmChunk,
child.position
)

View File

@ -285,18 +285,15 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
}
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
val params = srcSub.parameters
.map { PtSubroutineParameter(it.name, it.type, it.position) }
.zip(srcSub.asmParameterRegisters)
val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) })
val sub = PtAsmSub(srcSub.name,
srcSub.asmAddress,
srcSub.asmClobbers,
params,
srcSub.returntypes,
srcSub.asmReturnvaluesRegisters,
srcSub.asmReturnvaluesRegisters.zip(srcSub.returntypes),
srcSub.inline,
srcSub.position)
sub.parameters.forEach { it.first.parent=sub }
sub.parameters.forEach { it.second.parent=sub }
if(srcSub.asmAddress==null) {
var combinedTrueAsm = ""