mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 04:31:20 +00:00
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:
parent
09cbdf410a
commit
4a47e15b1c
@ -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,14 +1653,22 @@ 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
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"),
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user