merge asmsub and normal sub

This commit is contained in:
Irmen de Jong 2018-10-03 00:25:04 +02:00
parent 34fb82969c
commit 4df397d057
5 changed files with 67 additions and 73 deletions

View File

@ -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
}

View File

@ -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<SubroutineParameter>,
val returnvalues: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>,
val asmAddress: Int?,
override var statements: MutableList<IStatement>,
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<AsmSubroutineParameter>,
val returns: List<AsmSubroutineReturn>,
val clobbers: Set<Register>,
val statements: MutableList<IStatement>,
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<IStatement>,
var elsepart: List<IStatement>,
@ -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<Register>
= 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())
}

View File

@ -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))

View File

@ -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)
}

View File

@ -246,18 +246,14 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
val continueStmtLabelStack : Stack<String> = 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")
}