From 7c79cdbd2f32a8c3fbe4eeaad896df88a5534482 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 17 Dec 2024 12:52:01 +0100 Subject: [PATCH] fix symbol prefixing on goto with expression added coroutines example --- codeCore/src/prog8/code/ast/AstStatements.kt | 7 +- .../src/prog8/codegen/cpu6502/AsmGen.kt | 22 --- .../prog8/codegen/intermediate/IRCodeGen.kt | 1 + codeGenIntermediate/test/TestVmCodeGen.kt | 8 +- codeOptimizers/src/prog8/optimizer/Inliner.kt | 2 +- .../astprocessing/IntermediateAstMaker.kt | 6 +- .../IntermediateAstPostprocess.kt | 4 +- compiler/test/TestCompilerOnExamples.kt | 1 + docs/source/todo.rst | 6 +- examples/coroutines.p8 | 128 ++++++++++++++++++ examples/test.p8 | 7 +- 11 files changed, 149 insertions(+), 43 deletions(-) create mode 100644 examples/coroutines.p8 diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index d15985c8e..16044aff3 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -116,10 +116,9 @@ class PtIfElse(position: Position) : PtNode(position) { } -class PtJump(val target: PtExpression, position: Position) : PtNode(position) { - init { - target.parent = this - } +class PtJump(position: Position) : PtNode(position) { + val target: PtExpression + get() = children.single() as PtExpression } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index b97d4b7aa..8f79ee685 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -73,16 +73,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque nodesToPrefix += node.parent to index } } - is PtJump -> { - val identifier = node.target as? PtIdentifier - if(identifier!=null) { - val stNode = st.lookup(identifier.name) ?: throw AssemblyError("name not found $identifier") - if (stNode.astNode.definingBlock()?.options?.noSymbolPrefixing != true) { - val index = node.parent.children.indexOf(node) - nodesToPrefix += node.parent to index - } - } - } is PtBlock -> prefixNamedNode(node) is PtConstant -> prefixNamedNode(node) is PtLabel -> prefixNamedNode(node) @@ -107,7 +97,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque when(val node = parent.children[index]) { is PtIdentifier -> parent.children[index] = node.prefix(parent, st) is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last") - is PtJump -> parent.children[index] = node.prefix(parent, st) is PtVariable -> parent.children[index] = node.prefix(parent, st) else -> throw AssemblyError("weird node to prefix $node") } @@ -177,17 +166,6 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable { else this } -private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump { - val identifier = target as? PtIdentifier - if(identifier!=null) { - val prefixedIdent = identifier.prefix(this, st) - val jump = PtJump(prefixedIdent, position) - jump.parent = parent - return jump - } - return this -} - private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall { val newName = prefixScopedName(name, 's') val call = PtFunctionCall(newName, void, type, position) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index d1e60875b..e2a65f196 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1660,6 +1660,7 @@ class IRCodeGen( IRInstruction(Opcode.JUMP, labelSymbol = identifier.name) } } else { + // val tr = expressionEval.translateExpression(jump.target) TODO("JUMP to expression address ${jump.target}") } } diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 1b56592f7..a82547763 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -312,7 +312,7 @@ class TestVmCodeGen: FunSpec({ cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) if1.add(cmp1) - if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if1.add(PtNodeGroup()) sub.add(if1) val if2 = PtIfElse(Position.DUMMY) @@ -320,7 +320,7 @@ class TestVmCodeGen: FunSpec({ cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) if2.add(cmp2) - if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if2.add(PtNodeGroup()) sub.add(if2) block.add(sub) @@ -505,7 +505,7 @@ class TestVmCodeGen: FunSpec({ cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY)) if1.add(cmp1) - if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if1.add(PtNodeGroup()) sub.add(if1) val if2 = PtIfElse(Position.DUMMY) @@ -513,7 +513,7 @@ class TestVmCodeGen: FunSpec({ cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY)) if2.add(cmp2) - if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if2.add(PtNodeGroup()) sub.add(if2) block.add(sub) diff --git a/codeOptimizers/src/prog8/optimizer/Inliner.kt b/codeOptimizers/src/prog8/optimizer/Inliner.kt index c63baea06..ab4041f0e 100644 --- a/codeOptimizers/src/prog8/optimizer/Inliner.kt +++ b/codeOptimizers/src/prog8/optimizer/Inliner.kt @@ -102,7 +102,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti inline } - is Jump -> true + is Jump -> stmt.target is NumericLiteral || stmt.target is IdentifierReference else -> false } } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index c5aae2980..7f25fa1b4 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -378,7 +378,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr val scopedEndLabel = (srcIf.definingScope.scopedName + endLabel).joinToString(".") val elseLbl = PtIdentifier(scopedElseLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position) val endLbl = PtIdentifier(scopedEndLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position) - ifScope.add(PtJump(elseLbl, srcIf.position)) + ifScope.add(PtJump(srcIf.position).also { it.add(elseLbl) }) val elseScope = PtNodeGroup() branch.add(ifScope) branch.add(elseScope) @@ -386,7 +386,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr for (stmt in srcIf.truepart.statements) nodes.add(transformStatement(stmt)) if(srcIf.elsepart.isNotEmpty()) - nodes.add(PtJump(endLbl, srcIf.position)) + nodes.add(PtJump(srcIf.position).also { it.add(endLbl) }) nodes.add(PtLabel(elseLabel, srcIf.position)) if(srcIf.elsepart.isNotEmpty()) { for (stmt in srcIf.elsepart.statements) @@ -451,7 +451,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr private fun transform(srcJump: Jump): PtJump { val target = transformExpression(srcJump.target) - return PtJump(target, srcJump.position) + return PtJump(srcJump.position).also { it.add(target) } } private fun transform(label: Label): PtLabel = diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt index 6a0bca7f1..69114dbd6 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt @@ -189,7 +189,9 @@ private fun integrateDefers(subdefers: Map>, program: PtPro val skiplabel = "prog8_defer_skip_${idx+1}" val branchcc = PtConditionalBranch(BranchCondition.CC, Position.DUMMY) branchcc.add(PtNodeGroup().also { - it.add(PtJump(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY), Position.DUMMY)) + val jump = PtJump(Position.DUMMY) + jump.add(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)) + it.add(jump) }) branchcc.add(PtNodeGroup()) defersRoutine.add(branchcc) diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 47ea0e57e..e4b2a30d2 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -171,6 +171,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({ listOf( "animals", "balls", + "coroutines", "cube3d", "cube3d-float", "cube3d-gfx", diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 577044bce..2ca281568 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,10 +1,6 @@ TODO ==== -- FIX: uword[] @nosplit tasklist = [&start-1] "initialisation value has incompatible type" -- FIX: goto pointers[4]-1 - - - DONE: make word arrays split by default and add new @nosplit tag to make an array use the old linear storage format - DONE: &splitarray will give you the start address of the lsb-array (which is immediately followed by the msb-array) - DONE: add &< and &> operators to get the address of the lsb-array and msb-array, respectively. (&< is just syntactic sugar for &) @@ -58,7 +54,7 @@ IR/VM - fix call() return value handling - fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported") - proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg -- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address +- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address" . This needs a change in the JUMPI instruction: it has to take a register instead (like CALLI) - idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype) global initialization values are simply a list of LOAD instructions. Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings. diff --git a/examples/coroutines.p8 b/examples/coroutines.p8 new file mode 100644 index 000000000..162bff031 --- /dev/null +++ b/examples/coroutines.p8 @@ -0,0 +1,128 @@ + +; Cooperative multitasking / Coroutines +; Uses stack return address juggling to cycle between the different tasks when they call yield(). + +%import textio + + +main { + sub start() { + txt.print("cooperative multitasking / coroutines") + coroutines.start() + } +} + +coroutines { + uword[] tasklist = [&task1, &task2, &task3, &task4, &vsynctask] + + sub start() { + goto tasklist[0] + } + + uword[len(tasklist)] returnaddresses + ubyte active_task + + sub yield() { + ; store the return address of the yielding task, + ; and continue with the next one instead (round-robin) + returnaddresses[active_task] = sys.popw() + active_task++ + if active_task==len(returnaddresses) + active_task=0 + cx16.r0 = returnaddresses[active_task] + if cx16.r0==0 { + ; fetch start address of next task. + ; address on the stack must be pushed in reverse byte order + ; also, subtract 1 from the start address because JSR pushes returnaddress minus 1 + cx16.r0 = tasklist[active_task]-1 + sys.push(cx16.r0H) + sys.push(cx16.r0L) + } else + sys.pushw(cx16.r0) + + ; returning from yield then continues with the next coroutine + } + + sub task1() { + const ubyte x = 5 + ubyte y + repeat { + for y in 10 to 24 { + txt.setchr(x, y-1, sc:' ') + txt.setchr(x, y, sc:'1') + yield() + } + for y in 24 downto 10 { + txt.setchr(x, y+1, sc:' ') + txt.setchr(x, y, sc:'1') + yield() + } + } + ; need infinite loop + } + + sub task2() { + const ubyte x = 10 + ubyte y + repeat { + for y in 5 to 18 { + txt.setchr(x, y-1, sc:' ') + txt.setchr(x, y, sc:'2') + yield() + } + for y in 18 downto 5 { + txt.setchr(x, y+1, sc:' ') + txt.setchr(x, y, sc:'2') + yield() + } + } + ; need infinite loop + } + + sub task3() { + ubyte x + const ubyte y = 10 + repeat { + for x in 14 to 38 { + txt.setchr(x-1, y, sc:' ') + txt.setchr(x, y, sc:'3') + yield() + } + for x in 38 downto 14 { + txt.setchr(x+1, y, sc:' ') + txt.setchr(x, y, sc:'3') + yield() + } + } + ; need infinite loop + } + + sub task4() { + ubyte x + const ubyte y = 14 + repeat { + for x in 15 to 30 { + txt.setchr(x-1, y, sc:' ') + txt.setchr(x, y, sc:'4') + yield() + } + for x in 30 downto 15 { + txt.setchr(x+1, y, sc:' ') + txt.setchr(x, y, sc:'4') + yield() + } + } + ; need infinite loop + } + + sub vsynctask() { + repeat { + sys.waitvsync() + sys.waitvsync() + yield() + } + ; need infinite loop + } +} + + diff --git a/examples/test.p8 b/examples/test.p8 index 21651ad4d..db13a30a5 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,7 +1,8 @@ main { sub start() { - uword[] @nosplit tasklist = [&start-1] -; uword task_address = tasklist[0] -; goto task_address + uword[] tasklist = [1111,2222,3333] + uword task_address = tasklist[0] + goto task_address + goto task_address+1 } }