From c9e8c7a290f461b27cd17c29bf17292c4aeb9aaf Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 19 May 2022 23:38:16 +0200 Subject: [PATCH] vm: add in-place division --- .../prog8/codegen/virtual/AssignmentGen.kt | 2 +- .../src/prog8/codegen/virtual/CodeGen.kt | 62 ++++++++++++ .../prog8/codegen/virtual/ExpressionGen.kt | 31 ++++++ docs/source/todo.rst | 1 - virtualmachine/src/prog8/vm/VirtualMachine.kt | 97 ++++++++++++++++++- 5 files changed, 189 insertions(+), 4 deletions(-) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt index bb52c78bd..895fcd0c3 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt @@ -86,7 +86,7 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio "+" -> return expressionEval.operatorPlusInplace(address, vmDt, operand) "-" -> return expressionEval.operatorMinusInplace(address, vmDt, operand) "*" -> return expressionEval.operatorMultiplyInplace(address, vmDt, operand) - "/" -> { /* TODO */ } + "/" -> return expressionEval.operatorDivideInplace(address, vmDt, signed, operand) "|" -> { /* TODO */ } "&" -> { /* TODO */ } "^" -> { /* TODO */ } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index adc70f896..a1da9ccd7 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -487,6 +487,22 @@ class CodeGen(internal val program: PtProgram, return code } + internal fun divideByConstFloatInplace(address: Int, factor: Float): VmCodeChunk { + val code = VmCodeChunk() + if(factor==1f) + return code + if(factor==0f) { + val maxvalueReg = vmRegisters.nextFreeFloat() + code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE) + code += VmCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=address) + } else { + val factorReg = vmRegisters.nextFreeFloat() + code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor) + code += VmCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=address) + } + return code + } + internal fun divideByConst(dt: VmDataType, reg: Int, factor: Int, signed: Boolean): VmCodeChunk { val code = VmCodeChunk() if(factor==1) @@ -523,6 +539,52 @@ class CodeGen(internal val program: PtProgram, return code } + internal fun divideByConstInplace(dt: VmDataType, address: Int, factor: Int, signed: Boolean): VmCodeChunk { + val code = VmCodeChunk() + if(factor==1) + return code + val pow2 = powersOfTwo.indexOf(factor) + if(pow2==1) { + // just shift 1 bit + // TODO use memory-shift instruction? + val reg = vmRegisters.nextFree() + code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address) + code += if(signed) + VmCodeInstruction(Opcode.ASR, dt, reg1=reg) + else + VmCodeInstruction(Opcode.LSR, dt, reg1=reg) + code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address) + } + else if(pow2>=1) { + // just shift multiple bits + // TODO use memory-shift instruction? + val reg = vmRegisters.nextFree() + val pow2reg = vmRegisters.nextFree() + code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2) + code += if(signed) + VmCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg) + else + VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg) + code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address) + } else { + if (factor == 0) { + val reg = vmRegisters.nextFree() + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff) + code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address) + } + else { + val factorReg = vmRegisters.nextFree() + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor) + code += if(signed) + VmCodeInstruction(Opcode.DIVSM, dt, reg1=factorReg, value=address) + else + VmCodeInstruction(Opcode.DIVM, dt, reg1=factorReg, value=address) + } + } + return code + } + private fun translate(ifElse: PtIfElse): VmCodeChunk { if(ifElse.condition.operator !in ComparisonOperators) throw AssemblyError("if condition should only be a binary comparison expression") diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index 882eeabe4..5101bde2c 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -545,6 +545,37 @@ internal class ExpressionGen(private val codeGen: CodeGen) { return code } + internal fun operatorDivideInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk { + val code = VmCodeChunk() + val constFactorRight = operand as? PtNumber + if(vmDt==VmDataType.FLOAT) { + if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) { + val factor = constFactorRight.number.toFloat() + code += codeGen.divideByConstFloatInplace(address, factor) + } else { + val operandFpReg = codeGen.vmRegisters.nextFreeFloat() + code += translateExpression(operand, -1, operandFpReg) + code += if(signed) + VmCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value=address) + else + VmCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value=address) + } + } else { + if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) { + val factor = constFactorRight.number.toInt() + code += codeGen.divideByConstInplace(vmDt, address, factor, signed) + } else { + val operandReg = codeGen.vmRegisters.nextFree() + code += translateExpression(operand, operandReg, -1) + code += if(signed) + VmCodeInstruction(Opcode.DIVSM, vmDt, reg1=operandReg, value = address) + else + VmCodeInstruction(Opcode.DIVM, vmDt, reg1=operandReg, value = address) + } + } + return code + } + private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk { val code = VmCodeChunk() val constFactorLeft = binExpr.left as? PtNumber diff --git a/docs/source/todo.rst b/docs/source/todo.rst index cc90ffba3..bdc32d712 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,7 +4,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ - vm: implement the missing in-place operators in inplaceBinexpr() -- vm: implement the new in-memory instructions in the VirtualMachine eval loop itself. - complete the Inliner - add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value? diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 309535b26..4521a877c 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -144,9 +144,9 @@ class VirtualMachine(val memory: Memory, program: List) { Opcode.MUL -> InsMUL(ins) Opcode.MULM -> InsMULM(ins) Opcode.DIV -> InsDIV(ins) - Opcode.DIVM -> TODO() + Opcode.DIVM -> InsDIVM(ins) Opcode.DIVS -> InsDIVS(ins) - Opcode.DIVSM -> TODO() + Opcode.DIVSM -> InsDIVSM(ins) Opcode.MOD -> InsMOD(ins) Opcode.SGN -> InsSGN(ins) Opcode.CMP -> InsCMP(ins) @@ -759,6 +759,16 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } + private fun InsDIVM(i: Instruction) { + val address = i.value!! + when(i.type!!) { + VmDataType.BYTE -> divModByteUnsignedInplace("/", i.reg1!!, address) + VmDataType.WORD -> divModWordUnsignedInplace("/", i.reg1!!, address) + VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") + } + pc++ + } + private fun InsDIVS(i: Instruction) { when(i.type!!) { VmDataType.BYTE -> divModByteSigned("/", i.reg1!!, i.reg2!!) @@ -773,6 +783,21 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } + private fun InsDIVSM(i: Instruction) { + val address = i.value!! + when(i.type!!) { + VmDataType.BYTE -> divModByteSignedInplace("/", i.reg1!!, address) + VmDataType.WORD -> divModWordSignedInplace("/", i.reg1!!, address) + VmDataType.FLOAT -> { + val left = memory.getFloat(address) + val right = registers.getFloat(i.fpReg1!!) + val result = arithFloat(left, "/", right) + memory.setFloat(address, result) + } + } + pc++ + } + private fun InsMOD(i: Instruction) { when(i.type!!) { VmDataType.BYTE -> divModByteUnsigned("%", i.reg1!!, i.reg2!!) @@ -880,6 +905,23 @@ class VirtualMachine(val memory: Memory, program: List) { registers.setSB(reg1, result.toByte()) } + private fun divModByteSignedInplace(operator: String, reg1: Int, address: Int) { + val left = memory.getSB(address) + val right = registers.getSB(reg1) + val result = when(operator) { + "/" -> { + if(right==0.toByte()) 127 + else left / right + } + "%" -> { + if(right==0.toByte()) 127 + else left % right + } + else -> throw IllegalArgumentException("operator byte $operator") + } + memory.setSB(address, result.toByte()) + } + private fun divModByteUnsigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getUB(reg1) val right = registers.getUB(reg2) @@ -897,6 +939,23 @@ class VirtualMachine(val memory: Memory, program: List) { registers.setUB(reg1, result.toUByte()) } + private fun divModByteUnsignedInplace(operator: String, reg1: Int, address: Int) { + val left = memory.getUB(address) + val right = registers.getUB(reg1) + val result = when(operator) { + "/" -> { + if(right==0.toUByte()) 0xffu + else left / right + } + "%" -> { + if(right==0.toUByte()) 0xffu + else left % right + } + else -> throw IllegalArgumentException("operator byte $operator") + } + memory.setUB(address, result.toUByte()) + } + private fun plusMinusMultAnyWord(operator: String, reg1: Int, reg2: Int) { val left = registers.getUW(reg1) val right = registers.getUW(reg2) @@ -938,6 +997,23 @@ class VirtualMachine(val memory: Memory, program: List) { registers.setUW(reg1, result.toUShort()) } + private fun divModWordUnsignedInplace(operator: String, reg1: Int, address: Int) { + val left = memory.getUW(address) + val right = registers.getUW(reg1) + val result = when(operator) { + "/" -> { + if(right==0.toUShort()) 0xffffu + else left / right + } + "%" -> { + if(right==0.toUShort()) 0xffffu + else left % right + } + else -> throw IllegalArgumentException("operator word $operator") + } + memory.setUW(address, result.toUShort()) + } + private fun divModWordSigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getSW(reg1) val right = registers.getSW(reg2) @@ -955,6 +1031,23 @@ class VirtualMachine(val memory: Memory, program: List) { registers.setSW(reg1, result.toShort()) } + private fun divModWordSignedInplace(operator: String, reg1: Int, address: Int) { + val left = memory.getSW(address) + val right = registers.getSW(reg1) + val result = when(operator) { + "/" -> { + if(right==0.toShort()) 32767 + else left / right + } + "%" -> { + if(right==0.toShort()) 32767 + else left % right + } + else -> throw IllegalArgumentException("operator word $operator") + } + memory.setSW(address, result.toShort()) + } + private fun arithFloat(left: Float, operator: String, right: Float): Float = when(operator) { "+" -> left + right "-" -> left - right