mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
working on vm
This commit is contained in:
parent
30cbb6c9a8
commit
bb1bf6a88c
@ -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() {
|
||||
|
@ -11,7 +11,7 @@ sealed class PtNode(val position: Position) {
|
||||
val children = mutableListOf<PtNode>()
|
||||
lateinit var parent: PtNode
|
||||
|
||||
protected fun printIndented(indent: Int) {
|
||||
fun printIndented(indent: Int) {
|
||||
print(" ".repeat(indent))
|
||||
print("${this.javaClass.simpleName} ")
|
||||
printProperties()
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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()``
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user