mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +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
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
|
||||
class Inliner(val program: Program): AstWalker() {
|
||||
|
||||
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||
@ -142,8 +144,10 @@ class Inliner(val program: Program): AstWalker() {
|
||||
}
|
||||
|
||||
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 scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
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> {
|
||||
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])))
|
||||
return if(sub.isAsmSubroutine) {
|
||||
// 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 {
|
||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||
when (val toInline = sub.statements.first()) {
|
||||
is Return -> noModifications
|
||||
else -> listOf(IAstModification.ReplaceNode(gosub, toInline.copy(), parent))
|
||||
is Return -> {
|
||||
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
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
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()) {
|
||||
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
||||
return if(sub.isAsmSubroutine) {
|
||||
// simply insert the asm for the argument-less routine
|
||||
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
|
||||
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
|
||||
noModifications
|
||||
} else {
|
||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||
when (val toInline = sub.statements.first()) {
|
||||
is Return -> noModifications
|
||||
else -> listOf(IAstModification.ReplaceNode(functionCallStatement, toInline.copy(), parent))
|
||||
is Return -> {
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
) : 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> {
|
||||
if(functionCallStatement.target.nameInSource.size==1) {
|
||||
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)
|
||||
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> {
|
||||
|
||||
// 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>()
|
||||
|
||||
// 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.
|
||||
- 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
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
; 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 {
|
||||
; return arg>b1 and arg>b2 and arg>b3 and arg>b4
|
||||
; }
|
||||
@ -29,33 +51,20 @@ main {
|
||||
; txt.spc()
|
||||
; }
|
||||
|
||||
|
||||
sub start() {
|
||||
; mcCarthy()
|
||||
;test_stack.test()
|
||||
|
||||
ubyte one = 1
|
||||
ubyte two = 2
|
||||
uword onew = 1
|
||||
uword twow = 2
|
||||
ubyte[10] data = [1,2,3,4,5,6,7,8,9,10]
|
||||
uword bitmapbuf = &data
|
||||
other.inliner2()
|
||||
other.inliner2()
|
||||
rnd()
|
||||
void other.inliner()
|
||||
void other.inliner()
|
||||
|
||||
; @(bitmapbuf+onew) = 90+one
|
||||
; @(bitmapbuf+twow) = 90+two
|
||||
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()
|
||||
}
|
||||
ubyte derp = other.inliner() * other.inliner() ; TODO inline this (was $207 bytes size)
|
||||
txt.print_ub(derp)
|
||||
txt.nl()
|
||||
|
||||
;test_stack.test()
|
||||
|
||||
|
||||
; ; a "pixelshader":
|
||||
|
Loading…
Reference in New Issue
Block a user