fix IR if expression sometimes lacking a cmpi after calculation of the condition value

VM/IR: add a returni immediate value return instruction to replace certain returnr's
This commit is contained in:
Irmen de Jong 2024-10-31 22:19:04 +01:00
parent 09cbdf410a
commit 4a47e15b1c
7 changed files with 124 additions and 36 deletions

View File

@ -1395,12 +1395,14 @@ class IRCodeGen(
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> { private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) { fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_")) if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND") throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
val tr = expressionEval.translateExpression(condition) val tr = expressionEval.translateExpression(condition)
if(addCmpiZero)
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
result += tr.chunks result += tr.chunks
if(ifElse.hasElse()) { if(ifElse.hasElse()) {
val elseLabel = createLabelName() val elseLabel = createLabelName()
@ -1420,7 +1422,7 @@ class IRCodeGen(
fun translateBinExpr(condition: PtBinaryExpression) { fun translateBinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) if(condition.operator in LogicalOperators)
return translateSimple(condition, Opcode.BSTEQ) return translateSimple(condition, Opcode.BSTEQ, false)
val signed = condition.left.type in SignedDatatypes val signed = condition.left.type in SignedDatatypes
val elseBranchFirstReg: Int val elseBranchFirstReg: Int
@ -1559,18 +1561,21 @@ class IRCodeGen(
when(val cond=ifElse.condition) { when(val cond=ifElse.condition) {
is PtBool -> { is PtBool -> {
// normally this will be optimized away, but not with -noopt // normally this will be optimized away, but not with -noopt
translateSimple(cond, Opcode.BSTEQ) translateSimple(cond, Opcode.BSTEQ, false)
} }
is PtTypeCast -> { is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes) require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
translateSimple(cond, Opcode.BSTEQ) translateSimple(cond, Opcode.BSTEQ, false)
} }
is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> { is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
translateSimple(cond, Opcode.BSTEQ) translateSimple(cond, Opcode.BSTEQ, false)
}
is PtBuiltinFunctionCall, is PtFunctionCall -> {
translateSimple(cond, Opcode.BSTEQ, true)
} }
is PtPrefix -> { is PtPrefix -> {
require(cond.operator=="not") require(cond.operator=="not")
translateSimple(cond.value, Opcode.BSTNE) translateSimple(cond.value, Opcode.BSTNE, false)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
translateBinExpr(cond) translateBinExpr(cond)
@ -1648,16 +1653,24 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.RETURN), null) addInstr(result, IRInstruction(Opcode.RETURN), null)
} else { } else {
if(value.type==DataType.FLOAT) { if(value.type==DataType.FLOAT) {
if(value is PtNumber) {
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
} else {
val tr = expressionEval.translateExpression(value) val tr = expressionEval.translateExpression(value)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
} }
}
else { else {
if(value.asConstInteger()!=null) {
addInstr(result, IRInstruction(Opcode.RETURNI, irType(value.type), immediate = value.asConstInteger()), null)
} else {
val tr = expressionEval.translateExpression(value) val tr = expressionEval.translateExpression(value)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type), reg1 = tr.resultReg), null) addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type), reg1 = tr.resultReg), null)
} }
} }
}
return result return result
} }

View File

@ -299,7 +299,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
// remove useless RETURN // remove useless RETURN
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) { if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) {
val previous = chunk.instructions[idx-1] val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) { if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)

View File

@ -1,9 +1,8 @@
TODO TODO
==== ====
VM/IR: fix return value passing from %ir routines? (diskio.exists routine always returns true) replace zsound example by a zsmkit example
contribute a short how-to to the zsmkit repo for building a suitable blob
make better zsmkit example
write a howto for integrating third party library code like zsmkit and vtui write a howto for integrating third party library code like zsmkit and vtui
Improve register load order in subroutine call args assignments: Improve register load order in subroutine call args assignments:

View File

@ -1,30 +1,60 @@
%import diskio
%import textio %import textio
;%import gfx_hires4 %import diskio
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
txt.print_bool(diskio.exists("doesntexist.prg")) txt.print_ub(exists_byte("test.prg"))
txt.spc()
txt.print_ub(exists_byte("doesntexist.xxx"))
txt.nl() txt.nl()
txt.print_bool(diskio.exists("test.prg")) txt.print_bool(exists_bool("test.prg"))
txt.spc()
txt.print_bool(exists_bool("doesntexist.xxx"))
txt.nl() txt.nl()
txt.print_bool(exists1("test.prg"))
txt.spc()
txt.print_bool(exists1("doesntexist.xxx"))
txt.nl()
txt.print_bool(exists2("test.prg"))
txt.spc()
txt.print_bool(exists2("doesntexist.xxx"))
txt.nl()
}
diskio.f_open_w("dump.bin") sub exists1(str filename) -> bool {
diskio.f_close_w() ; -- returns true if the given file exists on the disk, otherwise false
diskio.f_close_w() if exists_byte(filename)!=0 {
diskio.f_close_w() return true
diskio.f_close_w() }
return false
}
; gfx_hires4.graphics_mode() sub exists2(str filename) -> bool {
; gfx_hires4.circle(300, 250, 200, 3) ; -- returns true if the given file exists on the disk, otherwise false
; gfx_hires4.rect(320, 10, 20, 200, 3) if exists_bool(filename) {
; gfx_hires4.fill(310, 310, 2) return true
; }
; repeat { return false
; } }
sub exists_bool(str name) -> bool {
%ir {{
loadm.w r65535,main.exists_bool.name
syscall 52 (r65535.w): r0.b
returnr.b r0
}}
}
sub exists_byte(str name) -> ubyte {
%ir {{
loadm.w r65535,main.exists_byte.name
syscall 52 (r65535.w): r0.b
returnr.b r0
}}
} }
} }

View File

@ -82,6 +82,7 @@ syscall number (argument register list) [: resultreg.type]
Always preceded by parameter setup and preparecall instructions Always preceded by parameter setup and preparecall instructions
return - restore last saved instruction location and continue at that instruction. No return value. return - restore last saved instruction location and continue at that instruction. No return value.
returnr reg1 - like return, but also returns the value in reg1 to the caller returnr reg1 - like return, but also returns the value in reg1 to the caller
returni number - like return, but also returns the immediate value to the caller
@ -289,6 +290,7 @@ enum class Opcode {
SYSCALL, SYSCALL,
RETURN, RETURN,
RETURNR, RETURNR,
RETURNI,
BSTCC, BSTCC,
BSTCS, BSTCS,
@ -428,7 +430,8 @@ val OpcodesThatJump = arrayOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPI, Opcode.JUMPI,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNR Opcode.RETURNR,
Opcode.RETURNI
) )
val OpcodesThatBranch = arrayOf( val OpcodesThatBranch = arrayOf(
@ -436,6 +439,7 @@ val OpcodesThatBranch = arrayOf(
Opcode.JUMPI, Opcode.JUMPI,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNR, Opcode.RETURNR,
Opcode.RETURNI,
Opcode.CALLI, Opcode.CALLI,
Opcode.CALL, Opcode.CALL,
Opcode.SYSCALL, Opcode.SYSCALL,
@ -647,7 +651,8 @@ val instructionFormats = mutableMapOf(
Opcode.CALL to InstructionFormat.from("N,call"), Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.SYSCALL to InstructionFormat.from("N,syscall"), Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.RETURN to InstructionFormat.from("N"), Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"), Opcode.RETURNR to InstructionFormat.from("BW,<r1 | F,<fr1"),
Opcode.RETURNI to InstructionFormat.from("BW,<i | F,<i"),
Opcode.BSTCC to InstructionFormat.from("N,<a"), Opcode.BSTCC to InstructionFormat.from("N,<a"),
Opcode.BSTCS to InstructionFormat.from("N,<a"), Opcode.BSTCS to InstructionFormat.from("N,<a"),
Opcode.BSTEQ to InstructionFormat.from("N,<a"), Opcode.BSTEQ to InstructionFormat.from("N,<a"),

View File

@ -145,7 +145,7 @@ class IRProgram(val name: String,
// link all jump and branching instructions to their target // link all jump and branching instructions to their target
chunk.instructions.forEach { chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.JUMPI && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.labelSymbol!=null) { if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.JUMPI && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.opcode!=Opcode.RETURNI && it.labelSymbol!=null) {
if(it.labelSymbol.startsWith('$') || it.labelSymbol.first().isDigit()) { if(it.labelSymbol.startsWith('$') || it.labelSymbol.first().isDigit()) {
// it's a call to an address (romsub most likely) // it's a call to an address (romsub most likely)
requireNotNull(it.address) requireNotNull(it.address)

View File

@ -213,6 +213,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.SYSCALL -> InsSYSCALL(ins) Opcode.SYSCALL -> InsSYSCALL(ins)
Opcode.RETURN -> InsRETURN() Opcode.RETURN -> InsRETURN()
Opcode.RETURNR -> InsRETURNR(ins) Opcode.RETURNR -> InsRETURNR(ins)
Opcode.RETURNI -> InsRETURNI(ins)
Opcode.BSTCC -> InsBSTCC(ins) Opcode.BSTCC -> InsBSTCC(ins)
Opcode.BSTCS -> InsBSTCS(ins) Opcode.BSTCS -> InsBSTCS(ins)
Opcode.BSTEQ -> InsBSTEQ(ins) Opcode.BSTEQ -> InsBSTEQ(ins)
@ -656,6 +657,46 @@ class VirtualMachine(irProgram: IRProgram) {
} }
} }
private fun InsRETURNI(i: IRInstruction) {
if(callStack.isEmpty())
exit(0)
else {
val context = callStack.removeLast()
val returns = context.fcallSpec.returns
when (i.type!!) {
IRDataType.BYTE -> {
if(returns.isNotEmpty())
registers.setUB(returns.single().registerNum, i.immediate!!.toUByte())
else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL)
throw IllegalArgumentException("missing return value reg")
}
}
IRDataType.WORD -> {
if(returns.isNotEmpty())
registers.setUW(returns.single().registerNum, i.immediate!!.toUShort())
else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL)
throw IllegalArgumentException("missing return value reg")
}
}
IRDataType.FLOAT -> {
if(returns.isNotEmpty())
registers.setFloat(returns.single().registerNum, i.immediateFp!!)
else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL)
throw IllegalArgumentException("missing return value reg")
}
}
}
pcChunk = context.returnChunk
pcIndex = context.returnIndex
}
}
private fun InsRETURNR(i: IRInstruction) { private fun InsRETURNR(i: IRInstruction) {
if(callStack.isEmpty()) if(callStack.isEmpty())
exit(0) exit(0)