mirror of
https://github.com/irmen/prog8.git
synced 2024-06-26 07:29:32 +00:00
allow inlining of subroutines with parameters, and fix inlining of subroutines with variables
This commit is contained in:
parent
d116eb7655
commit
e5ff61f201
|
@ -209,9 +209,6 @@ internal class AstChecker(private val program: Program,
|
|||
if(uniqueNames.size!=subroutine.parameters.size)
|
||||
err("parameter names must be unique")
|
||||
|
||||
if(subroutine.inline && !subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty())
|
||||
err("can't inline a non-asm subroutine that has parameters")
|
||||
|
||||
super.visit(subroutine)
|
||||
|
||||
// user-defined subroutines can only have zero or one return type
|
||||
|
|
|
@ -13,6 +13,7 @@ import prog8.compiler.target.cbm.AssemblyProgram
|
|||
import prog8.compiler.target.cbm.Petscii
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
|
||||
import prog8.optimizer.CallGraph
|
||||
import java.io.CharConversionException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
@ -32,6 +33,7 @@ internal class AsmGen(private val program: Program,
|
|||
// for expressions and augmented assignments:
|
||||
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
|
||||
private val callGraph = CallGraph(program)
|
||||
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||
|
@ -830,9 +832,18 @@ internal class AsmGen(private val program: Program,
|
|||
|
||||
|
||||
private fun translateSubroutine(sub: Subroutine) {
|
||||
var onlyVariables = false
|
||||
|
||||
if(sub.inline) {
|
||||
if(options.optimize)
|
||||
return // inline subroutines don't exist anymore on their own
|
||||
if(options.optimize) {
|
||||
if(sub.isAsmSubroutine ||callGraph.unused(sub))
|
||||
return
|
||||
|
||||
// from an inlined subroutine only the local variables are generated,
|
||||
// all other code statements are omitted in the subroutine itself
|
||||
// (they've been inlined at the call site, remember?)
|
||||
onlyVariables = true
|
||||
}
|
||||
else if(sub.amountOfRtsInAsm()==0) {
|
||||
// make sure the NOT INLINED subroutine actually does an rts at the end
|
||||
sub.statements.add(Return(null, Position.DUMMY))
|
||||
|
@ -849,7 +860,7 @@ internal class AsmGen(private val program: Program,
|
|||
|
||||
// asmsub with most likely just an inline asm in it
|
||||
out("${sub.name}\t.proc")
|
||||
sub.statements.forEach{ translate(it) }
|
||||
sub.statements.forEach { translate(it) }
|
||||
out(" .pend\n")
|
||||
} else {
|
||||
// regular subroutine
|
||||
|
@ -873,8 +884,10 @@ internal class AsmGen(private val program: Program,
|
|||
clc""")
|
||||
}
|
||||
|
||||
out("; statements")
|
||||
sub.statements.forEach{ translate(it) }
|
||||
if(!onlyVariables) {
|
||||
out("; statements")
|
||||
sub.statements.forEach { translate(it) }
|
||||
}
|
||||
|
||||
for(removal in removals.toList()) {
|
||||
if(removal.second==sub) {
|
||||
|
|
|
@ -118,9 +118,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||
// we do this by copying the subroutine's statements at the call site.
|
||||
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||
// (this condition has been enforced by an ast check earlier)
|
||||
if(!sub.isAsmSubroutine && sub.parameters.isNotEmpty())
|
||||
throw AssemblyError("can't inline a non-asm subroutine with parameters")
|
||||
asmgen.out(" \t; inlined routine follows: ${sub.name} from ${sub.position}")
|
||||
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||
val statements = sub.statements.filter { it !is ParameterVarDecl && it !is Directive }
|
||||
statements.forEach {
|
||||
if(it is Return) {
|
||||
|
@ -129,6 +127,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||
asmgen.translate(it)
|
||||
}
|
||||
}
|
||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||
}
|
||||
|
||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||
|
|
|
@ -6,10 +6,7 @@ import prog8.ast.Program
|
|||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Return
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.CompilationOptions
|
||||
|
@ -58,34 +55,21 @@ internal class SubroutineInliner(private val program: Program, val errors: IErro
|
|||
}
|
||||
|
||||
private fun annotateInlinedSubroutineIdentifiers(sub: Subroutine): List<IAstModification> {
|
||||
// this adds full name prefixes to all identifiers used in the subroutine,
|
||||
// this adds name prefixes to the identifiers used in the subroutine,
|
||||
// so that the statements can be inlined (=copied) in the call site and still reference
|
||||
// the correct symbols as seen from the scope of the subroutine.
|
||||
|
||||
if(sub.containsDefinedVariables())
|
||||
errors.warn("inlining a subroutine with variables, this could result in large code/memory size", sub.position)
|
||||
|
||||
class Annotator: AstWalker() {
|
||||
var numReturns=0
|
||||
|
||||
override fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
val stmt = identifier.targetStatement(program)!!
|
||||
val subroutine = identifier.definingSubroutine()
|
||||
return if(stmt is VarDecl && stmt.parent === subroutine) {
|
||||
val prefixed = stmt.makeScopedName(identifier.nameInSource.last()).replace('.','_')
|
||||
val withPrefix = IdentifierReference(listOf(prefixed), identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(identifier, withPrefix, parent))
|
||||
} else {
|
||||
val prefixed = stmt.makeScopedName(identifier.nameInSource.last()).split('.')
|
||||
val withPrefix = IdentifierReference(prefixed, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(identifier, withPrefix, parent))
|
||||
}
|
||||
}
|
||||
if(stmt is BuiltinFunctionStatementPlaceholder)
|
||||
return noModifications
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val prefixed = decl.makeScopedName(decl.name).replace('.','_')
|
||||
val newdecl = VarDecl(decl.type, decl.datatype, decl.zeropage, decl.arraysize, prefixed, decl.struct?.name, decl.value, decl.isArray, decl.autogeneratedDontRemove, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, newdecl, parent))
|
||||
val prefixed = stmt.makeScopedName(identifier.nameInSource.last()).split('.')
|
||||
val withPrefix = IdentifierReference(prefixed, identifier.position)
|
||||
return listOf(IAstModification.ReplaceNode(identifier, withPrefix, parent))
|
||||
}
|
||||
|
||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
|
|
|
@ -178,7 +178,6 @@ interface INameScope {
|
|||
}
|
||||
}
|
||||
|
||||
fun containsDefinedVariables() = statements.any { it is VarDecl && (it !is ParameterVarDecl) }
|
||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
TODO
|
||||
====
|
||||
|
||||
- allow inlining of subroutines with params
|
||||
- optimize several inner loops in gfx2
|
||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
||||
|
|
|
@ -4,27 +4,22 @@
|
|||
main {
|
||||
|
||||
sub start() {
|
||||
uword[] uw_arr = [1111,2222,3333]
|
||||
word[] w_arr = [1111,2222,3333]
|
||||
|
||||
ubyte ub = 42
|
||||
byte bb = -42
|
||||
ubyte ix = 2
|
||||
|
||||
uw_arr[1] = ub
|
||||
w_arr[1] = bb
|
||||
|
||||
txt.print_uw(uw_arr[1])
|
||||
txt.nl()
|
||||
txt.print_w(w_arr[1])
|
||||
txt.nl()
|
||||
|
||||
uw_arr[ix] = ub
|
||||
w_arr[ix] = bb
|
||||
|
||||
txt.print_uw(uw_arr[1])
|
||||
txt.nl()
|
||||
txt.print_w(w_arr[1])
|
||||
; cx16.rambank(4)
|
||||
; cx16.rambank(4)
|
||||
; cx16.rambank(4)
|
||||
; cx16.rambank(4)
|
||||
; cx16.rambank(4)
|
||||
uword yy = 12345
|
||||
ubyte xx
|
||||
xx = calc2(41, 12345)
|
||||
xx = calc2(41, 12345)
|
||||
xx = calc2(41, 12345)
|
||||
xx = calc2(41, 12345)
|
||||
txt.print_ub(xx) ; must be 99
|
||||
}
|
||||
|
||||
inline sub calc2(ubyte a1, uword a2) -> ubyte {
|
||||
uword thesum = a2 + a1
|
||||
return lsb(thesum+a2)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user