mirror of
https://github.com/irmen/prog8.git
synced 2025-01-15 08:30:05 +00:00
tweak subroutine order
This commit is contained in:
parent
7d77504335
commit
2e4b5768b0
@ -4,15 +4,32 @@
|
|||||||
|
|
||||||
~ main {
|
~ 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 sub1() -> byte {
|
||||||
|
return 11
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub sub2() -> byte {
|
||||||
|
return 22
|
||||||
|
}
|
||||||
|
|
||||||
|
A=55
|
||||||
|
|
||||||
|
sub sub3() -> byte {
|
||||||
|
return 33
|
||||||
|
}
|
||||||
|
|
||||||
|
X=34
|
||||||
|
|
||||||
|
sub sub4() -> byte {
|
||||||
|
return 44
|
||||||
|
}
|
||||||
|
|
||||||
|
A=sub1()
|
||||||
|
A=sub2()
|
||||||
|
A=sub3()
|
||||||
|
A=sub4()
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
@ -22,20 +39,38 @@ sub start() {
|
|||||||
|
|
||||||
bvar = 1
|
bvar = 1
|
||||||
bvar = 2.0
|
bvar = 2.0
|
||||||
;bvar = 2.w ; @todo don't crash
|
bvar = 2.w
|
||||||
wvar = 1 ; @todo optimize byte literal to word literal
|
bvar = 255.w
|
||||||
|
wvar = 1
|
||||||
wvar = 2.w
|
wvar = 2.w
|
||||||
wvar = 2.0
|
wvar = 2.0
|
||||||
wvar = bvar
|
wvar = bvar
|
||||||
fvar = 1 ; @todo optimize byte literal to float literal
|
fvar = 1
|
||||||
fvar = 2.w ; @todo optimize word literal to float literal
|
fvar = 2.w
|
||||||
fvar = 22.33
|
fvar = 22.33
|
||||||
fvar = bvar
|
fvar = bvar
|
||||||
fvar = wvar
|
fvar = wvar
|
||||||
|
|
||||||
MOVAF()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
A=sub1()
|
||||||
|
A=sub2()
|
||||||
|
A=sub3()
|
||||||
|
A=sub4()
|
||||||
|
sub4()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~ test {
|
||||||
|
|
||||||
|
sub test() -> byte {
|
||||||
|
return 44
|
||||||
|
}
|
||||||
|
sub test2() -> byte {
|
||||||
|
return 43
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ fun main(args: Array<String>) {
|
|||||||
val heap = HeapValues()
|
val heap = HeapValues()
|
||||||
moduleAst.checkIdentifiers()
|
moduleAst.checkIdentifiers()
|
||||||
moduleAst.constantFold(namespace, heap)
|
moduleAst.constantFold(namespace, heap)
|
||||||
|
StatementReorderer().process(moduleAst) // reorder statements to please the compiler later
|
||||||
moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid
|
moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid
|
||||||
|
|
||||||
// optimize the parse tree
|
// optimize the parse tree
|
||||||
@ -68,7 +69,6 @@ fun main(args: Array<String>) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
StatementReorderer().process(moduleAst) // reorder statements to please the compiler later
|
|
||||||
namespace = moduleAst.definingScope() // create it again, it could have changed in the meantime
|
namespace = moduleAst.definingScope() // create it again, it could have changed in the meantime
|
||||||
moduleAst.checkValid(namespace, compilerOptions, heap) // check if final tree is valid
|
moduleAst.checkValid(namespace, compilerOptions, heap) // check if final tree is valid
|
||||||
moduleAst.checkRecursion(namespace) // check if there are recursive subroutine calls
|
moduleAst.checkRecursion(namespace) // check if there are recursive subroutine calls
|
||||||
|
@ -171,7 +171,6 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
checkResult.add(SyntaxError("block memory address must be valid integer 0..\$ffff", block.position))
|
checkResult.add(SyntaxError("block memory address must be valid integer 0..\$ffff", block.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(block.statements)
|
|
||||||
return super.process(block)
|
return super.process(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +199,6 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
err("parameter names must be unique")
|
err("parameter names must be unique")
|
||||||
|
|
||||||
super.process(subroutine)
|
super.process(subroutine)
|
||||||
checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(subroutine.statements)
|
|
||||||
|
|
||||||
// subroutine must contain at least one 'return' or 'goto'
|
// subroutine must contain at least one 'return' or 'goto'
|
||||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp')
|
// (or if it has an asm block, that must contain a 'rts' or 'jmp')
|
||||||
@ -230,30 +228,6 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(statements: MutableList<IStatement>) {
|
|
||||||
// @todo hmm, or move all the subroutines at the end of the block? (no fall-through execution)
|
|
||||||
var preceding: IStatement = BuiltinFunctionStatementPlaceholder("dummy", Position("<<dummy>>", 0, 0,0 ))
|
|
||||||
var checkNext = false
|
|
||||||
for (stmt in statements) {
|
|
||||||
if(checkNext) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if(stmt is Subroutine) {
|
|
||||||
if(preceding !is Return
|
|
||||||
&& preceding !is Jump
|
|
||||||
&& preceding !is Subroutine
|
|
||||||
&& 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))
|
|
||||||
}
|
|
||||||
checkNext=true
|
|
||||||
}
|
|
||||||
preceding = stmt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assignment target must be register, or a variable name
|
* Assignment target must be register, or a variable name
|
||||||
* Also check data type compatibility
|
* Also check data type compatibility
|
||||||
@ -336,7 +310,7 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now, variables can only be declared in a block or subroutine (not in a loop statement block) @todo fix this
|
// for now, variables can only be declared in a block or subroutine (not in a loop statement block) @todo fix this (anonymous namescope)
|
||||||
if(decl.parent !is Block && decl.parent !is Subroutine) {
|
if(decl.parent !is Block && decl.parent !is Subroutine) {
|
||||||
err ("variables must be declared at block or subroutine level")
|
err ("variables must be declared at block or subroutine level")
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@ class StatementReorderer: IAstProcessor {
|
|||||||
// -- the directives '%output', '%launcher', '%zeropage', '%address' and '%option' will come first.
|
// -- the directives '%output', '%launcher', '%zeropage', '%address' and '%option' will come first.
|
||||||
// -- all vardecls then follow.
|
// -- all vardecls then follow.
|
||||||
// -- the remaining statements then follow in their original order.
|
// -- the remaining statements then follow in their original order.
|
||||||
|
//
|
||||||
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
||||||
|
// - all other subroutines will be moved to the end of their block.
|
||||||
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%address", "%option")
|
||||||
|
|
||||||
@ -25,11 +27,42 @@ class StatementReorderer: IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun process(block: Block): IStatement {
|
override fun process(block: Block): IStatement {
|
||||||
val startSub = block.statements.singleOrNull {it is Subroutine && it.name=="start"}
|
val subroutines = block.statements.asSequence().filter { it is Subroutine }.map { it as Subroutine }.toList()
|
||||||
if(startSub!=null) {
|
var numSubroutinesAtEnd = 0
|
||||||
block.statements.remove(startSub)
|
// move all subroutines to the end of the block
|
||||||
block.statements.add(0, startSub)
|
for (subroutine in subroutines) {
|
||||||
|
if(subroutine.name!="start" || block.name!="main") {
|
||||||
|
block.statements.remove(subroutine)
|
||||||
|
block.statements.add(subroutine)
|
||||||
|
}
|
||||||
|
numSubroutinesAtEnd++
|
||||||
}
|
}
|
||||||
|
// move the "start" subroutine to the top
|
||||||
|
if(block.name=="main") {
|
||||||
|
block.statements.singleOrNull { it is Subroutine && it.name == "start" } ?.let {
|
||||||
|
block.statements.remove(it)
|
||||||
|
block.statements.add(0, it)
|
||||||
|
numSubroutinesAtEnd--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure there is a 'return' in front of the first subroutine
|
||||||
|
// (if it isn't the first statement in the block itself, and isn't the program's entrypoint)
|
||||||
|
if(block.statements.size > numSubroutinesAtEnd) {
|
||||||
|
val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine
|
||||||
|
if(firstSub.name != "start" && block.name != "main") {
|
||||||
|
val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1]
|
||||||
|
if (stmtBeforeFirstSub !is Return
|
||||||
|
&& stmtBeforeFirstSub !is Jump
|
||||||
|
&& stmtBeforeFirstSub !is Subroutine
|
||||||
|
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
|
||||||
|
val ret = Return(emptyList(), stmtBeforeFirstSub.position)
|
||||||
|
ret.linkParents(stmtBeforeFirstSub.parent)
|
||||||
|
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val varDecls = block.statements.filter { it is VarDecl }
|
val varDecls = block.statements.filter { it is VarDecl }
|
||||||
block.statements.removeAll(varDecls)
|
block.statements.removeAll(varDecls)
|
||||||
block.statements.addAll(0, varDecls)
|
block.statements.addAll(0, varDecls)
|
||||||
|
@ -537,7 +537,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
translateFunctionCall(funcname, expr.arglist)
|
translateFunctionCall(funcname, expr.arglist)
|
||||||
} else {
|
} else {
|
||||||
when(target) {
|
when(target) {
|
||||||
is Subroutine -> translateSubroutineCall(target, expr.arglist)
|
is Subroutine -> translateSubroutineCall(target, expr.arglist, expr.position)
|
||||||
else -> TODO("non-builtin-function call to $target")
|
else -> TODO("non-builtin-function call to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,7 +608,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
is Label ->
|
is Label ->
|
||||||
stackvmProg.instr(Opcode.CALL, callLabel = targetStmt.scopedname)
|
stackvmProg.instr(Opcode.CALL, callLabel = targetStmt.scopedname)
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
translateSubroutineCall(targetStmt, stmt.arglist)
|
translateSubroutineCall(targetStmt, stmt.arglist, stmt.position)
|
||||||
// make sure we clean up the unused result values from the stack.
|
// make sure we clean up the unused result values from the stack.
|
||||||
for(rv in targetStmt.returnvalues) {
|
for(rv in targetStmt.returnvalues) {
|
||||||
val opcode=opcodeDiscard(rv)
|
val opcode=opcodeDiscard(rv)
|
||||||
@ -649,8 +649,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>) {
|
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position) {
|
||||||
// evaluate the arguments and assign them into the subroutine's argument variables.
|
// evaluate the arguments and assign them into the subroutine's argument variables.
|
||||||
|
stackvmProg.line(callPosition)
|
||||||
for(arg in arguments.zip(subroutine.parameters)) {
|
for(arg in arguments.zip(subroutine.parameters)) {
|
||||||
translate(arg.first)
|
translate(arg.first)
|
||||||
val opcode=opcodePopvar(arg.second.type)
|
val opcode=opcodePopvar(arg.second.type)
|
||||||
|
@ -85,6 +85,8 @@ class Program (val name: String,
|
|||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
val (lineNr, line) = lines.next()
|
val (lineNr, line) = lines.next()
|
||||||
|
if(line.isEmpty())
|
||||||
|
continue
|
||||||
if(line=="%end_instructions")
|
if(line=="%end_instructions")
|
||||||
return Pair(instructions, labels)
|
return Pair(instructions, labels)
|
||||||
if(!line.startsWith(' ') && line.endsWith(':')) {
|
if(!line.startsWith(' ') && line.endsWith(':')) {
|
||||||
@ -219,6 +221,7 @@ class Program (val name: String,
|
|||||||
val program: List<Instruction>
|
val program: List<Instruction>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
prog.add(LabelInstr("____program_end"))
|
||||||
prog.add(Instruction(Opcode.TERMINATE))
|
prog.add(Instruction(Opcode.TERMINATE))
|
||||||
prog.add(Instruction(Opcode.NOP))
|
prog.add(Instruction(Opcode.NOP))
|
||||||
program = prog
|
program = prog
|
||||||
|
@ -231,7 +231,7 @@ open class Instruction(val opcode: Opcode,
|
|||||||
|
|
||||||
class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) {
|
class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$name:"
|
return "\n$name:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user