mirror of
https://github.com/irmen/prog8.git
synced 2024-10-17 10:24:55 +00:00
tweak trivial subroutine inlining
This commit is contained in:
parent
0a65dfdd10
commit
f7183e38ee
@ -14,6 +14,8 @@ import prog8.code.core.InternalCompilerException
|
|||||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
||||||
|
|
||||||
|
|
||||||
|
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||||
|
|
||||||
class Inliner(val program: Program): AstWalker() {
|
class Inliner(val program: Program): AstWalker() {
|
||||||
|
|
||||||
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||||
@ -142,8 +144,10 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
val scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,41 +172,70 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
|
|
||||||
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = gosub.identifier.targetStatement(program) as? Subroutine
|
val sub = gosub.identifier.targetStatement(program) as? Subroutine
|
||||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
return if(sub==null)
|
||||||
|
noModifications
|
||||||
|
else
|
||||||
|
possibleInlineFcallStmt(sub, gosub, parent)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(sub.inline && sub.parameters.isEmpty()) {
|
||||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
||||||
return if(sub.isAsmSubroutine) {
|
return if(sub.isAsmSubroutine) {
|
||||||
// simply insert the asm for the argument-less routine
|
// simply insert the asm for the argument-less routine
|
||||||
listOf(IAstModification.ReplaceNode(gosub, sub.statements.single().copy(), parent))
|
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
||||||
} else {
|
} else {
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
when (val toInline = sub.statements.first()) {
|
when (val toInline = sub.statements.first()) {
|
||||||
is Return -> noModifications
|
is Return -> {
|
||||||
else -> listOf(IAstModification.ReplaceNode(gosub, toInline.copy(), parent))
|
val fcall = toInline.value as? FunctionCallExpression
|
||||||
|
if(fcall!=null) {
|
||||||
|
// insert the function call expression as a void function call directly
|
||||||
|
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||||
|
} else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||||
|
return if(sub==null)
|
||||||
|
noModifications
|
||||||
|
else
|
||||||
|
possibleInlineFcallStmt(sub, functionCallStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||||
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
||||||
return if(sub.isAsmSubroutine) {
|
return if(sub.isAsmSubroutine) {
|
||||||
// simply insert the asm for the argument-less routine
|
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
|
||||||
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
|
noModifications
|
||||||
} else {
|
} else {
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
|
||||||
when (val toInline = sub.statements.first()) {
|
when (val toInline = sub.statements.first()) {
|
||||||
is Return -> noModifications
|
is Return -> {
|
||||||
else -> listOf(IAstModification.ReplaceNode(functionCallStatement, toInline.copy(), parent))
|
// is an expression, so we have to have a Return here in the inlined sub
|
||||||
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
|
if(toInline.value!=null)
|
||||||
|
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
else -> noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO also inline function call expressions, and remove it from the StatementOptimizer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,46 +16,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
private val compTarget: ICompilationTarget
|
private val compTarget: ICompilationTarget
|
||||||
) : AstWalker() {
|
) : AstWalker() {
|
||||||
|
|
||||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
// if the first instruction in the called subroutine is a return statement with a simple value (NOT being a parameter),
|
|
||||||
// remove the jump altogeter and inline the returnvalue directly. (only if not part of a pipe expression)
|
|
||||||
|
|
||||||
fun scopePrefix(variable: IdentifierReference): IdentifierReference {
|
|
||||||
val target = variable.targetStatement(program) as INamedStatement
|
|
||||||
return IdentifierReference(target.scopedName, variable.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
val subroutine = functionCallExpr.target.targetSubroutine(program)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Return && first.value?.isSimple==true && parent !is IPipe) {
|
|
||||||
val copy = when(val orig = first.value!!) {
|
|
||||||
is AddressOf -> {
|
|
||||||
val scoped = scopePrefix(orig.identifier)
|
|
||||||
AddressOf(scoped, orig.position)
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
when(val expr = orig.addressExpression) {
|
|
||||||
is NumericLiteral -> DirectMemoryRead(expr.copy(), orig.position)
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
if(orig.targetVarDecl(program)?.origin == VarDeclOrigin.SUBROUTINEPARAM)
|
|
||||||
return noModifications
|
|
||||||
else
|
|
||||||
scopePrefix(orig)
|
|
||||||
}
|
|
||||||
is NumericLiteral -> orig.copy()
|
|
||||||
is StringLiteral -> orig.copy()
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, copy, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.size==1) {
|
if(functionCallStatement.target.nameInSource.size==1) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
|
@ -299,11 +299,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
|
||||||
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
|
||||||
if(!subroutine.isAsmSubroutine && compilerOptions.compTarget.name!=VMTarget.NAME)
|
|
||||||
subroutine.inline = false
|
|
||||||
|
|
||||||
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
|
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
|
||||||
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||||
|
|
||||||
|
@ -120,6 +120,12 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
||||||
|
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
||||||
|
if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME)
|
||||||
|
subroutine.inline = false
|
||||||
|
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||||
|
@ -5,7 +5,6 @@ For next release
|
|||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
- pipe operator: (targets other than 'Virtual'): allow non-unary function calls in the pipe that specify the other argument(s) in the calls. Already working for VM target.
|
- pipe operator: (targets other than 'Virtual'): allow non-unary function calls in the pipe that specify the other argument(s) in the calls. Already working for VM target.
|
||||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||||
- Inliner: also inline function call expressions, and remove it from the StatementOptimizer
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,33 @@
|
|||||||
;%import test_stack
|
;%import test_stack
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
||||||
|
|
||||||
main {
|
other {
|
||||||
|
ubyte variable = 40
|
||||||
|
ubyte var2=2
|
||||||
|
|
||||||
|
sub func1(ubyte arg) -> ubyte {
|
||||||
|
txt.print_ub(arg)
|
||||||
|
txt.spc()
|
||||||
|
return arg*var2
|
||||||
|
}
|
||||||
|
|
||||||
|
sub inliner() -> ubyte {
|
||||||
|
return func1(variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub inliner2() {
|
||||||
|
txt.print_ub(22)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main $2000 {
|
||||||
|
|
||||||
|
ubyte x=10
|
||||||
|
ubyte y=20
|
||||||
; sub ands(ubyte arg, ubyte b1, ubyte b2, ubyte b3, ubyte b4) -> ubyte {
|
; sub ands(ubyte arg, ubyte b1, ubyte b2, ubyte b3, ubyte b4) -> ubyte {
|
||||||
; return arg>b1 and arg>b2 and arg>b3 and arg>b4
|
; return arg>b1 and arg>b2 and arg>b3 and arg>b4
|
||||||
; }
|
; }
|
||||||
@ -29,33 +51,20 @@ main {
|
|||||||
; txt.spc()
|
; txt.spc()
|
||||||
; }
|
; }
|
||||||
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
; mcCarthy()
|
; mcCarthy()
|
||||||
;test_stack.test()
|
|
||||||
|
|
||||||
ubyte one = 1
|
other.inliner2()
|
||||||
ubyte two = 2
|
other.inliner2()
|
||||||
uword onew = 1
|
rnd()
|
||||||
uword twow = 2
|
void other.inliner()
|
||||||
ubyte[10] data = [1,2,3,4,5,6,7,8,9,10]
|
void other.inliner()
|
||||||
uword bitmapbuf = &data
|
|
||||||
|
|
||||||
; @(bitmapbuf+onew) = 90+one
|
ubyte derp = other.inliner() * other.inliner() ; TODO inline this (was $207 bytes size)
|
||||||
; @(bitmapbuf+twow) = 90+two
|
txt.print_ub(derp)
|
||||||
bitmapbuf += 5
|
|
||||||
; @(bitmapbuf-1) = 90+one
|
|
||||||
; @(bitmapbuf-2) = 90+two
|
|
||||||
@(bitmapbuf-onew) = 90+one
|
|
||||||
@(bitmapbuf-twow) = 90+two
|
|
||||||
|
|
||||||
ubyte value
|
|
||||||
for value in data {
|
|
||||||
txt.print_ub(value) ; 3 2 97 42 5 6 7 8 9 10
|
|
||||||
txt.spc()
|
|
||||||
}
|
|
||||||
txt.nl()
|
txt.nl()
|
||||||
|
|
||||||
;test_stack.test()
|
|
||||||
|
|
||||||
|
|
||||||
; ; a "pixelshader":
|
; ; a "pixelshader":
|
||||||
|
Loading…
Reference in New Issue
Block a user