mirror of
https://github.com/irmen/prog8.git
synced 2025-01-14 17:31:01 +00:00
added inlining certain trivial non-asm subroutine calls
This commit is contained in:
parent
e69aeb8b98
commit
fd6eb47e68
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@ -22,9 +22,4 @@
|
|||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
<component name="SwUserDefinedSpecifications">
|
|
||||||
<option name="specTypeByUrl">
|
|
||||||
<map />
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
</project>
|
@ -55,11 +55,9 @@ fun Program.optimizeStatements(errors: IErrorReporter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Program.inlineSubroutines(): Int {
|
fun Program.inlineSubroutines(): Int {
|
||||||
// TODO implement the inliner
|
val inliner = Inliner(this)
|
||||||
// val inliner = Inliner(this)
|
inliner.visit(this)
|
||||||
// inliner.visit(this)
|
return inliner.applyModifications()
|
||||||
// return inliner.applyModifications()
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
||||||
|
@ -3,53 +3,106 @@ package prog8.optimizer
|
|||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.core.InternalCompilerException
|
||||||
|
|
||||||
|
|
||||||
|
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
||||||
|
|
||||||
|
|
||||||
class Inliner(val program: Program): AstWalker() {
|
class Inliner(val program: Program): AstWalker() {
|
||||||
|
|
||||||
class DetermineInlineSubs(program: Program): IAstVisitor {
|
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||||
|
private val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
visit(program)
|
visit(program)
|
||||||
|
modifications.forEach { it.perform() }
|
||||||
|
modifications.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
|
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
|
||||||
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
|
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
|
||||||
if(!containsSubsOrVariables) {
|
if(!containsSubsOrVariables) {
|
||||||
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && subroutine.statements[1] is Return)) {
|
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))) {
|
||||||
// subroutine is possible candidate to be inlined
|
// subroutine is possible candidate to be inlined
|
||||||
subroutine.inline =
|
subroutine.inline =
|
||||||
when(val stmt=subroutine.statements[0]) {
|
when(val stmt=subroutine.statements[0]) {
|
||||||
is Return -> {
|
is Return -> {
|
||||||
if(stmt.value!!.isSimple) {
|
if(stmt.value is NumericLiteral)
|
||||||
makeFullyScoped(stmt)
|
|
||||||
true
|
true
|
||||||
|
else if (stmt.value is IdentifierReference) {
|
||||||
|
makeFullyScoped(stmt.value as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else if(stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size<=1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||||
|
when (stmt.value) {
|
||||||
|
is BuiltinFunctionCall -> {
|
||||||
|
makeFullyScoped(stmt.value as BuiltinFunctionCall)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
is FunctionCallExpression -> {
|
||||||
|
makeFullyScoped(stmt.value as FunctionCallExpression)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
is Assignment -> {
|
is Assignment -> {
|
||||||
val inline = stmt.value.isSimple && (stmt.target.identifier!=null || stmt.target.memoryAddress?.addressExpression?.isSimple==true)
|
if(stmt.value.isSimple) {
|
||||||
|
val targetInline =
|
||||||
|
if(stmt.target.identifier!=null) {
|
||||||
|
makeFullyScoped(stmt.target.identifier!!)
|
||||||
|
true
|
||||||
|
} else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||||
|
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
val valueInline =
|
||||||
|
if(stmt.value is IdentifierReference) {
|
||||||
|
makeFullyScoped(stmt.value as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else if((stmt.value as? DirectMemoryRead)?.addressExpression is NumericLiteral || (stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference) {
|
||||||
|
if((stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped((stmt.value as? DirectMemoryRead)?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
targetInline || valueInline
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
is BuiltinFunctionCallStatement -> {
|
||||||
|
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||||
if(inline)
|
if(inline)
|
||||||
makeFullyScoped(stmt)
|
makeFullyScoped(stmt)
|
||||||
inline
|
inline
|
||||||
}
|
}
|
||||||
is BuiltinFunctionCallStatement,
|
|
||||||
is FunctionCallStatement -> {
|
is FunctionCallStatement -> {
|
||||||
stmt as IFunctionCall
|
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
|
||||||
val inline = stmt.args.size<=1 && stmt.args.all { it.isSimple }
|
|
||||||
if(inline)
|
if(inline)
|
||||||
makeFullyScoped(stmt)
|
makeFullyScoped(stmt)
|
||||||
inline
|
inline
|
||||||
}
|
}
|
||||||
is PostIncrDecr -> {
|
is PostIncrDecr -> {
|
||||||
val inline = (stmt.target.identifier!=null || stmt.target.memoryAddress?.addressExpression?.isSimple==true)
|
if(stmt.target.identifier!=null) {
|
||||||
if(inline)
|
makeFullyScoped(stmt.target.identifier!!)
|
||||||
makeFullyScoped(stmt)
|
true
|
||||||
inline
|
}
|
||||||
|
else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
|
||||||
|
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
|
||||||
|
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
}
|
}
|
||||||
is Jump, is GoSub -> true
|
is Jump, is GoSub -> true
|
||||||
else -> false
|
else -> false
|
||||||
@ -60,20 +113,51 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
super.visit(subroutine)
|
super.visit(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(incrdecr: PostIncrDecr) {
|
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||||
TODO("Not yet implemented")
|
val scoped = (identifier.targetStatement(program)!! as INamedStatement).scopedName
|
||||||
|
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: IFunctionCall) {
|
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
|
||||||
TODO("Not yet implemented")
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(assign: Assignment) {
|
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||||
TODO("Not yet implemented")
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(ret: Return) {
|
private fun makeFullyScoped(call: BuiltinFunctionCall) {
|
||||||
TODO("Not yet implemented")
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
|
val scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeScopedArgs(args: List<Expression>): List<Expression> {
|
||||||
|
return args.map {
|
||||||
|
when (it) {
|
||||||
|
is NumericLiteral -> it.copy()
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val scoped = (it.targetStatement(program)!! as INamedStatement).scopedName
|
||||||
|
IdentifierReference(scoped, it.position)
|
||||||
|
}
|
||||||
|
else -> throw InternalCompilerException("expected only number or identifier arg, otherwise too complex")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,24 +168,41 @@ 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) {
|
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||||
val inlined = sub.statements
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
||||||
TODO("INLINE GOSUB: $gosub ---> $inlined")
|
return if(sub.isAsmSubroutine) {
|
||||||
|
// simply insert the asm for the argument-less routine
|
||||||
|
listOf(IAstModification.ReplaceNode(gosub, 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return noModifications
|
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> {
|
||||||
|
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = inlineCall(functionCallStatement as IFunctionCall, parent)
|
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||||
|
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
||||||
private fun inlineCall(call: IFunctionCall, parent: Node): Iterable<IAstModification> {
|
return if(sub.isAsmSubroutine) {
|
||||||
val sub = call.target.targetStatement(program) as? Subroutine
|
// simply insert the asm for the argument-less routine
|
||||||
if(sub!=null && sub.inline) {
|
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
|
||||||
val inlined = sub.statements
|
} else {
|
||||||
TODO("INLINE FCALL: $call ---> $inlined")
|
// 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO also inline function call expressions, and remove it from the StatementOptimizer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,9 @@ import com.github.michaelbull.result.Ok
|
|||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.getOrElse
|
import com.github.michaelbull.result.getOrElse
|
||||||
import com.github.michaelbull.result.mapError
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.ast.IFunctionCall
|
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.determineGosubArguments
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
@ -242,32 +241,10 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
// Gather the Goto and any preceding parameter assignments back into a single Function call node.
|
// Gather the Goto and any preceding parameter assignments back into a single Function call node.
|
||||||
// (the reason it was split up in the first place, is because the Compiler Ast optimizers
|
// (the reason it was split up in the first place, is because the Compiler Ast optimizers
|
||||||
// can then work on any complex expressions that are used as arguments.)
|
// can then work on any complex expressions that are used as arguments.)
|
||||||
val parent = gosub.parent as IStatementContainer
|
val arguments = determineGosubArguments(gosub)
|
||||||
val gosubIdx = parent.statements.indexOf(gosub)
|
|
||||||
val previousNodes = parent.statements.subList(0, gosubIdx).reversed()
|
|
||||||
val paramValues = mutableMapOf<String, Expression>()
|
|
||||||
for (node in previousNodes) {
|
|
||||||
if(node !is Assignment || node.origin!=AssignmentOrigin.PARAMETERASSIGN)
|
|
||||||
break
|
|
||||||
paramValues[node.target.identifier!!.nameInSource.last()] = node.value
|
|
||||||
}
|
|
||||||
// instead of just assigning to the parameters, another way is to use push()/pop()
|
|
||||||
if(previousNodes.isNotEmpty()) {
|
|
||||||
val first = previousNodes[0] as? IFunctionCall
|
|
||||||
if(first!=null && (first.target.nameInSource.singleOrNull() in arrayOf("pop", "popw"))) {
|
|
||||||
val numPops = previousNodes.indexOfFirst { (it as? IFunctionCall)?.target?.nameInSource?.singleOrNull() !in arrayOf("pop", "popw") }
|
|
||||||
val pops = previousNodes.subList(0, numPops)
|
|
||||||
val pushes = previousNodes.subList(numPops, numPops+numPops).reversed()
|
|
||||||
for ((push, pop) in pushes.zip(pops)) {
|
|
||||||
val name = ((pop as IFunctionCall).args.single() as IdentifierReference).nameInSource.last()
|
|
||||||
val arg = (push as IFunctionCall).args.single()
|
|
||||||
paramValues[name] = arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val parameters = gosub.identifier.targetSubroutine(program)!!.parameters
|
val parameters = gosub.identifier.targetSubroutine(program)!!.parameters
|
||||||
if(paramValues.size != parameters.size)
|
if(arguments.size != parameters.size)
|
||||||
throw FatalAstException("mismatched number of parameter assignments for function call")
|
throw FatalAstException("mismatched number of parameter assignments for function call")
|
||||||
|
|
||||||
val target = transform(gosub.identifier)
|
val target = transform(gosub.identifier)
|
||||||
@ -275,7 +252,7 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
|
|
||||||
// put arguments in correct order for the parameters
|
// put arguments in correct order for the parameters
|
||||||
parameters.forEach {
|
parameters.forEach {
|
||||||
val argument = paramValues.getValue(it.name)
|
val argument = arguments.getValue(it.name)
|
||||||
call.add(transformExpression(argument))
|
call.add(transformExpression(argument))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.ast
|
package prog8.ast
|
||||||
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.InferredTypes
|
import prog8.ast.expressions.InferredTypes
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.VarDeclOrigin
|
|
||||||
import prog8.ast.statements.VarDeclType
|
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.core.ZeropageWish
|
import prog8.code.core.ZeropageWish
|
||||||
@ -56,3 +56,32 @@ fun getTempRegisterName(dt: InferredTypes.InferredType): List<String> {
|
|||||||
else -> throw FatalAstException("invalid dt $dt")
|
else -> throw FatalAstException("invalid dt $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun determineGosubArguments(gosub: GoSub): Map<String, Expression> {
|
||||||
|
val parent = gosub.parent as IStatementContainer
|
||||||
|
val gosubIdx = parent.statements.indexOf(gosub)
|
||||||
|
val previousNodes = parent.statements.subList(0, gosubIdx).reversed()
|
||||||
|
|
||||||
|
val arguments = mutableMapOf<String, Expression>()
|
||||||
|
for (node in previousNodes) {
|
||||||
|
if(node !is Assignment || node.origin!=AssignmentOrigin.PARAMETERASSIGN)
|
||||||
|
break
|
||||||
|
arguments[node.target.identifier!!.nameInSource.last()] = node.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// instead of just assigning to the parameters, another way is to use push()/pop()
|
||||||
|
if(previousNodes.isNotEmpty()) {
|
||||||
|
val first = previousNodes[0] as? IFunctionCall
|
||||||
|
if(first!=null && (first.target.nameInSource.singleOrNull() in arrayOf("pop", "popw"))) {
|
||||||
|
val numPops = previousNodes.indexOfFirst { (it as? IFunctionCall)?.target?.nameInSource?.singleOrNull() !in arrayOf("pop", "popw") }
|
||||||
|
val pops = previousNodes.subList(0, numPops)
|
||||||
|
val pushes = previousNodes.subList(numPops, numPops+numPops).reversed()
|
||||||
|
for ((push, pop) in pushes.zip(pops)) {
|
||||||
|
val name = ((pop as IFunctionCall).args.single() as IdentifierReference).nameInSource.last()
|
||||||
|
val arg = (push as IFunctionCall).args.single()
|
||||||
|
arguments[name] = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arguments
|
||||||
|
}
|
||||||
|
@ -932,7 +932,10 @@ class FunctionCallExpression(override var target: IdentifierReference,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun copy() = FunctionCallExpression(target.copy(), args.map { it.copy() }.toMutableList(), position)
|
override fun copy() = FunctionCallExpression(target.copy(), args.map { it.copy() }.toMutableList(), position)
|
||||||
override val isSimple = target.nameInSource.size==1 && (target.nameInSource[0] in arrayOf("msb", "lsb", "peek", "peekw"))
|
override val isSimple =
|
||||||
|
target.nameInSource.size==1
|
||||||
|
&& target.nameInSource[0] in arrayOf("msb", "lsb", "peek", "peekw", "mkword")
|
||||||
|
&& args.all { it.isSimple }
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
if(node===target)
|
if(node===target)
|
||||||
|
@ -613,7 +613,10 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
args.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copy() = throw NotImplementedError("no support for duplicating a FunctionCallStatement")
|
override fun copy(): FunctionCallStatement {
|
||||||
|
val argsCopies = args.map { it.copy() }
|
||||||
|
return FunctionCallStatement(target.copy(), argsCopies.toMutableList(), void, position)
|
||||||
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
if(node===target)
|
if(node===target)
|
||||||
@ -637,7 +640,7 @@ class InlineAssembly(val assembly: String, override val position: Position) : St
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copy() = throw NotImplementedError("no support for duplicating a InlineAssembly")
|
override fun copy() = InlineAssembly(assembly, position)
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
@ -3,7 +3,6 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
- 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?
|
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||||
|
|
||||||
...
|
...
|
||||||
@ -27,6 +26,7 @@ Compiler:
|
|||||||
- vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us)
|
- vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us)
|
||||||
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
|
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
|
||||||
- vm: add more assignments to translateInplaceAssign()
|
- vm: add more assignments to translateInplaceAssign()
|
||||||
|
- Inliner: also inline function call expressions, and remove it from the StatementOptimizer
|
||||||
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
|
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
|
||||||
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.
|
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.
|
||||||
So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step.
|
So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step.
|
||||||
|
@ -7,46 +7,29 @@
|
|||||||
|
|
||||||
; 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 value = 42
|
ubyte value = 42
|
||||||
|
|
||||||
sub inline_candidate() -> ubyte {
|
sub getter() -> ubyte {
|
||||||
return math.sin8u(value)
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub inline_candidate2() {
|
main {
|
||||||
value++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sub add(ubyte first, ubyte second) -> ubyte {
|
|
||||||
return first + second
|
|
||||||
}
|
|
||||||
|
|
||||||
sub mul(ubyte first, ubyte second) -> ubyte {
|
|
||||||
return first * second
|
|
||||||
}
|
|
||||||
|
|
||||||
ubyte ix
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
ubyte @shared ix = other.getter()
|
||||||
ubyte @shared value1 = inline_candidate()
|
ix = other.getter()
|
||||||
txt.print_ub(value) ; 42
|
ix++
|
||||||
txt.spc()
|
ix = other.getter()
|
||||||
inline_candidate2()
|
ix++
|
||||||
inline_candidate2()
|
ix = other.getter()
|
||||||
inline_candidate2()
|
ix++
|
||||||
txt.print_ub(value) ; 45
|
ix = other.getter()
|
||||||
txt.nl()
|
ix++
|
||||||
txt.print_ub(inline_candidate())
|
ix = other.getter()
|
||||||
txt.nl()
|
ix++
|
||||||
|
|
||||||
ubyte @shared value=99 ; TODO compiler warning about shadowing
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
ubyte @shared add=99 ; TODO compiler warning about shadowing
|
|
||||||
|
|
||||||
; ; a "pixelshader":
|
; ; a "pixelshader":
|
||||||
; sys.gfx_enable(0) ; enable lo res screen
|
; sys.gfx_enable(0) ; enable lo res screen
|
||||||
|
Loading…
x
Reference in New Issue
Block a user