From 39d2194d8fa034320b915217dc32cf4f8ef565cd Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 26 Jan 2024 00:12:04 +0100 Subject: [PATCH] IR: implemented inplace prefix op on split array VM: NEG instructions also set N and Z flags --- .../codegen/intermediate/AssignmentGen.kt | 98 ++++++++++++++----- examples/test.p8 | 54 ++++------ .../src/prog8/intermediate/IRInstructions.kt | 6 +- virtualmachine/src/prog8/vm/VirtualMachine.kt | 24 ++++- 4 files changed, 117 insertions(+), 65 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index da227bb99..b4147c1c7 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -36,8 +36,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express else fallbackAssign(augAssign) } else if(array!=null) { - // TODO assignArrayAugmented(array, augAssign) - fallbackAssign(augAssign) + assignArrayAugmented(array, augAssign) } else { fallbackAssign(augAssign) } @@ -157,27 +156,84 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } private fun inplacePrefix(operator: String, array: PtArrayIndexer, eltSize: Int): Result { - if(array.splitWords) - TODO("inplace prefix for split word array") - val result = mutableListOf() val vmDt = irType(array.type) val constIndex = array.index.asConstInteger() + + fun loadIndex(): Int { + val tr = expressionEval.translateExpression(array.index) + addToResult(result, tr, tr.resultReg, -1) + if(!array.splitWords && eltSize>1) + result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) + return tr.resultReg + } + + if(array.splitWords) { + // handle split LSB/MSB arrays + when(operator) { + "+" -> { } + "-" -> { + val skipCarryLabel = codeGen.createLabelName() + if(constIndex!=null) { + addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null) + addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null) + addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null) + addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel) + } else { + val indexReg = loadIndex() + val registerLsb = codeGen.registers.nextFree() + val registerMsb = codeGen.registers.nextFree() + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") + it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") + it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb) + it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0) + it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel) + it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb) + } + result += IRCodeChunk(skipCarryLabel, null).also { + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") + } + } + } + "~" -> { + if(constIndex!=null) { + addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null) + addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null) + } else { + val indexReg = loadIndex() + val register = codeGen.registers.nextFree() + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") + it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") + it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") + } + } + } + else -> throw AssemblyError("weird prefix operator") + } + return Ok(result) + } + + // normal array. + when(operator) { "+" -> { } "-" -> { if(constIndex!=null) { addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) } else { + val indexReg = loadIndex() val register = codeGen.registers.nextFree() - val tr = expressionEval.translateExpression(array.index) - addToResult(result, tr, tr.resultReg, -1) - if(eltSize>1) - result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.NEG, vmDt, reg1 = register) - it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) } } } @@ -185,35 +241,29 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(constIndex!=null) { addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) } else { + val indexReg = loadIndex() val register = codeGen.registers.nextFree() - val tr = expressionEval.translateExpression(array.index) - addToResult(result, tr, tr.resultReg, -1) - if(eltSize>1) - result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.INV, vmDt, reg1 = register) - it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) } } } "not" -> { + // TODO: in boolean branch, is 'not' handled ok like this? val register = codeGen.registers.nextFree() if(constIndex!=null) { - // TODO: in boolean branch, is 'not' handled ok like this? result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1) it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) } } else { - val tr = expressionEval.translateExpression(array.index) - addToResult(result, tr, tr.resultReg, -1) - if(eltSize>1) - result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) + val indexReg = loadIndex() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1) - it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) } } } diff --git a/examples/test.p8 b/examples/test.p8 index 5a3b1139f..6b7681adf 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,43 +5,27 @@ main { sub start() { - ubyte @shared index = 1 + word[3] @split @shared array = [1111,$10ff,3333] -; @(2000) = 99 -; uword @shared ptr = 2000 -; txt.print_ub(@(2000)) -; txt.nl() -; @(2000) ++ -; @(2000) ++ -; @(2000) -- -; txt.print_ub(@(2000)) -; txt.nl() - - - uword[3] @split arr - - arr[1] = 9999 - txt.print_uw(arr[1]) + txt.print_w(array[1]) + txt.nl() + txt.print_w(-array[1]) + txt.nl() + array[1] = -array[1] + txt.print_w(array[1]) txt.nl() - arr[1] = arr[1]*5 - cx16.r0=2222 - arr[1] *= cx16.r0 - arr[1] -=5 - arr[1] -=index - txt.print_uw(arr[1]) txt.nl() -; arr[index] = 9999 -; txt.print_uw(arr[index]) -; txt.nl() -; arr[index] += 5 -; arr[index] += 5 -; arr[index] -= 5 -; txt.print_uw(arr[index]) -; txt.nl() + ubyte @shared idx = 1 + txt.print_w(array[idx]) + txt.nl() + txt.print_w(-array[idx]) + txt.nl() + array[idx] = -array[idx] + txt.print_w(array[idx]) + txt.nl() -; ; ubyte @shared xx ; uword[3] ubarr ; bool[3] barr @@ -52,20 +36,20 @@ main { ; ubarr[1] = ubarr[1] <= 2 ; ubarr[1] = ubarr[1] > 3 ; ubarr[1] = ubarr[1] >= 3 - +; ; barr[1] = barr[0] and barr[2] ; barr[1] = barr[0] or barr[2] ; barr[1] = barr[0] xor barr[2] ; barr[1] = not barr[0] - +; ; ubarr[1] = 999 ; ubarr[1] = ubarr[1]==999 ; txt.print_uw(ubarr[1]) - +; ; barr[1] = barr[1] and bb ; barr[1] = barr[1] or bb ; barr[1] = barr[1] xor bb - +; ; bb = bb and barr[1] ; bb = bb or barr[1] ; bb = bb xor barr[1] diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 2665af183..a9b44eebf 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -16,7 +16,7 @@ Program to execute is not stored in the system memory, it's just a separate list Value stack, max 128 entries of 1 byte each. Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC, LOAD instructions DO affect the Z and N flags. - INC/DEC instructions DO affect the Z and N flags, + INC/DEC/NEG instructions DO affect the Z and N flags, other instructions only affect Z an N flags if the value in a result register is written. See OpcodesThatSetStatusbits @@ -444,6 +444,8 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf( Opcode.LOADX, Opcode.LOADIX, Opcode.LOADR, + Opcode.NEG, + Opcode.NEGM, Opcode.INC, Opcode.INCM, Opcode.DEC, @@ -456,7 +458,7 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf( Opcode.OR, Opcode.XORM, Opcode.XORR, - Opcode.XOR + Opcode.XOR, ) val OpcodesThatDependOnCarry = arrayOf( diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 6d88e301e..cf979bb44 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -974,8 +974,16 @@ class VirtualMachine(irProgram: IRProgram) { private fun InsNEG(i: IRInstruction) { when(i.type!!) { - IRDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1!!).toInt()).toUByte()) - IRDataType.WORD -> registers.setUW(i.reg1!!, (-registers.getUW(i.reg1!!).toInt()).toUShort()) + IRDataType.BYTE -> { + val value = -registers.getUB(i.reg1!!).toInt() + registers.setUB(i.reg1!!, value.toUByte()) + statusbitsNZ(value, IRDataType.BYTE) + } + IRDataType.WORD -> { + val value = -registers.getUW(i.reg1!!).toInt() + registers.setUW(i.reg1!!, value.toUShort()) + statusbitsNZ(value, IRDataType.WORD) + } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, -registers.getFloat(i.fpReg1!!)) } nextPc() @@ -984,8 +992,16 @@ class VirtualMachine(irProgram: IRProgram) { private fun InsNEGM(i: IRInstruction) { val address = i.address!! when(i.type!!) { - IRDataType.BYTE -> memory.setUB(address, (-memory.getUB(address).toInt()).toUByte()) - IRDataType.WORD -> memory.setUW(address, (-memory.getUW(address).toInt()).toUShort()) + IRDataType.BYTE -> { + val value = -memory.getUB(address).toInt() + memory.setUB(address, value.toUByte()) + statusbitsNZ(value, IRDataType.BYTE) + } + IRDataType.WORD -> { + val value = -memory.getUW(address).toInt() + memory.setUW(address, value.toUShort()) + statusbitsNZ(value, IRDataType.WORD) + } IRDataType.FLOAT -> memory.setFloat(address, -memory.getFloat(address)) } nextPc()