added for loop over constant ranges

This commit is contained in:
Irmen de Jong 2022-03-30 23:40:39 +02:00
parent 0dc592b819
commit 0a0c58d450
7 changed files with 174 additions and 60 deletions

View File

@ -126,8 +126,8 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
get() = children[0] as PtExpression get() = children[0] as PtExpression
val to: PtExpression val to: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
val step: PtExpression val step: PtNumber
get() = children[2] as PtExpression get() = children[2] as PtNumber
override fun printProperties() {} override fun printProperties() {}
} }

View File

@ -54,6 +54,8 @@ class CodeGen(internal val program: PtProgram,
vmprog.addBlock(translate(block)) vmprog.addBlock(translate(block))
} }
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
return vmprog 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}") 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") 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())) code.lines.add(0, VmCodeComment(node.position.toString()))
return code return code
} }
@ -114,9 +116,10 @@ class CodeGen(internal val program: PtProgram,
val code = VmCodeChunk() val code = VmCodeChunk()
when(iterable) { when(iterable) {
is PtRange -> { is PtRange -> {
TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ") if(iterable.from is PtNumber && iterable.to is PtNumber)
iterable.printIndented(0) code += translateForInConstantRange(forLoop, loopvar)
TODO("forloop over range") else
code += translateForInNonConstantRange(forLoop, loopvar)
} }
is PtIdentifier -> { is PtIdentifier -> {
val arrayAddress = allocations.get(iterable.targetName) val arrayAddress = allocations.get(iterable.targetName)
@ -139,19 +142,9 @@ class CodeGen(internal val program: PtProgram,
} else { } else {
// iterate over array // iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type) val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt).toUInt() val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * elementSize.toInt() val lengthBytes = iterableVar.length!! * elementSize
val lengthReg = vmRegisters.nextFree() val lengthReg = vmRegisters.nextFree()
/*
index = 0
_loop:
if index==(length * <element_size>) goto _end
loopvar = read_mem(iterable+index)
<statements>
index += <elementsize>
goto _loop
_end: ...
*/
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel) 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.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress) code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements) code += translateNode(forLoop.statements)
code += addConst(VmDataType.BYTE, indexReg, elementSize) code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
} }
@ -169,17 +162,111 @@ class CodeGen(internal val program: PtProgram,
return code 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() val code = VmCodeChunk()
when(value) { when(value) {
0u -> { /* do nothing */ } 0 -> { /* do nothing */ }
1u -> { 1 -> {
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg) 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 -> { else -> {
val valueReg = vmRegisters.nextFree() val valueReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt()) if(value>0) {
code += VmCodeInstruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg) 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 return code

View File

@ -483,7 +483,7 @@ class IntermediateAstMaker(val program: Program) {
val range=PtRange(type, srcRange.position) val range=PtRange(type, srcRange.position)
range.add(transformExpression(srcRange.from)) range.add(transformExpression(srcRange.from))
range.add(transformExpression(srcRange.to)) range.add(transformExpression(srcRange.to))
range.add(transformExpression(srcRange.step)) range.add(transformExpression(srcRange.step) as PtNumber)
return range return range
} }

View File

@ -34,8 +34,11 @@ main {
txt.print_uw(ww) ; 11 txt.print_uw(ww) ; 11
txt.nl() txt.nl()
for bc in 10 to 20 step 3 { const ubyte rfrom = 10
; 10,13,16,19 const ubyte rto = 17
for bc in rfrom to rto step 2 {
; 10,12,14,16
txt.print_ub(bc) txt.print_ub(bc)
txt.spc() txt.spc()
ww++ ww++
@ -43,7 +46,7 @@ main {
txt.print_uw(ww) ; 15 txt.print_uw(ww) ; 15
txt.nl() 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 ; 30,26,22,18,14,10,6,2
txt.print_ub(bc) txt.print_ub(bc)
txt.spc() txt.spc()

View File

@ -61,28 +61,28 @@ BRANCHING
--------- ---------
All have type b or w. All have type b or w.
bz reg1, value - branch if reg1 is zero bz reg1, location - branch to location if reg1 is zero
bnz reg1, value - branch if reg1 is not zero bnz reg1, location - branch to location if reg1 is not zero
beq reg1, reg2, value - jump to location in program given by value, if reg1 == reg2 beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2
bne reg1, reg2, value - jump to location in program given by value, if reg1 != reg2 bne reg1, reg2, location - jump to location in program given by location, if reg1 != reg2
blt reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (unsigned) blt reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (unsigned)
blts reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (signed) blts reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (signed)
ble reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (unsigned) ble reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (unsigned)
bles reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (signed) bles reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (signed)
bgt reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (unsigned) bgt reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgts reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (signed) bgts reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (signed)
bge reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (unsigned) bge reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed) 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 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 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 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 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 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 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 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 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 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 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.) 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 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) 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) 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 inc reg1 - reg1 = reg1+1
incm address - memory at address += 1
dec reg1 - reg1 = reg1-1 dec reg1 - reg1 = reg1-1
decm address - memory at address -= 1
neg reg1 - reg1 = sign negation of reg1 neg reg1 - reg1 = sign negation of reg1
add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed) add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed)
sub 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, SGES,
INC, INC,
INCM,
DEC, DEC,
DECM,
NEG, NEG,
ADD, ADD,
SUB, SUB,
@ -225,19 +229,22 @@ data class Instruction(
val value: Int?=null, // 0-$ffff val value: Int?=null, // 0-$ffff
val symbol: List<String>?=null // alternative to value val symbol: List<String>?=null // alternative to value
) { ) {
override fun toString(): String { init {
val result = mutableListOf(opcode.name.lowercase())
val format = instructionFormats.getValue(opcode) val format = instructionFormats.getValue(opcode)
if(format.datatypes.isNotEmpty() && type==null) if(format.datatypes.isNotEmpty() && type==null)
throw IllegalArgumentException("missing type") throw IllegalArgumentException("missing type")
if(format.reg1 && reg1==null || if(format.reg1 && reg1==null ||
format.reg2 && reg2==null || format.reg2 && reg2==null ||
format.reg3 && reg3==null) format.reg3 && reg3==null)
throw IllegalArgumentException("missing a register") throw IllegalArgumentException("missing a register")
if(format.value && (value==null && symbol==null)) if(format.value && (value==null && symbol==null))
throw IllegalArgumentException("missing a value or symbol") throw IllegalArgumentException("missing a value or symbol")
}
override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase())
when(type) { when(type) {
VmDataType.BYTE -> result.add(".b ") VmDataType.BYTE -> result.add(".b ")
@ -320,7 +327,9 @@ val instructionFormats = mutableMapOf(
Opcode.SGES to InstructionFormat(BW, true, true, true, false), Opcode.SGES to InstructionFormat(BW, true, true, true, false),
Opcode.INC to InstructionFormat(BW, true, false, false, 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.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.NEG to InstructionFormat(BW, true, false, false, false),
Opcode.ADD to InstructionFormat(BW, true, true, true, false), Opcode.ADD to InstructionFormat(BW, true, true, true, false),
Opcode.SUB to InstructionFormat(BW, true, true, true, false), Opcode.SUB to InstructionFormat(BW, true, true, true, false),

View File

@ -121,7 +121,9 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.SGES -> InsSGES(ins) Opcode.SGES -> InsSGES(ins)
Opcode.INC -> InsINC(ins) Opcode.INC -> InsINC(ins)
Opcode.INCM -> InsINCM(ins)
Opcode.DEC -> InsDEC(ins) Opcode.DEC -> InsDEC(ins)
Opcode.DECM -> InsDECM(ins)
Opcode.NEG -> InsNEG(ins) Opcode.NEG -> InsNEG(ins)
Opcode.ADD -> InsADD(ins) Opcode.ADD -> InsADD(ins)
Opcode.SUB -> InsSUB(ins) Opcode.SUB -> InsSUB(ins)
@ -507,6 +509,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++ 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) { private fun InsDEC(i: Instruction) {
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)-1u).toUByte()) VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)-1u).toUByte())
@ -515,6 +525,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++ 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) { private fun InsNEG(i: Instruction) {
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1).toInt()).toUByte()) VmDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1).toInt()).toUByte())

View File

@ -47,23 +47,20 @@ class TestInstructions: FunSpec({
} }
test("missing type should fail") { test("missing type should fail") {
val ins = Instruction(Opcode.BZ, reg1=42, value=9999)
shouldThrow<IllegalArgumentException> { shouldThrow<IllegalArgumentException> {
ins.toString() Instruction(Opcode.BZ, reg1=42, value=9999)
} }
} }
test("missing registers should fail") { test("missing registers should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
shouldThrow<IllegalArgumentException> { shouldThrow<IllegalArgumentException> {
ins.toString() Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
} }
} }
test("missing value should fail") { test("missing value should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
shouldThrow<IllegalArgumentException> { shouldThrow<IllegalArgumentException> {
ins.toString() Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
} }
} }