expression splitter moved to separate optimizer

This commit is contained in:
Irmen de Jong 2020-10-01 02:58:12 +02:00
parent 95e9e1b550
commit 1464050bf5
7 changed files with 81 additions and 45 deletions

View File

@ -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)
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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<IAstModification> {
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 = <some-expression-not-X> <operator> <not-binary-expression>
// or X = <not-binary-expression> <associativeoperator> <some-expression-not-X>
// split that into X = <some-expression-not-X> ; X = X <operator> <not-binary-expression>
// 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
}
}
}

View File

@ -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()
}

View File

@ -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 = <some-expression-not-X> <operator> <not-binary-expression>
// or X = <not-binary-expression> <associativeoperator> <some-expression-not-X>
// split that into X = <some-expression-not-X> ; X = X <operator> <not-binary-expression>
// 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

View File

@ -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<IAstModification> {
@ -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<Statement>): List<Assignment> {
// removes 'duplicate' assignments that assign the isSameAs target
// removes 'duplicate' assignments that assign the same target directly after another
val linesToRemove = mutableListOf<Assignment>()
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)
}