From ae21e03e1d47c6c83a1f823f068abb69e6a9f139 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 1 Feb 2019 22:51:30 +0100 Subject: [PATCH] attempt at optimization to register parameter passing --- compiler/src/prog8/ast/AST.kt | 34 +++++++++++++++ .../src/prog8/ast/AstIdentifiersChecker.kt | 3 +- .../src/prog8/optimizing/ConstantFolding.kt | 4 +- .../prog8/optimizing/StatementOptimizer.kt | 22 +++++----- docs/source/todo.rst | 2 + examples/test.p8 | 42 +++++++++++-------- prog8.iml | 1 - 7 files changed, 78 insertions(+), 30 deletions(-) diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index a1c42d703..ac5ad3dc1 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1647,6 +1647,40 @@ 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 } + + val canBeAsmSubroutine =false // TODO see below +// !isAsmSubroutine +// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD)) +// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })) + + fun intoAsmSubroutine(): Subroutine { + // TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...) + return this // TODO + +// println("TO ASM $this") // TODO +// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE)) +// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null)) +// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD)) +// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null)) +// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }) +// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null)) +// else throw FatalAstException("cannot convert subroutine to asm parameters") +// +// val asmsub=Subroutine( +// name, +// parameters, +// returntypes, +// paramregs, +// emptyList(), +// emptySet(), +// null, +// true, +// statements, +// position +// ) +// asmsub.linkParents(parent) +// return asmsub + } } diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index c469a7ddd..3a22fd655 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -124,7 +124,8 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { // NOTE: // - numeric types BYTE and WORD and FLOAT are passed by value; // - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter) - if(subroutine.asmAddress==null) { + // - do NOT do this is the statement can be transformed into an asm subroutine later! + if(subroutine.asmAddress==null && !subroutine.canBeAsmSubroutine) { if(subroutine.asmParameterRegisters.isEmpty()) { subroutine.parameters .filter { it.name !in allDefinedNames } diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index cb8ef3e14..9d1222403 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -165,8 +165,10 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV val argConst = arg.first.value.constValue(namespace, heap) if(argConst!=null && argConst.type!=expectedDt) { val convertedValue = argConst.intoDatatype(expectedDt) - if(convertedValue!=null) + if(convertedValue!=null) { functionCall.arglist[arg.first.index] = convertedValue + optimizationsDone++ + } } } } diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 67c043d02..ec8185cab 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -55,6 +55,18 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)} } + if(subroutine.canBeAsmSubroutine) { + optimizationsDone++ + return subroutine.intoAsmSubroutine() // TODO this doesn't work yet due to parameter vardecl issue + + // TODO fix parameter passing so this also works: +// asmsub aa(byte arg @ Y) -> clobbers() -> () { +// byte local = arg ; @todo fix 'undefined symbol arg' by some sort of alias name for the parameter +// A=44 +// } + + } + return subroutine } @@ -83,16 +95,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He return linesToRemove } - private fun returnregisters(subroutine: Subroutine): List { - return when { - subroutine.returntypes.isEmpty() -> listOf() - subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.BYTE, DataType.UBYTE) -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null)) - subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.WORD, DataType.UWORD) -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null)) - subroutine.returntypes.size==2 && subroutine.returntypes.all { it in setOf(DataType.BYTE, DataType.UBYTE)} -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null)) - else -> throw FatalAstException("can't convert return values to registers") - } - } - private fun isNotMemory(target: AssignTarget): Boolean { if(target.register!=null) return true diff --git a/docs/source/todo.rst b/docs/source/todo.rst index d37c7dde0..d19014ee4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -32,6 +32,8 @@ Add more compiler optimizations to the existing ones. - can the parameter passing to subroutines be optimized to avoid copying? - subroutines with 1 or 2 byte args (or 1 word arg) should be converted to asm calling convention with the args in A/Y register + this requires rethinking the way parameters are represented, simply injecting vardecls to + declare local variables for them is not always correct anymore Also some library routines and code patterns could perhaps be optimized further diff --git a/examples/test.p8 b/examples/test.p8 index 486ba0442..66f793941 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,23 +4,31 @@ sub start() { - myblock2.foo() - myblock3.foo() - + foo(1) + bar(1,2) + baz(3333) + bzaz(60000) } + + sub foo(byte arg) { + byte local = arg + A=44 + } + + sub bar(byte arg1, ubyte arg2) { + byte local1 = arg1 + ubyte local2 = arg2 + A=44 + } + + sub baz(word arg) { + word local=arg + A=44 + } + sub bzaz(uword arg) { + uword local=arg + A=44 + } + } -~ myblock2 { - - sub foo() { - A=99 - } -} - - -~ myblock3 { - - sub foo() { - A=99 - } -} diff --git a/prog8.iml b/prog8.iml index c675202dc..a873d0513 100644 --- a/prog8.iml +++ b/prog8.iml @@ -17,6 +17,5 @@ - \ No newline at end of file