From 8095c4c155625a37c690f6bd4e442b5f2f46a87b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 21 Nov 2021 16:23:48 +0100 Subject: [PATCH 1/3] added GoSub node (internal use only later for calling subroutines) --- .../compiler/target/cpu6502/codegen/AsmGen.kt | 9 ++- .../src/prog8/optimizer/StatementOptimizer.kt | 13 +-- .../compiler/astprocessing/AstChecker.kt | 6 +- .../src/prog8/ast/AstToSourceTextConverter.kt | 5 +- .../src/prog8/ast/statements/AstStatements.kt | 81 +++++++------------ examples/test.p8 | 31 +++---- 6 files changed, 64 insertions(+), 81 deletions(-) diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index 2633e8695..289166bd8 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -1298,6 +1298,8 @@ $repeatLabel lda $counterVar val jump = stmt.truepart.statements.first() as? Jump if(jump!=null) { + if(jump.isGosub) + throw FatalAstException("didn't expect GoSub here") // branch with only a jump (goto) val instruction = branchInstruction(stmt.condition, false) out(" $instruction ${getJumpTarget(jump)}") @@ -1387,7 +1389,12 @@ $label nop""") } } - private fun translate(jump: Jump) = jmp(getJumpTarget(jump)) + private fun translate(jump: Jump) { + if(jump.isGosub) + out(" jsr ${getJumpTarget(jump)}") + else + jmp(getJumpTarget(jump)) + } private fun getJumpTarget(jump: Jump): String { val ident = jump.identifier diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index 6d3851a47..1814e387b 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -277,12 +277,13 @@ class StatementOptimizer(private val program: Program, } override fun after(jump: Jump, parent: Node): Iterable { - // if the jump is to the next statement, remove the jump - val scope = jump.parent as IStatementContainer - val label = jump.identifier?.targetStatement(program) - if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) - return listOf(IAstModification.Remove(jump, scope)) - + if(!jump.isGosub) { + // if the jump is to the next statement, remove the jump + val scope = jump.parent as IStatementContainer + val label = jump.identifier?.targetStatement(program) + if (label != null && scope.statements.indexOf(label) == scope.statements.indexOf(jump) + 1) + return listOf(IAstModification.Remove(jump, scope)) + } return noModifications } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 5cc40b4fb..5d2e672df 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -169,6 +169,9 @@ internal class AstChecker(private val program: Program, if(targetStatement is BuiltinFunctionStatementPlaceholder) errors.err("can't jump to a builtin function", jump.position) } + if(!jump.isGosub && targetStatement is Subroutine && targetStatement.parameters.any()) { + errors.err("can't jump to a subroutine that takes parameters", jump.position) + } } val addr = jump.address @@ -224,7 +227,8 @@ internal class AstChecker(private val program: Program, count++ } override fun visit(jump: Jump) { - count++ + if(!jump.isGosub) + count++ } } diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 5309db836..deb9196b5 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -219,7 +219,10 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: } override fun visit(jump: Jump) { - output("goto ") + if(jump.isGosub) + output("gosub ") + else + output("goto ") when { jump.address!=null -> output(jump.address.toHex()) jump.generatedLabel!=null -> output(jump.generatedLabel) diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index b7d8d730f..29bdd5494 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -92,10 +92,7 @@ class Block(override val name: String, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "Block(name=$name, address=$address, ${statements.size} statements)" - } + override fun toString() = "Block(name=$name, address=$address, ${statements.size} statements)" fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet() } @@ -135,10 +132,7 @@ data class Label(override val name: String, override val position: Position) : S override fun copy() = Label(name, position) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "Label(name=$name, pos=$position)" - } + override fun toString()= "Label(name=$name, pos=$position)" } open class Return(var value: Expression?, final override val position: Position) : Statement() { @@ -158,10 +152,7 @@ open class Return(var value: Expression?, final override val position: Position) override fun copy() = Return(value?.copy(), position) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "Return($value, pos=$position)" - } + override fun toString() = "Return($value, pos=$position)" } class Break(override val position: Position) : Statement() { @@ -256,10 +247,8 @@ open class VarDecl(val type: VarDeclType, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" - } + override fun toString() = + "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" fun zeroElementValue(): NumericLiteralValue { if(allowInitializeWithZero) @@ -303,10 +292,7 @@ class ArrayIndex(var indexExpr: Expression, fun accept(visitor: IAstVisitor) = indexExpr.accept(visitor) fun accept(visitor: AstWalker) = indexExpr.accept(visitor, this) - - override fun toString(): String { - return("ArrayIndex($indexExpr, pos=$position)") - } + override fun toString() = "ArrayIndex($indexExpr, pos=$position)" fun constIndex() = (indexExpr as? NumericLiteralValue)?.number?.toInt() @@ -335,10 +321,7 @@ open class Assignment(var target: AssignTarget, var value: Expression, final ove override fun copy()= Assignment(target.copy(), value.copy(), position) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return("Assignment(target: $target, value: $value, pos=$position)") - } + override fun toString() = "Assignment(target: $target, value: $value, pos=$position)" /** * Is the assigment value an expression that references the assignment target itself? @@ -514,17 +497,15 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val override fun copy() = PostIncrDecr(target.copy(), operator, position) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "PostIncrDecr(op: $operator, target: $target, pos=$position)" - } + override fun toString() = "PostIncrDecr(op: $operator, target: $target, pos=$position)" } -class Jump(val address: UInt?, - val identifier: IdentifierReference?, - val generatedLabel: String?, // used in code generation scenarios - override val position: Position) : Statement() { +open class Jump(val address: UInt?, + val identifier: IdentifierReference?, + val generatedLabel: String?, // can be used in code generation scenarios + override val position: Position) : Statement() { override lateinit var parent: Node + open val isGosub = false override fun linkParents(parent: Node) { this.parent = parent @@ -536,9 +517,18 @@ class Jump(val address: UInt?, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - override fun toString(): String { - return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)" - } + override fun toString() = + "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)" +} + +// a GoSub is ONLY created internally for calling subroutines +class GoSub(address: UInt?, identifier: IdentifierReference?, generatedLabel: String?, position: Position) : + Jump(address, identifier, generatedLabel, position) { + + override val isGosub = true + override fun copy() = GoSub(address, identifier?.copy(), generatedLabel, position) + override fun toString() = + "GoSub(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)" } class FunctionCallStatement(override var target: IdentifierReference, @@ -567,7 +557,6 @@ class FunctionCallStatement(override var target: IdentifierReference, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - override fun toString() = "FunctionCallStatement(target=$target, pos=$position)" } @@ -695,10 +684,8 @@ class Subroutine(override val name: String, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" - } + override fun toString() = + "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } @@ -825,10 +812,7 @@ class ForLoop(var loopVar: IdentifierReference, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString(): String { - return "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)" - } + override fun toString() = "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)" fun loopVarDt(program: Program) = loopVar.inferType(program) } @@ -978,9 +962,7 @@ class WhenChoice(var values: MutableList?, // if null, th } override fun copy() = WhenChoice(values?.map{ it.copy() }?.toMutableList(), statements.copy(), position) - override fun toString(): String { - return "Choice($values at $position)" - } + override fun toString() = "Choice($values at $position)" fun accept(visitor: IAstVisitor) = visitor.visit(this) fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) @@ -1000,10 +982,7 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position replacement.parent = this } - override fun toString(): String { - return "DirectMemoryWrite($addressExpression)" - } - + override fun toString() = "DirectMemoryWrite($addressExpression)" fun accept(visitor: IAstVisitor) = visitor.visit(this) fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun copy() = DirectMemoryWrite(addressExpression.copy(), position) diff --git a/examples/test.p8 b/examples/test.p8 index da2cd9604..661aca586 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,30 +4,19 @@ main { sub start() { - repeat 100 { - random_rgb12() - txt.print_ubhex(target_red,false) - txt.print_ubhex(target_green,false) - txt.print_ubhex(target_blue,false) - txt.nl() - } + ubyte xx = 20 + routine(xx) + xx++ + routine(xx) + xx++ + routine(xx) repeat { } } - ubyte target_red - ubyte target_green - ubyte target_blue - - sub random_rgb12() { - do { - uword rr = rndw() - target_red = msb(rr) & 15 - target_green = lsb(rr) - target_blue = target_green & 15 - target_green >>= 4 - } until target_red+target_green+target_blue >= 12 - } - + sub routine(ubyte x) { + txt.print_ub(x) + txt.spc() + } } From 0e2e5ffa52944a944463029a09a7e35e314c8736 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 21 Nov 2021 22:12:35 +0100 Subject: [PATCH 2/3] fix parameter name conflict --- compiler/res/prog8lib/cx16/syslib.p8 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 1d6e60205..f6f15a647 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -367,14 +367,14 @@ romsub $fecc = monitor() clobbers(A,X,Y) ; ---- utilities ----- -inline asmsub rombank(ubyte rombank @A) { +inline asmsub rombank(ubyte bank @A) { ; -- set the rom banks %asm {{ sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38) }} } -inline asmsub rambank(ubyte rambank @A) { +inline asmsub rambank(ubyte bank @A) { ; -- set the ram bank %asm {{ sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38) From ff715881bca5e54f0e936fe64515cfe28e79c454 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 21 Nov 2021 23:21:39 +0100 Subject: [PATCH 3/3] allow scoped identifiers to reference a subroutine parameter directly. also for asmsubroutines, but the asm generation for that is not yet done. --- .../compiler/target/cpu6502/codegen/AsmGen.kt | 1 - .../codegen/assignment/AsmAssignment.kt | 12 +++++- codeGeneration/test/AsmGenSymbolsTests.kt | 6 +-- .../compiler/BeforeAsmGenerationAstChanger.kt | 18 +++++---- .../astprocessing/AstIdentifiersChecker.kt | 2 +- .../astprocessing/AstVariousTransforms.kt | 4 +- compiler/test/TestMemory.kt | 22 +++++------ compilerAst/src/prog8/ast/AstToplevel.kt | 28 ++++++++------ compilerAst/src/prog8/ast/Program.kt | 2 +- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 4 ++ .../src/prog8/ast/statements/AstStatements.kt | 38 ++++++++++++++++--- docs/source/todo.rst | 6 +++ examples/test.p8 | 28 +++++++++----- 13 files changed, 117 insertions(+), 54 deletions(-) diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index 289166bd8..6eb363063 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -720,7 +720,6 @@ class AsmGen(private val program: Program, internal fun translate(stmt: Statement) { outputSourceLine(stmt) when(stmt) { - is ParameterVarDecl -> { /* subroutine parameter vardecls don't get any special treatment here */ } is VarDecl -> translate(stmt) is NopStatement -> {} is Directive -> translate(stmt) diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AsmAssignment.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AsmAssignment.kt index 436ba603a..96f678e73 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AsmAssignment.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AsmAssignment.kt @@ -62,7 +62,13 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, throw AssemblyError("unknown dt") val dt = idt.getOr(DataType.UNDEFINED) when { - identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) + identifier != null -> { + val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter + if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine) { + TODO("ASSIGN ASMPARAM $parameter :: $assign") + } + AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) + } arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this) memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this) else -> throw AssemblyError("weird target") @@ -133,6 +139,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind, is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation") is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation") is IdentifierReference -> { + val parameter = value.targetVarDecl(program)?.subroutineParameter + if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine) { + TODO("ASSIGN SOURCE FROM ASMPARAM $parameter :: $value") + } val dt = value.inferType(program).getOr(DataType.UNDEFINED) val varName=asmgen.asmVariableName(value) // special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system diff --git a/codeGeneration/test/AsmGenSymbolsTests.kt b/codeGeneration/test/AsmGenSymbolsTests.kt index c371671a6..4748ba7fd 100644 --- a/codeGeneration/test/AsmGenSymbolsTests.kt +++ b/codeGeneration/test/AsmGenSymbolsTests.kt @@ -46,8 +46,8 @@ class AsmGenSymbolsTests: StringSpec({ } */ - val varInSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteralValue.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY) - val var2InSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, Position.DUMMY) + val varInSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteralValue.optimalInteger(1234, Position.DUMMY), false, false, false, null, Position.DUMMY) + val var2InSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, null, Position.DUMMY) val labelInSub = Label("locallabel", Position.DUMMY) val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY) @@ -63,7 +63,7 @@ class AsmGenSymbolsTests: StringSpec({ val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8) val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements, Position.DUMMY) val labelInBlock = Label("label_outside", Position.DUMMY) - val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY) + val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, null, Position.DUMMY) val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 9a7f3064e..5e54533f6 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -83,14 +83,18 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o IAstModification.ReplaceNode(it, uwordParam, subroutine) } - val stringParamNames = stringParams.map { it.name }.toSet() - val varsChanges = subroutine.statements - .filterIsInstance() - .filter { it.autogeneratedDontRemove && it.name in stringParamNames } - .map { - val newvar = VarDecl(it.type, DataType.UWORD, it.zeropage, null, it.name, null, false, true, it.sharedWithAsm, it.position) - IAstModification.ReplaceNode(it, newvar, subroutine) + val stringParamsByNames = stringParams.associateBy { it.name } + val varsChanges = + if(stringParamsByNames.isNotEmpty()) { + subroutine.statements + .filterIsInstance() + .filter { it.subroutineParameter!=null && it.name in stringParamsByNames } + .map { + val newvar = VarDecl(it.type, DataType.UWORD, it.zeropage, null, it.name, null, false, true, it.sharedWithAsm, stringParamsByNames.getValue(it.name), it.position) + IAstModification.ReplaceNode(it, newvar, subroutine) + } } + else emptyList() return parameterChanges + varsChanges } diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 9fc2462c7..7ed5fc743 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -75,7 +75,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, private val paramsToCheck = paramNames.intersect(namesInSub) for(name in paramsToCheck) { val symbol = subroutine.searchSymbol(name) - if(symbol!=null && symbol.position != subroutine.position) + if(symbol!=null && (symbol as? VarDecl)?.subroutineParameter==null) nameError(name, symbol.position, subroutine) } diff --git a/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt b/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt index 1b7d6a57d..73a9d7533 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt @@ -20,13 +20,13 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker() val symbolsInSub = subroutine.allDefinedSymbols val namesInSub = symbolsInSub.map{ it.first }.toSet() if(subroutine.asmAddress==null) { - if(subroutine.asmParameterRegisters.isEmpty() && subroutine.parameters.isNotEmpty()) { + if(!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty()) { val vars = subroutine.statements.filterIsInstance().map { it.name }.toSet() if(!vars.containsAll(subroutine.parameters.map{it.name})) { return subroutine.parameters .filter { it.name !in namesInSub } .map { - val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position) + val vardecl = VarDecl.fromParameter(it) IAstModification.InsertFirst(vardecl, subroutine) } } diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index 648ac99ca..2a6beae1f 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -110,7 +110,7 @@ class TestMemory: FunSpec({ } fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { - val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY) + val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, null, Position.DUMMY) val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) @@ -149,7 +149,7 @@ class TestMemory: FunSpec({ } test("regular variable not in mapped IO ram on C64") { - val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -160,8 +160,8 @@ class TestMemory: FunSpec({ } test("memory mapped variable not in mapped IO ram on C64") { - val address = 0x1000 - val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY) + val address = 0x1000u + val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -172,8 +172,8 @@ class TestMemory: FunSpec({ } test("memory mapped variable in mapped IO ram on C64") { - val address = 0xd020 - val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY) + val address = 0xd020u + val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) @@ -184,7 +184,7 @@ class TestMemory: FunSpec({ } test("array not in mapped IO ram") { - val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, null, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) @@ -196,8 +196,8 @@ class TestMemory: FunSpec({ } test("memory mapped array not in mapped IO ram") { - val address = 0x1000 - val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY) + val address = 0x1000u + val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, null, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) @@ -209,8 +209,8 @@ class TestMemory: FunSpec({ } test("memory mapped array in mapped IO ram") { - val address = 0xd800 - val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY) + val address = 0xd800u + val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, null, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 137bc015e..aee24c21e 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -7,7 +7,6 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstVisitor import prog8.parser.SourceCode -import kotlin.reflect.typeOf const val internedStringsModuleName = "prog8_interned_strings" @@ -69,23 +68,28 @@ interface IStatementContainer { fun isNotEmpty(): Boolean = statements.isNotEmpty() fun searchSymbol(name: String): Statement? { + if(this is Subroutine && isAsmSubroutine) + return searchAsmParameter(name) + // this is called quite a lot and could perhaps be optimized a bit more, // but adding a memoization cache didn't make much of a practical runtime difference... for (stmt in statements) { when(stmt) { -// is INamedStatement -> { + is INamedStatement -> { + if(stmt.name==name) return stmt + } +// is VarDecl -> { +// // a variable was found with this name, which could also be the vardecl +// // that is (auto)generated for regular subroutine parameters. // if(stmt.name==name) return stmt // } - is VarDecl -> { - if(stmt.name==name) return stmt - } - is Label -> { - if(stmt.name==name) return stmt - } - is Subroutine -> { - if(stmt.name==name) - return stmt - } +// is Label -> { +// if(stmt.name==name) return stmt +// } +// is Subroutine -> { +// if(stmt.name==name) +// return stmt +// } is AnonymousScope -> { val found = stmt.searchSymbol(name) if(found!=null) diff --git a/compilerAst/src/prog8/ast/Program.kt b/compilerAst/src/prog8/ast/Program.kt index 22af56151..ffd06837e 100644 --- a/compilerAst/src/prog8/ast/Program.kt +++ b/compilerAst/src/prog8/ast/Program.kt @@ -94,7 +94,7 @@ class Program(val name: String, val varName = "string_${internedStringsBlock.statements.size}" val decl = VarDecl( VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string, - isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position + isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, subroutineParameter = null, position = string.position ) internedStringsBlock.statements.add(decl) decl.linkParents(internedStringsBlock) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index fc9660010..ff673fd17 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -64,6 +64,7 @@ private fun Prog8ANTLRParser.VariabledeclarationContext.toAst() : Statement { vd.ARRAYSIG() != null || vd.arrayindex() != null, false, vd.SHARED()!=null, + null, it.toPosition() ) } @@ -81,6 +82,7 @@ private fun Prog8ANTLRParser.VariabledeclarationContext.toAst() : Statement { vd.ARRAYSIG() != null || vd.arrayindex() != null, false, vd.SHARED() != null, + null, cvarinit.toPosition() ) } @@ -98,6 +100,7 @@ private fun Prog8ANTLRParser.VariabledeclarationContext.toAst() : Statement { vd.ARRAYSIG() != null || vd.arrayindex() != null, false, vd.SHARED()!=null, + null, mvarinit.toPosition() ) } @@ -600,6 +603,7 @@ private fun Prog8ANTLRParser.VardeclContext.toAst(): VarDecl { ARRAYSIG() != null || arrayindex() != null, false, SHARED()!=null, + null, toPosition() ) } diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 29bdd5494..05e9aea1a 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -185,6 +185,7 @@ open class VarDecl(val type: VarDeclType, val isArray: Boolean, val autogeneratedDontRemove: Boolean, val sharedWithAsm: Boolean, + val subroutineParameter: SubroutineParameter?, final override val position: Position) : Statement(), INamedStatement { override lateinit var parent: Node var allowInitializeWithZero = true @@ -194,6 +195,16 @@ open class VarDecl(val type: VarDeclType, companion object { private var autoHeapValueSequenceNumber = 0 + fun fromParameter(param: SubroutineParameter): VarDecl { + return VarDecl(VarDeclType.VAR, param.type, ZeropageWish.DONTCARE, null, param.name, null, + isArray = false, + autogeneratedDontRemove = true, + sharedWithAsm = false, + subroutineParameter = param, + position = param.position + ) + } + fun createAuto(array: ArrayLiteralValue): VarDecl { val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" val arrayDt = @@ -204,7 +215,7 @@ open class VarDecl(val type: VarDeclType, val declaredType = ArrayToElementTypes.getValue(arrayDt) val arraysize = ArrayIndex.forArray(array) return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array, - isArray = true, autogeneratedDontRemove = true, sharedWithAsm = false, position = array.position) + isArray = true, autogeneratedDontRemove = true, sharedWithAsm = false, subroutineParameter = null, position = array.position) } fun defaultZero(dt: DataType, position: Position) = when(dt) { @@ -258,16 +269,13 @@ open class VarDecl(val type: VarDeclType, } override fun copy(): VarDecl { - val c = VarDecl(type, declaredDatatype, zeropage, arraysize?.copy(), name, value?.copy(), isArray, autogeneratedDontRemove, sharedWithAsm, position) + val c = VarDecl(type, declaredDatatype, zeropage, arraysize?.copy(), name, value?.copy(), + isArray, autogeneratedDontRemove, sharedWithAsm, subroutineParameter, position) c.allowInitializeWithZero = this.allowInitializeWithZero return c } } -// a vardecl used only for subroutine parameters -class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position) - : VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, false, true, false, position) - class ArrayIndex(var indexExpr: Expression, override val position: Position) : Node { override lateinit var parent: Node @@ -709,6 +717,24 @@ class Subroutine(override val name: String, .filter { it is InlineAssembly } .map { (it as InlineAssembly).assembly } .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it} + + + // code to provide the ability to reference asmsub parameters via qualified name: + private val asmParamsDecls = mutableMapOf() + + fun searchAsmParameter(name: String): VarDecl? { + require(isAsmSubroutine) + + val existingDecl = asmParamsDecls[name] + if(existingDecl!=null) + return existingDecl + + val param = parameters.firstOrNull {it.name==name} ?: return null + val decl = VarDecl.fromParameter(param) + decl.linkParents(this) + asmParamsDecls[name] = decl + return decl + } } open class SubroutineParameter(val name: String, diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 91127ae79..2811298e5 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,12 @@ TODO For next compiler release (7.4) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use GoSub to call subroutines (not in expressions?): + - [DONE] allow separate assigns to subroutine's parameter variables / registers + - for asmsubs: implement asmgen for assigning to asm parameter (register) + - for asmsubs: implement asmgen for reading asm parameter (register) + - replace subroutine param load code with just the right order of those assigns + - finally replace the actual call with a GoSub ... diff --git a/examples/test.p8 b/examples/test.p8 index 661aca586..bbda586da 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,22 +1,32 @@ -%import textio main { sub start() { + ubyte @shared xx + main.routine.r1arg = 20 + ; main.routine2.r2arg = 20 ; TODO asmgen - ubyte xx = 20 - routine(xx) + xx = main.routine.r1arg xx++ - routine(xx) - xx++ - routine(xx) + ;xx = main.routine2.r2arg ; TODO asmgen + ;xx++ + printstuff("hello") repeat { } } - sub routine(ubyte x) { - txt.print_ub(x) - txt.spc() + sub printstuff(str addr) { + } + sub routine(ubyte r1arg) { + r1arg++ + } + + asmsub routine2(ubyte r2arg @ A) { + %asm {{ + rts + }} + } + }