diff --git a/compiler/src/prog8/ast/processing/AstWalker.kt b/compiler/src/prog8/ast/processing/AstWalker.kt index d6ea71d50..3cdf97eee 100644 --- a/compiler/src/prog8/ast/processing/AstWalker.kt +++ b/compiler/src/prog8/ast/processing/AstWalker.kt @@ -82,6 +82,7 @@ interface IAstModification { class SwapOperands(val expr: BinaryExpression): IAstModification { override fun perform() { + require(expr.operator in associativeOperators) val tmp = expr.left expr.left = expr.right expr.right = tmp @@ -228,6 +229,7 @@ abstract class AstWalker { track(before(decl, parent), decl, parent) decl.value?.accept(this, decl) decl.arraysize?.accept(this, decl) + decl.struct?.accept(this, decl) track(after(decl, parent), decl, parent) } diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 9bf765a6a..9f91db7c4 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -33,6 +33,7 @@ interface IAstVisitor { fun visit(decl: VarDecl) { decl.value?.accept(this) decl.arraysize?.accept(this) + decl.struct?.accept(this) } fun visit(subroutine: Subroutine) { diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 378379054..0639b6485 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -7,6 +7,7 @@ import prog8.ast.statements.Directive import prog8.compiler.target.C64Target import prog8.compiler.target.CompilationTarget import prog8.compiler.target.Cx16Target +import prog8.optimizer.* import prog8.optimizer.UnusedCodeRemover import prog8.optimizer.constantFold import prog8.optimizer.optimizeStatements @@ -189,9 +190,10 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) { // keep optimizing expressions and statements until no more steps remain val optsDone1 = programAst.simplifyExpressions() val optsDone2 = programAst.optimizeStatements(errors) - programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away + val optsDone3 = programAst.splitExpressions() + programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away errors.handle() - if (optsDone1 + optsDone2 == 0) + if (optsDone1 + optsDone2 + optsDone3 == 0) break } diff --git a/compiler/src/prog8/optimizer/ExpressionSplitter.kt b/compiler/src/prog8/optimizer/ExpressionSplitter.kt new file mode 100644 index 000000000..c24d6c9f9 --- /dev/null +++ b/compiler/src/prog8/optimizer/ExpressionSplitter.kt @@ -0,0 +1,65 @@ +package prog8.optimizer + +import prog8.ast.INameScope +import prog8.ast.Node +import prog8.ast.Program +import prog8.ast.expressions.* +import prog8.ast.processing.AstWalker +import prog8.ast.processing.IAstModification +import prog8.ast.statements.AssignTarget +import prog8.ast.statements.Assignment + +class ExpressionSplitter(private val program: Program) : AstWalker() { + + // TODO once this works, integrate it back into expressionsimplifier + override fun after(assignment: Assignment, parent: Node): Iterable { + + if(assignment!=null) { + val expr = assignment.value as? BinaryExpression + if (expr != null) { + // reduce the complexity of a (binary) expression that has to be evaluated on the eval stack, + // by attempting to splitting it up into individual simple steps: + // X = + // or X = + // split that into X = ; X = X + + // TODO FIX THIS, IT SOMETIMES JUST LOOPS... (for example on plasma.p8) + if (expr.operator !in comparisonOperators && !assignment.isAugmentable && isSimpleTarget(assignment.target, program.namespace)) { + if (expr.right !is BinaryExpression) { + println("SPLIT RIGHT BINEXPR $expr") + val firstAssign = Assignment(assignment.target, expr.left, assignment.position) + val augExpr = BinaryExpression(assignment.target.toExpression(), expr.operator, expr.right, expr.position) + return listOf( + IAstModification.InsertBefore(assignment, firstAssign, parent), + IAstModification.ReplaceNode(assignment.value, augExpr, assignment) + ) + } else if (expr.left !is BinaryExpression && expr.operator in associativeOperators) { + println("SPLIT LEFT BINEXPR $expr") + val firstAssign = Assignment(assignment.target, expr.right, assignment.position) + val augExpr = BinaryExpression(assignment.target.toExpression(), expr.operator, expr.left, expr.position) + return listOf( + IAstModification.InsertBefore(assignment, firstAssign, parent), + IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) + } + } + } + } + + return emptyList() + } + + private fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean { + return when { + target.identifier!=null -> target.isInRegularRAM(namespace) + target.memoryAddress!=null -> target.isInRegularRAM(namespace) + target.arrayindexed!=null -> { + val index = target.arrayindexed!!.arrayspec.index + if(index is NumericLiteralValue || index is IdentifierReference) + target.isInRegularRAM(namespace) + else + false + } + else -> false + } + } +} diff --git a/compiler/src/prog8/optimizer/Extensions.kt b/compiler/src/prog8/optimizer/Extensions.kt index 87800312e..4457cc0b5 100644 --- a/compiler/src/prog8/optimizer/Extensions.kt +++ b/compiler/src/prog8/optimizer/Extensions.kt @@ -53,3 +53,10 @@ internal fun Program.simplifyExpressions() : Int { opti.visit(this) return opti.applyModifications() } + +internal fun Program.splitExpressions() : Int { + val opti = ExpressionSplitter(this) + opti.visit(this) + return opti.applyModifications() +} + diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 495c6f0f7..63206b4c9 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -436,43 +436,6 @@ internal class StatementOptimizer(private val program: Program, } } - - fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean { - return when { - target.identifier!=null -> target.isInRegularRAM(namespace) - target.memoryAddress!=null -> target.isInRegularRAM(namespace) - target.arrayindexed!=null -> { - val index = target.arrayindexed!!.arrayspec.index - if(index is NumericLiteralValue || index is IdentifierReference) - target.isInRegularRAM(namespace) - else - false - } - else -> false - } - } - - // reduce the complexity of a (binary) expression that has to be evaluated on the eval stack, - // by attempting to splitting it up into individual simple steps: - // X = - // or X = - // split that into X = ; X = X - // TODO FIX THIS SPLITTING (IT ENDS UP IN A LOOP SOMETIMES) -// if(bexpr.operator !in comparisonOperators && !assignment.isAugmentable && isSimpleTarget(assignment.target, program.namespace)) { -// if (bexpr.right !is BinaryExpression) { -// val firstAssign = Assignment(assignment.target, bexpr.left, assignment.position) -// val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position) -// return listOf( -// IAstModification.InsertBefore(assignment, firstAssign, parent), -// IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) -// } else if (bexpr.left !is BinaryExpression && bexpr.operator in associativeOperators) { -// val firstAssign = Assignment(assignment.target, bexpr.right, assignment.position) -// val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position) -// return listOf( -// IAstModification.InsertBefore(assignment, firstAssign, parent), -// IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) -// } -// } } return noModifications diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index 6054c286d..f5ec19e01 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -4,14 +4,11 @@ import prog8.ast.INameScope import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.ErrorReporter -import prog8.ast.expressions.NumericLiteralValue import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstModification import prog8.ast.statements.* -// TODO remove unneeded assignments such as: cc = 0 ; cc= xbuf ; ... the first can be removed (unless target is not RAM) - internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() { override fun before(program: Program, parent: Node): Iterable { @@ -86,15 +83,14 @@ internal class UnusedCodeRemover(private val program: Program, private val error return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) } } - // subroutine, anonscope, private fun deduplicateAssignments(statements: List): List { - // removes 'duplicate' assignments that assign the isSameAs target + // removes 'duplicate' assignments that assign the same target directly after another val linesToRemove = mutableListOf() for (stmtPairs in statements.windowed(2, step = 1)) { val assign1 = stmtPairs[0] as? Assignment val assign2 = stmtPairs[1] as? Assignment - if (assign1 != null && assign2 != null) { + if (assign1 != null && assign2 != null && !assign2.isAugmentable) { if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace)) linesToRemove.add(assign1) }