IR: fix codegen for routines returning in CPU Status register flag

This commit is contained in:
Irmen de Jong 2023-12-14 21:16:14 +01:00
parent 332ba8ed7e
commit b24df31c2b
5 changed files with 97 additions and 67 deletions

View File

@ -430,6 +430,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += tr.chunks
}
// return value
var statusFlagResult: Statusflag? = null
val returnRegSpec = if(fcall.void) null else {
if(callTarget.returns.isEmpty())
null
@ -438,8 +439,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val returnIrType = irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
else {
statusFlagResult = returns.register.statusflag
val returnRegister = if(statusFlagResult==null) codeGen.registers.nextFree() else -1
FunctionCallArgs.RegSpec(returnIrType, returnRegister, returns.register)
}
} else {
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
val returns = callTarget.returns.first { it.register.registerOrPair!=null }
@ -457,12 +461,49 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
var finalReturnRegister = returnRegSpec?.registerNum ?: -1
if(fcall.parent is PtAssignment) {
// look if the status flag bit should actually be returned as a 0/1 byte value in a result register (so it can be assigned)
if(statusFlagResult!=null && returnRegSpec!=null) {
// assign status flag bit to the return value register
finalReturnRegister = codeGen.registers.nextFree()
when(statusFlagResult) {
Statusflag.Pc -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, returnRegSpec.dt, reg1=finalReturnRegister, immediate = 0)
it += IRInstruction(Opcode.ROXL, returnRegSpec.dt, reg1=finalReturnRegister)
}
}
else -> {
val branchOpcode = when(statusFlagResult) {
Statusflag.Pc -> Opcode.BSTCS
Statusflag.Pz -> Opcode.BSTEQ
Statusflag.Pv -> Opcode.BSTVS
Statusflag.Pn -> Opcode.BSTNEG
}
val setLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(branchOpcode, labelSymbol = setLabel)
it += IRInstruction(Opcode.LOAD, returnRegSpec.dt, reg1=finalReturnRegister, immediate = 0)
it += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
}
result += IRCodeChunk(setLabel, null).also {
it += IRInstruction(Opcode.LOAD, returnRegSpec.dt, reg1=finalReturnRegister, immediate = 1)
}
result += IRCodeChunk(endLabel, null)
}
}
}
}
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, finalReturnRegister)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
}
else -> throw AssemblyError("invalid node type")
}

View File

@ -2,8 +2,6 @@
TODO
====
- IR (expericodegen): fix code for calling routines that return a boolean in a status register such as Carry flag, it has to store the flag value somewhere
- merge branch optimize-st for some optimizations regarding SymbolTable use
- [on branch: call-pointers] allow calling a subroutine via a pointer variable (indirect JSR, optimized form of callfar())
@ -18,6 +16,8 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- What happens when subs return a boolean not in A, but in Carry flag?
- What happens when we keep the BOOL type around until in codegen? (so, get rid of Boolean->ubyte and boolean remover)
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays.
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
(this is already done hardcoded for several of the builtin functions)

View File

@ -4,54 +4,33 @@
main {
sub start() {
if test_c_set()
txt.print("yes1\n")
else
goto skip1
bool @shared statusc = test_carry_set()
bool @shared statusv = test_v_set()
bool @shared statusz = test_z_set()
bool @shared statusn = test_n_set()
txt.print("no1\n")
skip1:
if test_c_clear()
txt.print("yes2\n")
else
goto skip2
txt.print("no1\n")
skip2:
txt.print("done\n")
if test_carry_set() {
txt.print("set!\n")
}
if test_v_set() {
txt.print("set!\n")
}
if test_z_set() {
txt.print("set!\n")
}
if test_n_set() {
txt.print("set!\n")
}
}
asmsub test_c_clear() -> bool @Pc {
asmsub test_carry_set() -> bool @Pc {
%asm {{
clc
sec
rts
}}
}
asmsub test_z_clear() -> bool @Pz {
%asm {{
lda #1
rts
}}
}
asmsub test_n_clear() -> bool @Pn {
%asm {{
lda #1
rts
}}
}
asmsub test_v_clear() -> bool @Pv {
%asm {{
clv
rts
}}
}
asmsub test_c_set() -> bool @Pc {
asmsub test_v_set() -> bool @Pv {
%asm {{
sec
rts
@ -60,22 +39,15 @@ skip2:
asmsub test_z_set() -> bool @Pz {
%asm {{
lda #0
sec
rts
}}
}
asmsub test_n_set() -> bool @Pn {
%asm {{
lda #-1
sec
rts
}}
}
asmsub test_v_set() -> bool @Pv {
%asm {{
bit +
+ rts
}}
}
}

View File

@ -928,18 +928,27 @@ data class IRInstruction(
val returns = fcallArgs.returns
if(returns!=null) {
result.add(":")
when (returns.dt) {
IRDataType.BYTE -> result.add("r${returns.registerNum}.b")
IRDataType.WORD -> result.add("r${returns.registerNum}.w")
IRDataType.FLOAT -> result.add("fr${returns.registerNum}.f")
val cpuReg = if(returns.cpuRegister==null) "" else {
if(returns.cpuRegister.registerOrPair!=null)
returns.cpuRegister.registerOrPair.toString()
else
returns.cpuRegister.statusflag.toString()
}
if(returns.cpuRegister!=null) {
val cpuReg =
if(returns.cpuRegister.registerOrPair!=null)
returns.cpuRegister.registerOrPair.toString()
else
returns.cpuRegister.statusflag.toString()
result.add("@"+cpuReg)
if(cpuReg.isEmpty()) {
when (returns.dt) {
IRDataType.BYTE -> result.add("r${returns.registerNum}.b")
IRDataType.WORD -> result.add("r${returns.registerNum}.w")
IRDataType.FLOAT -> result.add("fr${returns.registerNum}.f")
}
} else {
result.add("@" + cpuReg)
if(returns.cpuRegister?.statusflag==null) {
when (returns.dt) {
IRDataType.BYTE -> result.add(".b")
IRDataType.WORD -> result.add(".w")
IRDataType.FLOAT -> result.add(".f")
}
}
}
}
} else {

View File

@ -254,6 +254,14 @@ private fun parseCall(rest: String): ParsedCall {
return FunctionCallArgs.RegSpec(type, num, cpuRegister)
}
fun parseReturnRegspec(reg: String): FunctionCallArgs.RegSpec {
return if(reg.startsWith('@')) {
FunctionCallArgs.RegSpec(IRDataType.BYTE, -1, parseRegisterOrStatusflag(reg.drop(1)))
} else {
parseRegspec(reg)
}
}
fun parseArgs(args: String): List<FunctionCallArgs.ArgumentSpec> {
if(args.isBlank())
return emptyList()
@ -285,7 +293,7 @@ private fun parseCall(rest: String): ParsedCall {
actualTarget,
address,
arguments,
if(returns==null) null else parseRegspec(returns)
if(returns==null) null else parseReturnRegspec(returns)
)
}