IR: fix romsub encoding

This commit is contained in:
Irmen de Jong 2023-05-14 16:48:48 +02:00
parent 905921a684
commit 630c8a5faa
9 changed files with 62 additions and 181 deletions

View File

@ -235,7 +235,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
class StRomSub(name: String,
val address: UInt,
val address: UInt?, // null in case of asmsub, specified in case of romsub
val parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>,
astNode: PtNode) :

View File

@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) {
is PtAsmSub -> {
if(node.address==null) {
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
} else {
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)
}
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)
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node)

View File

@ -4,7 +4,10 @@ import prog8.code.StRomSub
import prog8.code.StStaticVariable
import prog8.code.StSub
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes
import prog8.intermediate.*
internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) {
@ -361,10 +364,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, call, null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(call.fcallArgs!!.returns!!.dt==IRDataType.FLOAT)
ExpressionCodeResult(result, codeGen.irType(fcall.type), -1, call.fcallArgs!!.returns!!.registerNum)
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, codeGen.irType(fcall.type), call.fcallArgs!!.returns!!.registerNum, -1)
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>()
@ -401,30 +404,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
// create the call
val call = IRInstruction(Opcode.CALL, address = callTarget.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
val call =
if(callTarget.address==null)
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
if(fcall.void) {
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} else {
val regStr = if(callTarget.returns.isEmpty())
throw AssemblyError("expect a return value")
else if(callTarget.returns.size==1) {
val returns = callTarget.returns.single()
if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
} else {
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
callTarget.returns.first { it.register.registerOrPair!=null }.toString()
}
return if(fcall.type==DataType.FLOAT) {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.LOADCPU, IRDataType.FLOAT, fpReg1 = resultFpReg, labelSymbol = regStr), null)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, resultFpReg)
} else {
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOADCPU, returnRegSpec!!.dt, reg1 = resultReg, labelSymbol = regStr), null)
ExpressionCodeResult(result, returnRegSpec.dt, resultReg, -1)
}
}
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
else -> throw AssemblyError("invalid node type")
}

View File

@ -3,7 +3,7 @@ TODO
For next minor release
^^^^^^^^^^^^^^^^^^^^^^
- fix VM problem with param passing: void string.copy(".prg", &output_filename + string.length(output_filename))
- fix romsub encoding into IR
...

View File

@ -1,27 +1,15 @@
%import textio
%import string
%zeropage basicsafe
main {
sub start() {
txt.chrout('!')
txt.print("test")
txt.nl()
; uword seconds_uword = 1
; uword remainder = seconds_uword % $0003 ==0
; txt.print_uw(remainder)
;
; blerp()
ubyte @shared foo = derp(99)
}
sub blerp() {
%ir {{
_xxx:
loadr r2,r3
_yyy:
loadr r3,r4
return
asmsub derp(ubyte xx @Y) -> ubyte @ A {
%asm {{
rts
}}
}
}

View File

@ -1,6 +1,5 @@
package prog8.intermediate
import prog8.code.core.CpuRegister
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex
@ -39,15 +38,11 @@ loadi reg1, reg2 - load reg1 with value at memory indirect,
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
loadr reg1, reg2 - load reg1 with value in register reg2
loadcpu reg1, cpureg - load reg1 with value from cpu register (register/registerpair/statusflag)
storem reg1, address - store reg1 at memory address
storecpu reg1, cpureg - store reg1 in cpu register (register/registerpair/statusflag)
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
storezm address - store zero at memory address
storezcpu cpureg - store zero in cpu register (register/registerpair/statusflag)
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg
@ -60,6 +55,9 @@ call label(argument register list) [: resultreg.type]
- calls a subroutine with the given arguments and return value (optional).
save current instruction location+1, continue execution at instruction nr of the label.
the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
For example: call $ffd2(r5.b@A)
syscall number (argument register list) [: resultreg.type]
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
will be POPped off into the given resultregister if any.
@ -231,9 +229,7 @@ enum class Opcode {
LOADX,
LOADIX,
LOADR,
LOADCPU,
STOREM,
STORECPU,
STOREI,
STOREX,
STOREIX,
@ -418,11 +414,6 @@ val OpcodesThatBranch = setOf(
Opcode.BLES
)
val OpcodesForCpuRegisters = setOf(
Opcode.LOADCPU,
Opcode.STORECPU
)
enum class IRDataType {
BYTE,
WORD,
@ -513,9 +504,7 @@ val instructionFormats = mutableMapOf(
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.LOADCPU to InstructionFormat.from("BW,>r1"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
Opcode.STORECPU to InstructionFormat.from("BW,<r1"),
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
@ -823,33 +812,47 @@ data class IRInstruction(
}
if(this.fcallArgs!=null) {
immediate?.let { result.add(it.toHex()) }
labelSymbol?.let { result.add(it) }
immediate?.let { result.add(it.toHex()) } // syscall
labelSymbol?.let { result.add(it) } // regular subroutine call
address?.let { result.add(address.toHex()) } // romcall
result.add("(")
fcallArgs.arguments.forEach {
val location = if(it.address==null) {
if(it.name.isBlank()) "" else it.name+"="
} else "${it.address}="
if(it.reg.cpuRegister!=null)
TODO("handle cpuregister ${it.reg.cpuRegister}")
val cpuReg = if(it.reg.cpuRegister==null) "" else {
if(it.reg.cpuRegister.registerOrPair!=null)
"@"+it.reg.cpuRegister.registerOrPair.toString()
else
"@"+it.reg.cpuRegister.statusflag.toString()
}
when(it.reg.dt) {
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b,")
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w,")
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f,")
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,")
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,")
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,")
}
}
if(result.last().endsWith(',')) {
result.add(result.removeLast().trimEnd(','))
}
result.add(")")
fcallArgs.returns?.let {
val returns = fcallArgs.returns
if(returns!=null) {
result.add(":")
when(it.dt) {
IRDataType.BYTE -> result.add("r${it.registerNum}.b")
IRDataType.WORD -> result.add("r${it.registerNum}.w")
IRDataType.FLOAT -> result.add("fr${it.registerNum}.f")
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")
}
if(returns.cpuRegister!=null) {
val cpuReg =
if(returns.cpuRegister.registerOrPair!=null)
returns.cpuRegister.registerOrPair.toString()
else
returns.cpuRegister.statusflag.toString()
result.add("@"+cpuReg)
}
}
} else {

View File

@ -211,21 +211,6 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
}
if(opcode in OpcodesForCpuRegisters) {
val reg = operands.last().lowercase()
if(reg !in setOf(
"a", "x", "y",
"ax", "ay", "xy",
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10","r11",
"r12", "r13", "r14", "r15",
"pc", "pz", "pv","pn"))
throw IRParseException("invalid cpu reg: $reg")
return left(IRInstruction(opcode, type, reg1, labelSymbol = reg))
}
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol))
}

View File

@ -283,8 +283,6 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.BREAKPOINT -> InsBREAKPOINT()
Opcode.CLC -> { statusCarry = false; nextPc() }
Opcode.SEC -> { statusCarry = true; nextPc() }
Opcode.LOADCPU -> InsLOADCPU(ins)
Opcode.STORECPU -> InsSTORECPU(ins)
Opcode.FFROMUB -> InsFFROMUB(ins)
Opcode.FFROMSB -> InsFFROMSB(ins)
@ -373,81 +371,6 @@ class VirtualMachine(irProgram: IRProgram) {
throw BreakpointException(pcChunk, pcIndex)
}
private fun InsLOADCPU(i: IRInstruction) {
val reg = i.labelSymbol!!
val value: UInt
if(reg.startsWith('r')) {
val regnum = reg.substring(1).toInt()
val regAddr = cx16virtualregsBaseAddress + regnum*2
value = memory.getUW(regAddr).toUInt()
} else {
value = when(reg) {
"a" -> registers.cpuA.toUInt()
"x" -> registers.cpuX.toUInt()
"y" -> registers.cpuY.toUInt()
"ax" -> (registers.cpuA.toUInt() shl 8) or registers.cpuX.toUInt()
"ay" -> (registers.cpuA.toUInt() shl 8) or registers.cpuY.toUInt()
"xy" -> (registers.cpuX.toUInt() shl 8) or registers.cpuY.toUInt()
"pc" -> if(statusCarry) 1u else 0u
"pz" -> if(statusZero) 1u else 0u
"pn" -> if(statusNegative) 1u else 0u
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
else -> throw IllegalArgumentException("invalid cpu reg")
}
}
when(i.type!!) {
IRDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte())
IRDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort())
else -> throw java.lang.IllegalArgumentException("invalid cpu reg type")
}
nextPc()
}
private fun InsSTORECPU(i: IRInstruction) {
val value: UInt = when(i.type!!) {
IRDataType.BYTE -> registers.getUB(i.reg1!!).toUInt()
IRDataType.WORD -> registers.getUW(i.reg1!!).toUInt()
IRDataType.FLOAT -> throw IllegalArgumentException("there are no float cpu registers")
}
StoreCPU(value, i.type!!, i.labelSymbol!!)
nextPc()
}
private fun StoreCPU(value: UInt, dt: IRDataType, regStr: String) {
if(regStr.startsWith('r')) {
val regnum = regStr.substring(1).toInt()
val regAddr = cx16virtualregsBaseAddress + regnum*2
when(dt) {
IRDataType.BYTE -> memory.setUB(regAddr, value.toUByte())
IRDataType.WORD -> memory.setUW(regAddr, value.toUShort())
else -> throw IllegalArgumentException("invalid reg dt")
}
} else {
when (regStr) {
"a" -> registers.cpuA = value.toUByte()
"x" -> registers.cpuX = value.toUByte()
"y" -> registers.cpuY = value.toUByte()
"ax" -> {
registers.cpuA = (value and 255u).toUByte()
registers.cpuX = (value shr 8).toUByte()
}
"ay" -> {
registers.cpuA = (value and 255u).toUByte()
registers.cpuY = (value shr 8).toUByte()
}
"xy" -> {
registers.cpuX = (value and 255u).toUByte()
registers.cpuY = (value shr 8).toUByte()
}
"pc" -> statusCarry = value == 1u
"pz" -> statusZero = value == 1u
"pn" -> statusNegative = value == 1u
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
else -> throw IllegalArgumentException("invalid cpu reg")
}
}
}
private fun InsLOAD(i: IRInstruction) {
if(i.type==IRDataType.FLOAT)
registers.setFloat(i.fpReg1!!, i.immediateFp!!)

View File

@ -67,7 +67,7 @@ class VmProgramLoader {
programChunks.forEach {
it.instructions.forEach { ins ->
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch && ins.opcode !in OpcodesForCpuRegisters)
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
require(ins.address != null) { "instruction with labelSymbol for a var should have value set to the memory address" }
}
}
@ -152,16 +152,12 @@ class VmProgramLoader {
// placeholder is not a variable, so it must be a label of a code chunk instead
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
val opcode = chunk.instructions[line].opcode
if(target==null) {
// exception allowed: storecpu/loadcpu instructions that refer to CPU registers
if(opcode !in OpcodesForCpuRegisters)
throw IRParseException("placeholder not found in variables nor labels: $label")
}
else if(opcode in OpcodesThatBranch) {
if(target==null)
throw IRParseException("placeholder not found in variables nor labels: $label")
else if(opcode in OpcodesThatBranch)
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
} else {
else
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO
}
}
} else {
chunk.instructions[line] = chunk.instructions[line].copy(address = replacement)