mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
clean up subroutine inlining, basis for new try
This commit is contained in:
parent
dad5b17ac8
commit
627aa61184
@ -54,6 +54,14 @@ fun Program.optimizeStatements(errors: IErrorReporter,
|
||||
return optimizationCount
|
||||
}
|
||||
|
||||
fun Program.inlineSubroutines(): Int {
|
||||
// TODO implement the inliner
|
||||
// val inliner = Inliner(this)
|
||||
// inliner.visit(this)
|
||||
// return inliner.applyModifications()
|
||||
return 0
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
||||
val opti = ExpressionSimplifier(this, errors)
|
||||
opti.visit(this)
|
||||
|
107
codeOptimizers/src/prog8/optimizer/Inliner.kt
Normal file
107
codeOptimizers/src/prog8/optimizer/Inliner.kt
Normal file
@ -0,0 +1,107 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
|
||||
class Inliner(val program: Program): AstWalker() {
|
||||
|
||||
class DetermineInlineSubs(program: Program): IAstVisitor {
|
||||
init {
|
||||
visit(program)
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
|
||||
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
|
||||
if(!containsSubsOrVariables) {
|
||||
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && subroutine.statements[1] is Return)) {
|
||||
// subroutine is possible candidate to be inlined
|
||||
subroutine.inline =
|
||||
when(val stmt=subroutine.statements[0]) {
|
||||
is Return -> {
|
||||
if(stmt.value!!.isSimple) {
|
||||
makeFullyScoped(stmt)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
is Assignment -> {
|
||||
val inline = stmt.value.isSimple && (stmt.target.identifier!=null || stmt.target.memoryAddress?.addressExpression?.isSimple==true)
|
||||
if(inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
is BuiltinFunctionCallStatement,
|
||||
is FunctionCallStatement -> {
|
||||
stmt as IFunctionCall
|
||||
val inline = stmt.args.size<=1 && stmt.args.all { it.isSimple }
|
||||
if(inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
is PostIncrDecr -> {
|
||||
val inline = (stmt.target.identifier!=null || stmt.target.memoryAddress?.addressExpression?.isSimple==true)
|
||||
if(inline)
|
||||
makeFullyScoped(stmt)
|
||||
inline
|
||||
}
|
||||
is Jump, is GoSub -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visit(subroutine)
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(incrdecr: PostIncrDecr) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(call: IFunctionCall) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(assign: Assignment) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(ret: Return) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
override fun before(program: Program): Iterable<IAstModification> {
|
||||
DetermineInlineSubs(program)
|
||||
return super.before(program)
|
||||
}
|
||||
|
||||
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
||||
val sub = gosub.identifier.targetStatement(program) as? Subroutine
|
||||
if(sub!=null && sub.inline) {
|
||||
val inlined = sub.statements
|
||||
TODO("INLINE GOSUB: $gosub ---> $inlined")
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> = inlineCall(functionCallExpr as IFunctionCall, parent)
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = inlineCall(functionCallStatement as IFunctionCall, parent)
|
||||
|
||||
private fun inlineCall(call: IFunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
val sub = call.target.targetStatement(program) as? Subroutine
|
||||
if(sub!=null && sub.inline) {
|
||||
val inlined = sub.statements
|
||||
TODO("INLINE FCALL: $call ---> $inlined")
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
@ -350,9 +350,10 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
val optsDone1 = program.simplifyExpressions(errors)
|
||||
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
|
||||
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
||||
val optsDone4 = program.inlineSubroutines()
|
||||
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
errors.report()
|
||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
|
||||
break
|
||||
}
|
||||
errors.report()
|
||||
|
@ -298,8 +298,10 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(compilerOptions.compTarget.name!=VMTarget.NAME && subroutine.inline && !subroutine.isAsmSubroutine)
|
||||
err("subroutine inlining is currently only supported on asmsub routines")
|
||||
// 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")
|
||||
|
@ -124,7 +124,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||
if (!subroutine.isAsmSubroutine && (!subroutine.inline || !options.optimize)) {
|
||||
if (!subroutine.isAsmSubroutine) {
|
||||
if(subroutine.statements.isEmpty() ||
|
||||
(subroutine.amountOfRtsInAsm() == 0
|
||||
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
|
||||
|
@ -274,13 +274,12 @@ private fun Prog8ANTLRParser.LabeldefContext.toAst(): Statement =
|
||||
|
||||
private fun Prog8ANTLRParser.SubroutineContext.toAst() : Subroutine {
|
||||
// non-asm subroutine
|
||||
val inline = inline()!=null
|
||||
val returntype = sub_return_part()?.datatype()?.toAst()
|
||||
return Subroutine(identifier().text,
|
||||
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
|
||||
if(returntype==null) emptyList() else listOf(returntype),
|
||||
statement_block()?.toAst() ?: mutableListOf(),
|
||||
inline,
|
||||
false,
|
||||
toPosition())
|
||||
}
|
||||
|
||||
|
@ -690,7 +690,7 @@ class Subroutine(override val name: String,
|
||||
val asmClobbers: Set<CpuRegister>,
|
||||
val asmAddress: UInt?,
|
||||
val isAsmSubroutine: Boolean,
|
||||
val inline: Boolean,
|
||||
var inline: Boolean,
|
||||
override var statements: MutableList<Statement>,
|
||||
override val position: Position) : Statement(), INameScope {
|
||||
|
||||
|
@ -691,8 +691,7 @@ in-place to the locations where the subroutine is called, rather than inserting
|
||||
subroutine. This may increase code size significantly and can only be used in limited scenarios, so YMMV.
|
||||
Note that the routine's code is copied verbatim into the place of the subroutine call in this case,
|
||||
so pay attention to any jumps and rts instructions in the inlined code!
|
||||
|
||||
At this time it is not yet possible to inline regular Prog8 subroutines, this may be added in the future.
|
||||
Inlining regular Prog8 subroutines is at the discretion of the compiler.
|
||||
|
||||
|
||||
Calling a subroutine
|
||||
|
@ -3,13 +3,8 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- make it possible to inline non-asmsub routines that just contain a single statement (return, functioncall, assignment)
|
||||
Only if the arguments are simple expressions, and the inlined subroutine cannot contain further nested subroutines!
|
||||
This requires all identifiers in the inlined expression to be changed to fully scoped names (because their scope changes).
|
||||
If we can do that why not perhaps also able to inline multi-line subroutines?
|
||||
Why would it be limited to just 1 line? Maybe to protect against code size bloat.
|
||||
Once this works, look for library subroutines that should be inlined.
|
||||
- vm: add way more instructions operating directly on memory instead of only registers
|
||||
- complete the Inliner
|
||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||
|
||||
...
|
||||
|
@ -6,13 +6,18 @@
|
||||
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
||||
|
||||
main {
|
||||
ubyte value = 42
|
||||
|
||||
sub derp() -> ubyte {
|
||||
return math.sin8u(value)
|
||||
}
|
||||
|
||||
sub start() {
|
||||
str thing = "????"
|
||||
|
||||
if thing=="bmap" {
|
||||
txt.print("gottem")
|
||||
}
|
||||
ubyte value = derp()
|
||||
txt.print_ub(value)
|
||||
txt.nl()
|
||||
txt.print_ub(derp())
|
||||
txt.nl()
|
||||
|
||||
; TODO: test with builtin function using multiple args (such as mkword)
|
||||
; ubyte value = add(3,4) |> add(10) |> mul(2) |> math.sin8u() ; TODO should not work yet on vm codegen, but it compiles.... :/
|
||||
|
@ -242,7 +242,7 @@ inlineasm : '%asm' INLINEASMBLOCK;
|
||||
inline: 'inline';
|
||||
|
||||
subroutine :
|
||||
inline? 'sub' identifier '(' sub_params? ')' sub_return_part? (statement_block EOL)
|
||||
'sub' identifier '(' sub_params? ')' sub_return_part? (statement_block EOL)
|
||||
;
|
||||
|
||||
sub_return_part : '->' datatype ;
|
||||
|
Loading…
Reference in New Issue
Block a user