ir: improve register type detection

This commit is contained in:
Irmen de Jong 2024-12-26 21:11:32 +01:00
parent 7b4a82b91a
commit 942d3ee640
7 changed files with 115 additions and 48 deletions

View File

@ -403,10 +403,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
val lsbmsbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
@ -417,10 +418,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
val lsbmsbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)

View File

@ -148,8 +148,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1)
addToResult(result, right, right.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -501,9 +502,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return exprGen.translateExpression(call.args.single())
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {

View File

@ -55,11 +55,12 @@ Future Things and Ideas
IR/VM
-----
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- addUsedRegistersCounts() doesn't always determine the datatype correctly. For instance with indirect instructions it thinks it still is a byte whereas it is a word (address)
- addUsedRegistersCounts() doesn't always determine the datatype correctly. --> GET RID OF THE Sxxx OPCODES FOR NOW?
- fix TODO("IR rol/ror on split words array")
- fix "<< in array" / ">> in array"
- implement missing operators in AssignmentGen (array shifts etc)
- fix call() return value handling
- try to get rid of LSIG opcode again (but this will introduce byte reads from word typed registers...)
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.

View File

@ -2,25 +2,31 @@
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
cx16.r0L = 0
cx16.r9L = if cx16.r0L & $80 != 0 11 else 22
cx16.r10L = if cx16.r0L & $40 == 0 11 else 22
txt.print_ub(cx16.r9L)
txt.spc()
txt.print_ub(cx16.r10L)
cx16.r2 = $eeee
txt.print_uwhex(cx16.r2, true)
txt.nl()
cx16.r0L = 255
cx16.r9L = if cx16.r0L & $80 != 0 11 else 22
cx16.r10L = if cx16.r0L & $40 == 0 11 else 22
txt.print_ub(cx16.r9L)
txt.spc()
txt.print_ub(cx16.r10L)
cx16.r2,void = thing2() ; TODO fix IR+6502 codegen missing an ext.b (it is present when thing only returns single returnvalue)
txt.print_uwhex(cx16.r2, true)
txt.nl()
cx16.r2 = thing() ; codegen does ext.b correctly here
txt.print_uwhex(cx16.r2, true)
txt.nl()
}
asmsub thing() -> ubyte @A {
%asm {{
lda #$44
rts
}}
}
asmsub thing2() -> ubyte @A, bool @Pc {
%asm {{
lda #$aa
clc
rts
}}
}
}

View File

@ -143,7 +143,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
regs.append(" r$regnum -> $types")
if (types.size > 1) {
regs.append(" !!!! more than one type !!!!\n")
println("Detected multi-type register usage: $regnum->$types in ${chunk.label} at ${chunk.sourceLinesPositions.firstOrNull()}")
println("IR: Detected multi-type register usage: $regnum->$types in ${chunk.label} at ${chunk.sourceLinesPositions.firstOrNull()}")
}
else
regs.append("\n")

View File

@ -40,7 +40,7 @@ loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
loadr reg1, reg2 - load reg1 with value in register reg2
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
loadha reg1 - load cpu hardware register A into reg1.b
loadhx reg1 - load cpu hardware register X into reg1.b
loadhy reg1 - load cpu hardware register Y into reg1.b
@ -69,7 +69,7 @@ storehfacone fpreg1 - store fpreg1.f into "cpu register" fac1
CONTROL FLOW
------------
jump location - continue running at instruction at 'location' (label/memory address)
jumpi rqg1 - continue running at memory address in reg1 (indirect jump)
jumpi reg1 - continue running at memory address in reg1 (indirect jump)
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
call label(argument register list) [: resultreg.type]
@ -252,6 +252,7 @@ sei - set interrupt disable flag
nop - do nothing
breakpoint - trigger a breakpoint
align alignmentvalue - represents a memory alignment directive
lsig [b, w] reg1, reg2 - reg1 becomes the least significant byte (or word) of the word (or int) in reg2 (.w not yet implemented; requires 32 bits regs)
msig [b, w] reg1, reg2 - reg1 becomes the most significant byte (or word) of the word (or int) in reg2 (.w not yet implemented; requires 32 bits regs)
concat [b, w] reg1, reg2, reg3 - reg1.w = 'concatenate' two registers: lsb/lsw of reg2 (as msb) and lsb/lsw of reg3 (as lsb) into word or int (int not yet implemented; requires 32bits regs)
push [b, w, f] reg1 - push value in reg1 on the stack
@ -432,6 +433,7 @@ enum class Opcode {
POP,
PUSHST,
POPST,
LSIG,
MSIG,
CONCAT,
BREAKPOINT,
@ -790,6 +792,7 @@ val instructionFormats = mutableMapOf(
Opcode.FFLOOR to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FCEIL to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.LSIG to InstructionFormat.from("BW,>r1,<r2"),
Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"),
Opcode.PUSH to InstructionFormat.from("BW,<r1 | F,<fr1"),
Opcode.POP to InstructionFormat.from("BW,>r1 | F,>fr1"),
@ -926,35 +929,32 @@ data class IRInstruction(
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
if(type!=null) {
val actualtype = determineReg1Type()
if(actualtype!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
types += actualtype
regsTypes[this.reg1] = types
}
}
OperandDirection.WRITE -> {
writeRegsCounts[this.reg1!!] = writeRegsCounts.getValue(this.reg1)+1
if(type!=null) {
val actualtype = determineReg1Type()
if(actualtype!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
types += actualtype
regsTypes[this.reg1] = types
}
}
OperandDirection.READWRITE -> {
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
if(type!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg1] = types
}
writeRegsCounts[this.reg1] = writeRegsCounts.getValue(this.reg1)+1
if(type!=null) {
val actualtype = determineReg1Type()
if(actualtype!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
types += actualtype
regsTypes[this.reg1] = types
}
}
@ -963,10 +963,11 @@ data class IRInstruction(
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
writeRegsCounts[this.reg2!!] = writeRegsCounts.getValue(this.reg2)+1
if(type!=null) {
val actualtype = determineReg2Type()
if(actualtype!=null) {
var types = regsTypes[this.reg2]
if(types==null) types = mutableSetOf()
types += type
types += actualtype
regsTypes[this.reg2] = types
}
}
@ -976,10 +977,11 @@ data class IRInstruction(
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
writeRegsCounts[this.reg3!!] = writeRegsCounts.getValue(this.reg3)+1
if(type!=null) {
val actualtype = determineReg3Type()
if(actualtype!=null) {
var types = regsTypes[this.reg3]
if(types==null) types = mutableSetOf()
types += type
types += actualtype
regsTypes[this.reg3] = types
}
}
@ -1034,6 +1036,44 @@ data class IRInstruction(
}
}
private fun determineReg1Type(): IRDataType? {
if(opcode==Opcode.JUMPI || opcode==Opcode.CALLI || opcode==Opcode.STOREZI)
return IRDataType.WORD
if(opcode==Opcode.EXT || opcode==Opcode.EXTS)
return when(type) {
IRDataType.BYTE -> IRDataType.WORD
IRDataType.WORD -> TODO("ext.w into long type")
else -> null
}
if(opcode==Opcode.CONCAT)
return when(type) {
IRDataType.BYTE -> IRDataType.WORD
IRDataType.WORD -> TODO("concat.w from long type")
else -> null
}
if(opcode==Opcode.ASRNM || opcode==Opcode.LSRNM || opcode==Opcode.LSLNM)
return IRDataType.BYTE
return this.type
}
private fun determineReg2Type(): IRDataType? {
if(opcode==Opcode.LOADI || opcode==Opcode.STOREI)
return IRDataType.WORD
if(opcode==Opcode.MSIG || opcode==Opcode.LSIG)
return when(type) {
IRDataType.BYTE -> IRDataType.WORD
IRDataType.WORD -> TODO("msig/lsig.w from long type")
else -> null
}
if(opcode==Opcode.ASRN || opcode==Opcode.LSRN || opcode==Opcode.LSLN)
return IRDataType.BYTE
return this.type
}
private fun determineReg3Type(): IRDataType? {
return this.type
}
override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase())

View File

@ -313,6 +313,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.ROLM -> InsROLM(ins, false)
Opcode.ROXL -> InsROL(ins, true)
Opcode.ROXLM -> InsROLM(ins, true)
Opcode.LSIG -> InsLSIG(ins)
Opcode.MSIG -> InsMSIG(ins)
Opcode.CONCAT -> InsCONCAT(ins)
Opcode.PUSH -> InsPUSH(ins)
@ -2290,6 +2291,18 @@ class VirtualMachine(irProgram: IRProgram) {
statusCarry = newStatusCarry
}
private fun InsLSIG(i: IRInstruction) {
when(i.type!!) {
IRDataType.BYTE -> {
val value = registers.getUW(i.reg2!!)
registers.setUB(i.reg1!!, value.toUByte())
}
IRDataType.WORD -> throw IllegalArgumentException("lsig.w not yet supported, requires 32-bits registers")
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
nextPc()
}
private fun InsMSIG(i: IRInstruction) {
when(i.type!!) {
IRDataType.BYTE -> {