From bb1bf6a88cfad6447145b65675860ec07e1d5390 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 28 Mar 2022 00:18:56 +0200 Subject: [PATCH] working on vm --- codeAst/src/prog8/code/SymbolTable.kt | 19 ++++--- codeAst/src/prog8/code/ast/AstBase.kt | 2 +- .../codegen/cpu6502/ProgramAndVarsGen.kt | 2 +- .../codegen/cpu6502/VariableAllocator.kt | 16 ++---- .../codegen/experimental/AstToXmlConverter.kt | 4 +- .../src/prog8/codegen/virtual/CodeGen.kt | 27 +++++++++- .../prog8/codegen/virtual/ExpressionGen.kt | 43 +++++++-------- .../codegen/virtual/VariableAllocator.kt | 6 +-- .../astprocessing/SymbolTableMaker.kt | 9 +++- docs/source/todo.rst | 19 +++++-- examples/test.p8 | 54 +++++++++++-------- virtualmachine/src/prog8/vm/Instructions.kt | 1 + 12 files changed, 121 insertions(+), 81 deletions(-) diff --git a/codeAst/src/prog8/code/SymbolTable.kt b/codeAst/src/prog8/code/SymbolTable.kt index 7c4c70b5e..e152af41b 100644 --- a/codeAst/src/prog8/code/SymbolTable.kt +++ b/codeAst/src/prog8/code/SymbolTable.kt @@ -119,7 +119,7 @@ open class StNode(val name: String, } } - protected fun printIndented(indent: Int) { + fun printIndented(indent: Int) { print(" ".repeat(indent)) when(type) { StNodeType.GLOBAL -> print("SYMBOL-TABLE:") @@ -152,21 +152,24 @@ class StStaticVariable(name: String, val initialNumericValue: Double?, val initialStringValue: StString?, val initialArrayValue: StArray?, - val arraysize: Int?, + val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val zpwish: ZeropageWish, position: Position) : StNode(name, StNodeType.STATICVAR, position) { init { - if(arraysize!=null && initialArrayValue!=null) - require(arraysize == initialArrayValue.size) - if(arraysize!=null || initialArrayValue!=null) - require(initialStringValue==null && initialNumericValue==null) + if(length!=null) { + require(initialNumericValue == null) + if(initialArrayValue!=null) + require(length == initialArrayValue.size) + } if(initialNumericValue!=null) require(dt in NumericDatatypes) - if(initialArrayValue!=null || arraysize!=null) + if(initialArrayValue!=null) require(dt in ArrayDatatypes) - if(initialStringValue!=null) + if(initialStringValue!=null) { require(dt == DataType.STR) + require(length == initialStringValue.first.length+1) + } } override fun printProperties() { diff --git a/codeAst/src/prog8/code/ast/AstBase.kt b/codeAst/src/prog8/code/ast/AstBase.kt index 6a7c6d6f4..e4b0c7412 100644 --- a/codeAst/src/prog8/code/ast/AstBase.kt +++ b/codeAst/src/prog8/code/ast/AstBase.kt @@ -11,7 +11,7 @@ sealed class PtNode(val position: Position) { val children = mutableListOf() lateinit var parent: PtNode - protected fun printIndented(indent: Int) { + fun printIndented(indent: Int) { print(" ".repeat(indent)) print("${this.javaClass.simpleName} ") printProperties() diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 1344fc1fe..882475e73 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -480,7 +480,7 @@ internal class ProgramAndVarsGen( DataType.STR -> { throw AssemblyError("all string vars should have been interned into prog") } - in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.arraysize) + in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.length) else -> { throw AssemblyError("weird dt") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 247289350..500f8ea69 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -63,11 +63,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable, var numberOfNonIntegerVariables = 0 varsRequiringZp.forEach { variable -> - val numElements = numArrayElements(variable) val result = zeropage.allocate( variable.scopedName, variable.dt, - numElements, + variable.length, variable.position, errors ) @@ -83,11 +82,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable, if(errors.noErrors()) { varsPreferringZp.forEach { variable -> - val numElements = numArrayElements(variable) val result = zeropage.allocate( variable.scopedName, variable.dt, - numElements, + variable.length, variable.position, errors ) @@ -103,11 +101,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable, if(zeropage.free.isEmpty()) { break } else { - val numElements = numArrayElements(variable) val result = zeropage.allocate( variable.scopedName, variable.dt, - numElements, + variable.length, variable.position, errors ) @@ -137,11 +134,4 @@ internal class VariableAllocator(private val symboltable: SymbolTable, collect(st) return vars } - - private fun numArrayElements(variable: StStaticVariable) = - when(variable.dt) { - DataType.STR -> variable.initialStringValue!!.first.length+1 // 1 extra because of 0 termination char - in ArrayDatatypes -> variable.arraysize!! - else -> null - } } diff --git a/codeGenExperimental/src/prog8/codegen/experimental/AstToXmlConverter.kt b/codeGenExperimental/src/prog8/codegen/experimental/AstToXmlConverter.kt index 010c04499..a37a69e7c 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/AstToXmlConverter.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/AstToXmlConverter.kt @@ -73,8 +73,8 @@ class AstToXmlConverter(internal val program: PtProgram, xml.elt("var") xml.attr("type", node.dt.name) xml.attr("zpwish", node.zpwish.name) - if(node.arraysize!=null) - xml.attr("arraysize", node.arraysize.toString()) + if(node.length!=null) + xml.attr("length", node.length.toString()) if(node.initialNumericValue!=null || node.initialArrayValue!=null || node.initialStringValue!=null) { xml.startChildren() if(node.initialNumericValue!=null) { diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index c53f44075..47ba2c4b4 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -1,5 +1,6 @@ package prog8.codegen.virtual +import prog8.code.StStaticVariable import prog8.code.SymbolTable import prog8.code.ast.* import prog8.code.core.* @@ -58,7 +59,7 @@ class CodeGen(internal val program: PtProgram, is PtJump -> translate(node) is PtWhen -> TODO("when") is PtPipe -> expressionEval.translate(node, regUsage.nextFree(), regUsage) - is PtForLoop -> TODO("for-loop") + is PtForLoop -> translate(node, regUsage) is PtIfElse -> translate(node, regUsage) is PtPostIncrDecr -> translate(node, regUsage) is PtRepeatLoop -> translate(node, regUsage) @@ -90,6 +91,30 @@ class CodeGen(internal val program: PtProgram, return code } + private fun translate(forLoop: PtForLoop, regUsage: RegisterUsage): VmCodeChunk { + val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable + val iterable = forLoop.iterable + val code = VmCodeChunk() + when(iterable) { + is PtRange -> { + println("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ") + iterable.printIndented(0) + TODO() + } + is PtIdentifier -> { + val address = allocations.get(iterable.targetName) + val variable = symbolTable.lookup(iterable.targetName) as StStaticVariable + val length = variable.length!! + println("forloop ${loopvar.dt} ${loopvar.scopedName} in $iterable @${address.toHex()} ${length}") + val indexReg = regUsage.nextFree() + val loopvarReg = regUsage.nextFree() + TODO() + } + else -> throw AssemblyError("weird for iterable") + } + return code + } + private fun translate(ifElse: PtIfElse, regUsage: RegisterUsage): VmCodeChunk { var branch = Opcode.BZ var condition = ifElse.condition diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index 9e5258686..23bd58f8e 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -83,14 +83,14 @@ internal class ExpressionGen(val codeGen: CodeGen) { val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position) call.children.add(check.element) call.children.add(check.iterable) - call.children.add(PtNumber(DataType.UBYTE, iterable.arraysize!!.toDouble(), iterable.position)) + call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) code += translate(call, resultRegister, regUsage) } DataType.ARRAY_UW, DataType.ARRAY_W -> { val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position) call.children.add(check.element) call.children.add(check.iterable) - call.children.add(PtNumber(DataType.UBYTE, iterable.arraysize!!.toDouble(), iterable.position)) + call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) code += translate(call, resultRegister, regUsage) } DataType.ARRAY_F -> TODO("containment check in float-array") @@ -204,21 +204,17 @@ internal class ExpressionGen(val codeGen: CodeGen) { } DataType.FLOAT -> { TODO("floating point not yet supported") - when(cast.value.type) { - DataType.BYTE -> { - // TODO("byte -> float") - } - DataType.UBYTE -> { - // TODO("ubyte -> float") - } - DataType.WORD -> { - // TODO("word -> float") - } - DataType.UWORD -> { - // TODO("uword -> float") - } - else -> throw AssemblyError("weird cast value type") - } +// when(cast.value.type) { +// DataType.BYTE -> { +// } +// DataType.UBYTE -> { +// } +// DataType.WORD -> { +// } +// DataType.UWORD -> { +// } +// else -> throw AssemblyError("weird cast value type") +// } } else -> throw AssemblyError("weird cast type") } @@ -229,7 +225,8 @@ internal class ExpressionGen(val codeGen: CodeGen) { val code = VmCodeChunk() val leftResultReg = regUsage.nextFree() val rightResultReg = regUsage.nextFree() - // TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. + // TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. But only if this would result in a different opcode such as ADD 1 -> INC, MUL 1 -> NOP + // actually optimizing the code should not be done here but in a tailored code optimizer step. val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage) val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage) code += leftCode @@ -288,7 +285,7 @@ internal class ExpressionGen(val codeGen: CodeGen) { val ins = if(binExpr.type in SignedDatatypes) Opcode.SGES else Opcode.SGE code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) } - else -> TODO("operator ${binExpr.operator}") + else -> throw AssemblyError("weird operator ${binExpr.operator}") } return code } @@ -357,9 +354,11 @@ internal class ExpressionGen(val codeGen: CodeGen) { "msb" -> { code += translateExpression(call.args.single(), resultRegister, regUsage) code += VmCodeInstruction(Instruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)) + // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. } "lsb" -> { code += translateExpression(call.args.single(), resultRegister, regUsage) + // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. } "memory" -> { val name = (call.args[0] as PtString).value @@ -391,12 +390,6 @@ internal class ExpressionGen(val codeGen: CodeGen) { code += translateExpression(call.args.single(), addressReg, regUsage) code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg)) } - "memory" -> { - val memname = (call.args[0] as PtString).value - val size = (call.args[1] as PtNumber).number.toInt() - val align = (call.args[2] as PtNumber).number.toInt() - TODO("memory '$memname', $size, $align") - } else -> { TODO("builtinfunc ${call.name}") // code += VmCodeInstruction(Instruction(Opcode.NOP)) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt index 411161bbe..f16bb36c0 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt @@ -19,7 +19,7 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg when (variable.dt) { DataType.STR -> variable.initialStringValue!!.first.length + 1 // include the zero byte in NumericDatatypes -> program.memsizer.memorySize(variable.dt) - in ArrayDatatypes -> program.memsizer.memorySize(variable.dt, variable.arraysize!!) + in ArrayDatatypes -> program.memsizer.memorySize(variable.dt, variable.length!!) else -> throw InternalCompilerException("weird dt") } @@ -55,14 +55,14 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg if(variable.initialArrayValue!=null) { variable.initialArrayValue!!.joinToString(",") { it.number!!.toString() } } else { - (1..variable.arraysize!!).joinToString(",") { "0" } + (1..variable.length!!).joinToString(",") { "0" } } } in ArrayDatatypes -> { if(variable.initialArrayValue!==null) { variable.initialArrayValue!!.joinToString(",") { it.number!!.toHex() } } else { - (1..variable.arraysize!!).joinToString(",") { "0" } + (1..variable.length!!).joinToString(",") { "0" } } } else -> throw InternalCompilerException("weird dt") diff --git a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt index aefe0c3fc..8b8b75b58 100644 --- a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt @@ -63,7 +63,14 @@ internal class SymbolTableMaker: IAstVisitor { val initialArray = makeInitialArray(initialArrayLit) if(decl.isArray && decl.datatype !in ArrayDatatypes) throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}") - StStaticVariable(decl.name, decl.datatype, initialNumeric, initialString, initialArray, decl.arraysize?.constIndex(), decl.zeropage, decl.position) + val numElements = + if(decl.isArray) + decl.arraysize!!.constIndex() + else if(initialStringLit!=null) + initialStringLit.value.length+1 // include the terminating 0-byte + else + null + StStaticVariable(decl.name, decl.datatype, initialNumeric, initialString, initialArray, numElements, decl.zeropage, decl.position) } VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position) VarDeclType.MEMORY -> StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), decl.position) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 67f23043a..02803f9b0 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,21 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- vm codegen: ForLoop +- vm codegen: fix primes endless loop stuck on '2' +- x16: screen_set_mode -> screen_mode + other api https://github.com/commanderx16/x16-docs/blob/master/Commander%20X16%20Programmer's%20Reference%20Guide.md#function-name-screen_mode +- optimize diskio load_raw on X16 because headerless files are now supported https://github.com/commanderx16/x16-rom/pull/216 + note: must still work on c64/c128 that don't have this! +- major version bump once X16 r39 rom is officially finalized +- vm codegen: When +- vm codegen: Pipe expression +- vm codegen: validate that PtFunctionCall translation works okay with resultregister +- vm: support no globals re-init option +- vm codegen/assembler: variable memory locations should also be referenced by the variable name instead of just the address +- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed. + It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed. + So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step. + ... @@ -62,10 +77,8 @@ Expressions: Optimizations: -- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once (if?) 6502-codegen is no longer done from +- various optimizers should/do skip stuff if compTarget.name==VMTarget.NAME. Once (if?) 6502-codegen is no longer done from the old CompilerAst, those checks should probably be removed. - (most of them avoid the case where extra temporary variables are introduced in an attempt to simplify - the expression code generation) - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served - translateUnaryFunctioncall() in BuiltinFunctionsAsmGen: should be able to assign parameters to a builtin function directly from register(s), this will make the use of a builtin function in a pipe expression more efficient without using a temporary variable compare ``aa = startvalue(1) |> sin8u() |> cos8u() |> sin8u() |> cos8u()`` diff --git a/examples/test.p8 b/examples/test.p8 index d51bc3e2f..d1e0571a7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,32 +7,40 @@ main { txt.clear_screen() txt.print("Welcome to a prog8 pixel shader :-)\n") - uword @shared chunk = memory("irmen", 4000, 256) - txt.print_uwhex(chunk,true) + uword ww = 0 + ubyte bc + uword wc + + for bc in "irmen" { + ; +5 -> 17 + txt.chrout(bc) + ww++ + } + txt.print_uw(ww) txt.nl() - ubyte bb = 4 - ubyte[] array = [1,2,3,4,5,6] - uword[] warray = [1111,2222,3333] - str tekst = "test" - uword ww = 19 - bb = bb in "teststring" - bb++ - bb = bb in [1,2,3,4,5,6] - bb++ - bb = bb in array - bb++ - bb = bb in tekst - bb++ - bb = ww in warray - bb++ - bb = 666 in warray - bb ++ - bb = '?' in tekst - bb++ - txt.print("bb=") - txt.print_ub(bb) + for wc in [1000,1111,1222] { + txt.print_uw(wc) + txt.spc() + ww++ ; +3 -> 20 + } + txt.print_uw(ww) txt.nl() + + for bc in 10 to 20 step 3 { + ; 10,13,16,19 -> 4 + ww++ + } + txt.print_uw(ww) + txt.nl() + for bc in 30 to 10 step -4 { + ; 30,26,22,18,14,10,6,2 -> +8 -> 12 + ww++ + } + txt.print_uw(ww) + txt.nl() + + sys.exit(99) diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index eeeac7a7e..dd2fbfbba 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -220,6 +220,7 @@ data class Instruction( val reg2: Int?=null, // 0-$ffff val reg3: Int?=null, // 0-$ffff val value: Int?=null, // 0-$ffff +// TODO add string symbol here as alternative to value ) { override fun toString(): String { val result = mutableListOf(opcode.name.lowercase())