From 0a0c58d450b62450f5a853f3d7a147e6cbe43db8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 30 Mar 2022 23:40:39 +0200 Subject: [PATCH] added for loop over constant ranges --- codeAst/src/prog8/code/ast/AstExpressions.kt | 4 +- .../src/prog8/codegen/virtual/CodeGen.kt | 131 +++++++++++++++--- .../prog8/compiler/IntermediateAstMaker.kt | 2 +- examples/test.p8 | 9 +- virtualmachine/src/prog8/vm/Instructions.kt | 61 ++++---- virtualmachine/src/prog8/vm/VirtualMachine.kt | 18 +++ virtualmachine/test/TestInstructions.kt | 9 +- 7 files changed, 174 insertions(+), 60 deletions(-) diff --git a/codeAst/src/prog8/code/ast/AstExpressions.kt b/codeAst/src/prog8/code/ast/AstExpressions.kt index 64b21c8dd..2e10fc146 100644 --- a/codeAst/src/prog8/code/ast/AstExpressions.kt +++ b/codeAst/src/prog8/code/ast/AstExpressions.kt @@ -126,8 +126,8 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position) get() = children[0] as PtExpression val to: PtExpression get() = children[1] as PtExpression - val step: PtExpression - get() = children[2] as PtExpression + val step: PtNumber + get() = children[2] as PtNumber override fun printProperties() {} } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 28c2ce50c..966d3da3f 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -54,6 +54,8 @@ class CodeGen(internal val program: PtProgram, vmprog.addBlock(translate(block)) } + println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}") + return vmprog } @@ -103,7 +105,7 @@ class CodeGen(internal val program: PtProgram, is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}") else -> TODO("missing codegen for $node") } - if(code.lines.isNotEmpty()) + if(code.lines.isNotEmpty() && node.position.line!=0) code.lines.add(0, VmCodeComment(node.position.toString())) return code } @@ -114,9 +116,10 @@ class CodeGen(internal val program: PtProgram, val code = VmCodeChunk() when(iterable) { is PtRange -> { - TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ") - iterable.printIndented(0) - TODO("forloop over range") + if(iterable.from is PtNumber && iterable.to is PtNumber) + code += translateForInConstantRange(forLoop, loopvar) + else + code += translateForInNonConstantRange(forLoop, loopvar) } is PtIdentifier -> { val arrayAddress = allocations.get(iterable.targetName) @@ -139,19 +142,9 @@ class CodeGen(internal val program: PtProgram, } else { // iterate over array val elementDt = ArrayToElementTypes.getValue(iterable.type) - val elementSize = program.memsizer.memorySize(elementDt).toUInt() - val lengthBytes = iterableVar.length!! * elementSize.toInt() + val elementSize = program.memsizer.memorySize(elementDt) + val lengthBytes = iterableVar.length!! * elementSize val lengthReg = vmRegisters.nextFree() - /* - index = 0 - _loop: - if index==(length * ) goto _end - loopvar = read_mem(iterable+index) - - index += - goto _loop - _end: ... - */ code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes) code += VmCodeLabel(loopLabel) @@ -159,7 +152,7 @@ class CodeGen(internal val program: PtProgram, code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress) code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress) code += translateNode(forLoop.statements) - code += addConst(VmDataType.BYTE, indexReg, elementSize) + code += addConstReg(VmDataType.BYTE, indexReg, elementSize) code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeLabel(endLabel) } @@ -169,17 +162,111 @@ class CodeGen(internal val program: PtProgram, return code } - private fun addConst(dt: VmDataType, reg: Int, value: UInt): VmCodeChunk { + private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk { + val iterable = forLoop.iterable as PtRange + TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in non-constant range ${iterable} ") + iterable.printIndented(0) + } + + private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk { + val iterable = forLoop.iterable as PtRange + val step = iterable.step.number.toInt() + val range = IntProgression.fromClosedRange( + (iterable.from as PtNumber).number.toInt(), + (iterable.to as PtNumber).number.toInt() + step, + step) + if (range.isEmpty() || range.step==0) + throw AssemblyError("empty range or step 0") + + val loopLabel = createLabelName() + val loopvarAddress = allocations.get(loopvar.scopedName) + val indexReg = vmRegisters.nextFree() + val endvalueReg = vmRegisters.nextFree() + val vmDt = vmType(loopvar.dt) + val code = VmCodeChunk() + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=endvalueReg, value=range.last) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=indexReg, value=range.first) + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=indexReg, value=loopvarAddress) + code += VmCodeLabel(loopLabel) + code += translateNode(forLoop.statements) + if(range.step==1 || range.step==2) { + code += addConstMem(vmDt, loopvarAddress.toUInt(), range.step) + code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = indexReg, value = loopvarAddress) + } else { + code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = indexReg, value = loopvarAddress) + code += addConstReg(vmDt, indexReg, range.step) + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = indexReg, value = loopvarAddress) + } + code += VmCodeInstruction(Opcode.BNE, vmDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel) + return code + } + + private fun addConstReg(dt: VmDataType, reg: Int, value: Int): VmCodeChunk { val code = VmCodeChunk() when(value) { - 0u -> { /* do nothing */ } - 1u -> { + 0 -> { /* do nothing */ } + 1 -> { code += VmCodeInstruction(Opcode.INC, dt, reg1=reg) } + 2 -> { + code += VmCodeInstruction(Opcode.INC, dt, reg1=reg) + code += VmCodeInstruction(Opcode.INC, dt, reg1=reg) + } + -1 -> { + code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg) + } + -2 -> { + code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg) + code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg) + } else -> { val valueReg = vmRegisters.nextFree() - code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt()) - code += VmCodeInstruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg) + if(value>0) { + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value) + code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = reg, reg3 = valueReg) + } + else { + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value) + code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = reg, reg3 = valueReg) + } + } + } + return code + } + + private fun addConstMem(dt: VmDataType, address: UInt, value: Int): VmCodeChunk { + val code = VmCodeChunk() + when(value) { + 0 -> { /* do nothing */ } + 1 -> { + code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt()) + } + 2 -> { + code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt()) + code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt()) + } + -1 -> { + code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt()) + } + -2 -> { + code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt()) + code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt()) + } + else -> { + val valueReg = vmRegisters.nextFree() + val operandReg = vmRegisters.nextFree() + if(value>0) { + code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt()) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value) + code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg) + code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt()) + } + else { + code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt()) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value) + code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg) + code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt()) + } } } return code diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index 8dfeaa959..f289e507a 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -483,7 +483,7 @@ class IntermediateAstMaker(val program: Program) { val range=PtRange(type, srcRange.position) range.add(transformExpression(srcRange.from)) range.add(transformExpression(srcRange.to)) - range.add(transformExpression(srcRange.step)) + range.add(transformExpression(srcRange.step) as PtNumber) return range } diff --git a/examples/test.p8 b/examples/test.p8 index d666cb56c..8673c8494 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -34,8 +34,11 @@ main { txt.print_uw(ww) ; 11 txt.nl() - for bc in 10 to 20 step 3 { - ; 10,13,16,19 + const ubyte rfrom = 10 + const ubyte rto = 17 + + for bc in rfrom to rto step 2 { + ; 10,12,14,16 txt.print_ub(bc) txt.spc() ww++ @@ -43,7 +46,7 @@ main { txt.print_uw(ww) ; 15 txt.nl() - for bc in 30 to 10 step -4 { + for bc in 30 to 0 step -4 { ; 30,26,22,18,14,10,6,2 txt.print_ub(bc) txt.spc() diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index 54367697c..cab3b571c 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -61,28 +61,28 @@ BRANCHING --------- All have type b or w. -bz reg1, value - branch if reg1 is zero -bnz reg1, value - branch if reg1 is not zero -beq reg1, reg2, value - jump to location in program given by value, if reg1 == reg2 -bne reg1, reg2, value - jump to location in program given by value, if reg1 != reg2 -blt reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (unsigned) -blts reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (signed) -ble reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (unsigned) -bles reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (signed) -bgt reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (unsigned) -bgts reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (signed) -bge reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (unsigned) -bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed) -seq reg1, reg2, reg3 - set reg=1 if reg2 == reg3, otherwise set reg1=0 -sne reg1, reg2, reg3 - set reg=1 if reg2 != reg3, otherwise set reg1=0 -slt reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (unsigned), otherwise set reg1=0 -slts reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (signed), otherwise set reg1=0 -sle reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (unsigned), otherwise set reg1=0 -sles reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (signed), otherwise set reg1=0 -sgt reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (unsigned), otherwise set reg1=0 -sgts reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (signed), otherwise set reg1=0 -sge reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (unsigned), otherwise set reg1=0 -sges reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (signed), otherwise set reg1=0 +bz reg1, location - branch to location if reg1 is zero +bnz reg1, location - branch to location if reg1 is not zero +beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2 +bne reg1, reg2, location - jump to location in program given by location, if reg1 != reg2 +blt reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (unsigned) +blts reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (signed) +ble reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (unsigned) +bles reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (signed) +bgt reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (unsigned) +bgts reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (signed) +bge reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (unsigned) +bges reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (signed) +seq reg1, reg2, reg3 - set reg=1 if reg2 == reg3, otherwise set reg1=0 +sne reg1, reg2, reg3 - set reg=1 if reg2 != reg3, otherwise set reg1=0 +slt reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (unsigned), otherwise set reg1=0 +slts reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (signed), otherwise set reg1=0 +sle reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (unsigned), otherwise set reg1=0 +sles reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (signed), otherwise set reg1=0 +sgt reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (unsigned), otherwise set reg1=0 +sgts reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (signed), otherwise set reg1=0 +sge reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (unsigned), otherwise set reg1=0 +sges reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (signed), otherwise set reg1=0 TODO: support for the prog8 special branching instructions if_XX (bcc, bcs etc.) but we don't have any 'processor flags' whatsoever in the vm so it's a bit weird @@ -95,7 +95,9 @@ All have type b or w. Note: result types are the same as operand types! E.g. byt ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (latter not yet implemented as we don't have longs yet) exts reg1 - reg1 = signed extension of reg1 (byte to word, or word to long) (note: latter ext.w, not yet implemented as we don't have longs yet) inc reg1 - reg1 = reg1+1 +incm address - memory at address += 1 dec reg1 - reg1 = reg1-1 +decm address - memory at address -= 1 neg reg1 - reg1 = sign negation of reg1 add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed) sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signed) @@ -181,7 +183,9 @@ enum class Opcode { SGES, INC, + INCM, DEC, + DECM, NEG, ADD, SUB, @@ -225,19 +229,22 @@ data class Instruction( val value: Int?=null, // 0-$ffff val symbol: List?=null // alternative to value ) { - override fun toString(): String { - val result = mutableListOf(opcode.name.lowercase()) + init { val format = instructionFormats.getValue(opcode) if(format.datatypes.isNotEmpty() && type==null) throw IllegalArgumentException("missing type") if(format.reg1 && reg1==null || - format.reg2 && reg2==null || - format.reg3 && reg3==null) + format.reg2 && reg2==null || + format.reg3 && reg3==null) throw IllegalArgumentException("missing a register") if(format.value && (value==null && symbol==null)) throw IllegalArgumentException("missing a value or symbol") + } + + override fun toString(): String { + val result = mutableListOf(opcode.name.lowercase()) when(type) { VmDataType.BYTE -> result.add(".b ") @@ -320,7 +327,9 @@ val instructionFormats = mutableMapOf( Opcode.SGES to InstructionFormat(BW, true, true, true, false), Opcode.INC to InstructionFormat(BW, true, false, false, false), + Opcode.INCM to InstructionFormat(BW, false, false, false, true ), Opcode.DEC to InstructionFormat(BW, true, false, false, false), + Opcode.DECM to InstructionFormat(BW, false, false, false, true ), Opcode.NEG to InstructionFormat(BW, true, false, false, false), Opcode.ADD to InstructionFormat(BW, true, true, true, false), Opcode.SUB to InstructionFormat(BW, true, true, true, false), diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 851c493d2..d5e13f5e7 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -121,7 +121,9 @@ class VirtualMachine(val memory: Memory, program: List) { Opcode.SGES -> InsSGES(ins) Opcode.INC -> InsINC(ins) + Opcode.INCM -> InsINCM(ins) Opcode.DEC -> InsDEC(ins) + Opcode.DECM -> InsDECM(ins) Opcode.NEG -> InsNEG(ins) Opcode.ADD -> InsADD(ins) Opcode.SUB -> InsSUB(ins) @@ -507,6 +509,14 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } + private fun InsINCM(i: Instruction) { + when(i.type!!) { + VmDataType.BYTE -> memory.setUB(i.value!!, (memory.getUB(i.value)+1u).toUByte()) + VmDataType.WORD -> memory.setUW(i.value!!, (memory.getUW(i.value)+1u).toUShort()) + } + pc++ + } + private fun InsDEC(i: Instruction) { when(i.type!!) { VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)-1u).toUByte()) @@ -515,6 +525,14 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } + private fun InsDECM(i: Instruction) { + when(i.type!!) { + VmDataType.BYTE -> memory.setUB(i.value!!, (memory.getUB(i.value)-1u).toUByte()) + VmDataType.WORD -> memory.setUW(i.value!!, (memory.getUW(i.value)-1u).toUShort()) + } + pc++ + } + private fun InsNEG(i: Instruction) { when(i.type!!) { VmDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1).toInt()).toUByte()) diff --git a/virtualmachine/test/TestInstructions.kt b/virtualmachine/test/TestInstructions.kt index d81593f1c..1a083248f 100644 --- a/virtualmachine/test/TestInstructions.kt +++ b/virtualmachine/test/TestInstructions.kt @@ -47,23 +47,20 @@ class TestInstructions: FunSpec({ } test("missing type should fail") { - val ins = Instruction(Opcode.BZ, reg1=42, value=9999) shouldThrow { - ins.toString() + Instruction(Opcode.BZ, reg1=42, value=9999) } } test("missing registers should fail") { - val ins = Instruction(Opcode.BZ, VmDataType.BYTE, value=9999) shouldThrow { - ins.toString() + Instruction(Opcode.BZ, VmDataType.BYTE, value=9999) } } test("missing value should fail") { - val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42) shouldThrow { - ins.toString() + Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42) } }