From 4df397d0570a2ab8218a5047b06471bc9589cd7c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 3 Oct 2018 00:25:04 +0200 Subject: [PATCH] merge asmsub and normal sub --- compiler/examples/test.p8 | 13 ++++ compiler/src/prog8/ast/AST.kt | 72 +++++++------------ compiler/src/prog8/ast/AstChecker.kt | 18 +++-- .../src/prog8/ast/AstIdentifiersChecker.kt | 18 ++--- compiler/src/prog8/compiler/Compiler.kt | 19 ++--- 5 files changed, 67 insertions(+), 73 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 508d88a63..851bf702a 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,7 +1,18 @@ %option enable_floats + + ~ main { +asmsub MOVFM (mflpt: word @ AY) -> clobbers(A,Y) -> () = $bba2 ; load mflpt value from memory in A/Y into fac1 +asmsub FREADMEM () -> clobbers(A,Y) -> () = $bba6 ; load mflpt value from memory in $22/$23 into fac1 +asmsub CONUPK (mflpt: word @ AY) -> clobbers(A,Y) -> () = $ba8c ; load mflpt value from memory in A/Y into fac2 +asmsub FAREADMEM () -> clobbers(A,Y) -> () = $ba90 ; load mflpt value from memory in $22/$23 into fac2 +asmsub MOVFA () -> clobbers(A,X) -> () = $bbfc ; copy fac2 to fac1 +asmsub MOVAF () -> clobbers(A,X) -> () = $bc0c ; copy fac1 to fac2 (rounded) +asmsub MOVEF () -> clobbers(A,X) -> () = $bc0f ; copy fac1 to fac2 +asmsub FTOMEMXY (mflpt: word @ XY) -> clobbers(A,Y) -> () = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt + sub start() { @@ -22,6 +33,8 @@ sub start() { fvar = bvar fvar = wvar + MOVAF() + return } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 24cf0fb34..5ef475060 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -217,10 +217,6 @@ interface IAstProcessor { return returnStmt } - fun process(asmSubroutine: AsmSubroutine): IStatement { - return asmSubroutine - } - fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression { arrayIndexedExpression.identifier?.process(this) arrayIndexedExpression.array.process(this) @@ -1314,15 +1310,20 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS } +class RegisterOrStatusflag(val register: Register?, val statusflag: Statusflag?) + class Subroutine(override val name: String, val parameters: List, val returnvalues: List, + val asmParameterRegisters: List, + val asmReturnvaluesRegisters: List, + val asmClobbers: Set, + val asmAddress: Int?, override var statements: MutableList, override val position: Position) : IStatement, INameScope { override lateinit var parent: Node val scopedname: String by lazy { makeScopedName(name).joinToString(".") } - override fun linkParents(parent: Node) { this.parent = parent parameters.forEach { it.linkParents(this) } @@ -1332,7 +1333,7 @@ class Subroutine(override val name: String, override fun process(processor: IAstProcessor) = processor.process(this) override fun toString(): String { - return "Subroutine(name=$name, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements)" + return "Subroutine(name=$name, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements, address=$asmAddress)" } override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes") @@ -1349,45 +1350,6 @@ open class SubroutineParameter(val name: String, } } - -// @todo merge this with normal Subroutine? -class AsmSubroutine(val name: String, - val address: Int?, - val params: List, - val returns: List, - val clobbers: Set, - val statements: MutableList, - override val position: Position) : IStatement { - override lateinit var parent: Node - - override fun linkParents(parent: Node) { - this.parent = parent - params.forEach { it.linkParents(this) } - returns.forEach { it.linkParents(this) } - statements.forEach { it.linkParents(this) } - } - - override fun process(processor: IAstProcessor) = processor.process(this) -} - -class AsmSubroutineParameter(name: String, - type: DataType, - val register: Register?, - val statusflag: Statusflag?, - position: Position) : SubroutineParameter(name, type, position) - - -class AsmSubroutineReturn(val type: DataType, - val register: Register?, - val statusflag: Statusflag?, - override val position: Position): Node { - override lateinit var parent: Node - override fun linkParents(parent: Node) { - this.parent = parent - } -} - - class IfStatement(var condition: IExpression, var statements: List, var elsepart: List, @@ -1619,11 +1581,25 @@ private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement { val address = asmsub_address()?.address?.toAst()?.number?.toInt() val params = asmsub_params()?.toAst() ?: emptyList() val returns = asmsub_returns()?.toAst() ?: emptyList() + val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) } + val normalReturnvalues = returns.map { it.type } + val paramRegisters = params.map { RegisterOrStatusflag(it.register, it.statusflag) } + val returnRegisters = returns.map { RegisterOrStatusflag(it.register, it.statusflag) } val clobbers = clobber()?.toAst() ?: emptySet() val statements = statement_block()?.toAst() ?: mutableListOf() - return AsmSubroutine(name, address, params, returns, clobbers, statements, toPosition()) + return Subroutine(name, normalParameters, normalReturnvalues, paramRegisters, returnRegisters, clobbers, address, statements, toPosition()) } +private class AsmSubroutineParameter(name: String, + type: DataType, + val register: Register?, + val statusflag: Statusflag?, + position: Position) : SubroutineParameter(name, type, position) + +private class AsmSubroutineReturn(val type: DataType, + val register: Register?, + val statusflag: Statusflag?, + val position: Position) private fun prog8Parser.ClobberContext.toAst(): Set = this.register().asSequence().map { it.toAst() }.toSet() @@ -1686,6 +1662,10 @@ private fun prog8Parser.SubroutineContext.toAst() : Subroutine { return Subroutine(identifier().text, sub_params()?.toAst() ?: emptyList(), sub_return_part()?.toAst() ?: emptyList(), + emptyList(), + emptyList(), + emptySet(), + null, statement_block()?.toAst() ?: mutableListOf(), toPosition()) } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index bb6561b49..6d10f2ca6 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -82,8 +82,9 @@ class AstChecker(private val namespace: INameScope, override fun process(returnStmt: Return): IStatement { val expectedReturnValues = (returnStmt.definingScope() as? Subroutine)?.returnvalues ?: emptyList() - if(expectedReturnValues.size != returnStmt.values.size) + if(expectedReturnValues.size != returnStmt.values.size) { checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position)) + } for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { if(rv.first.value!=rv.second.resultingDatatype(namespace, heap)) @@ -210,10 +211,14 @@ class AstChecker(private val namespace: INameScope, .map { (it as InlineAssembly).assembly } .count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it } if (amount == 0) { - if(subroutine.returnvalues.isNotEmpty()) - err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") - // if there's no return statement, we add the implicit one at the end. - subroutine.statements.add(Return(emptyList(), subroutine.position)) + if(subroutine.returnvalues.isNotEmpty()) { + // for asm subroutines with an address, no statement check is possible. + if(subroutine.asmAddress==null) + err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") + } + // if there's no return statement, we add the implicit one at the end, but only if it's not a kernel routine. + if(subroutine.asmAddress==null) + subroutine.statements.add(Return(emptyList(), subroutine.position)) } } @@ -231,7 +236,7 @@ class AstChecker(private val namespace: INameScope, var checkNext = false for (stmt in statements) { if(checkNext) { - if(stmt !is Label && stmt !is Subroutine && stmt !is AsmSubroutine) + if(stmt !is Label && stmt !is Subroutine) checkResult.add(SyntaxError("preceding subroutine definition at line ${preceding.position.line} must be followed here by a label, another subroutine statement, or nothing", stmt.position)) return } @@ -239,7 +244,6 @@ class AstChecker(private val namespace: INameScope, if(preceding !is Return && preceding !is Jump && preceding !is Subroutine - && preceding !is AsmSubroutine && preceding !is VarDecl && preceding !is BuiltinFunctionStatementPlaceholder) { checkResult.add(SyntaxError("subroutine definition must be preceded by a return, jump, vardecl, or another subroutine statement", stmt.position)) diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 6a6963ea6..d7cff9e9b 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -86,14 +86,16 @@ class AstIdentifiersChecker : IAstProcessor { nameError(name.key, name.value.position, subroutine) } - // inject subroutine params as local variables (if they're not there yet) - subroutine.parameters - .filter { !definedNames.containsKey(it.name) } - .forEach { - val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) - vardecl.linkParents(subroutine) - subroutine.statements.add(0, vardecl) - } + // inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines) + if(subroutine.asmAddress==null) { + subroutine.parameters + .filter { !definedNames.containsKey(it.name) } + .forEach { + val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) + vardecl.linkParents(subroutine) + subroutine.statements.add(0, vardecl) + } + } } return super.process(subroutine) } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 75b5fbce7..ad06c32b9 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -246,18 +246,14 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, val continueStmtLabelStack : Stack = Stack() override fun process(subroutine: Subroutine): IStatement { - stackvmProg.label(subroutine.scopedname) - // note: the caller has already written the arguments into the subroutine's parameter variables. - translate(subroutine.statements) - return super.process(subroutine) - } - - override fun process(asmSubroutine: AsmSubroutine): IStatement { - if(asmSubroutine.statements.isNotEmpty()) { - stackvmProg.label(asmSubroutine.makeScopedName(asmSubroutine.name).joinToString(".")) - translate(asmSubroutine.statements) + if(subroutine.asmAddress==null) { + stackvmProg.label(subroutine.scopedname) + // note: the caller has already written the arguments into the subroutine's parameter variables. + translate(subroutine.statements) + } else { + throw CompilerException("kernel subroutines (with memory address and no body) are not supported by StackVM: $subroutine") } - return super.process(asmSubroutine) + return super.process(subroutine) } override fun process(block: Block): IStatement { @@ -297,7 +293,6 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, is WhileLoop -> translate(stmt) is RepeatLoop -> translate(stmt) is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these. - is AsmSubroutine -> {} is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM") else -> TODO("translate statement $stmt to stackvm") }