From 5fb714fcb2c688339fa9a811375b9981ac12a95d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 2 Oct 2020 01:54:37 +0200 Subject: [PATCH] expression splitter integrated into expression simplifier --- compiler/src/prog8/compiler/Main.kt | 5 +- .../prog8/optimizer/ExpressionSimplifier.kt | 79 ++++++++++++++++ .../src/prog8/optimizer/ExpressionSplitter.kt | 91 ------------------- compiler/src/prog8/optimizer/Extensions.kt | 7 -- 4 files changed, 81 insertions(+), 101 deletions(-) delete mode 100644 compiler/src/prog8/optimizer/ExpressionSplitter.kt diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 0639b6485..4913b1364 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -190,10 +190,9 @@ 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) - 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 + optsDone3 == 0) + if (optsDone1 + optsDone2 == 0) break } @@ -220,7 +219,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: programAst.processAstBeforeAsmGeneration(errors) errors.handle() - printAst(programAst) // TODO + // printAst(programAst) CompilationTarget.instance.machine.initializeZeropage(compilerOptions) val assembly = CompilationTarget.instance.asmGenerator( diff --git a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt index 9bab6d40c..ac8e04c43 100644 --- a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt @@ -1,11 +1,15 @@ package prog8.optimizer +import prog8.ast.INameScope import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstModification +import prog8.ast.statements.AssignTarget +import prog8.ast.statements.Assignment +import prog8.ast.statements.VarDecl import kotlin.math.abs import kotlin.math.log2 import kotlin.math.pow @@ -275,6 +279,81 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() return noModifications } + +// override fun after(decl: VarDecl, parent: Node): Iterable { +// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: +// if(decl.type==VarDeclType.VAR ) { +// val binExpr = decl.value as? BinaryExpression +// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) { +// // split into a vardecl with just the left expression, and an aug. assignment with the right expression. +// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position) +// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) +// val assign = Assignment(target, augExpr, binExpr.position) +// println("SPLIT VARDECL $decl") +// return listOf( +// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl), +// IAstModification.InsertAfter(decl, assign, parent) +// ) +// } +// } +// return noModifications +// } + + override fun after(assignment: Assignment, parent: Node): Iterable { + + val binExpr = assignment.value as? BinaryExpression + if (binExpr != 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 = BinExpr X = LeftExpr + followed by + / \ IF 'X' not used X = BinExpr + / \ IN LEFTEXPR ==> + / \ / \ + LeftExpr. RightExpr. / \ + / \ / \ X RightExpr. + .. .. .. .. + + */ + if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) { + if (!assignment.isAugmentable) { + val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position) + val targetExpr = assignment.target.toExpression() + val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position) + println("SPLIT $assignment") + return listOf( + IAstModification.InsertBefore(assignment, firstAssign, parent), + IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) + } + } + + // TODO further unraveling of binary expression trees into flat statements. + // however this should probably be done in a more generic way to also service + // the expressiontrees that are not used in an assignment statement... + } + + return noModifications + } + + 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) + target.isInRegularRAM(namespace) + else + false + } + else -> false + } + } + override fun after(functionCall: FunctionCall, parent: Node): Iterable { if(functionCall.target.nameInSource == listOf("lsb")) { val arg = functionCall.args[0] diff --git a/compiler/src/prog8/optimizer/ExpressionSplitter.kt b/compiler/src/prog8/optimizer/ExpressionSplitter.kt deleted file mode 100644 index 36b3d2d1c..000000000 --- a/compiler/src/prog8/optimizer/ExpressionSplitter.kt +++ /dev/null @@ -1,91 +0,0 @@ -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 -import prog8.ast.statements.VarDecl - -class ExpressionSplitter(private val program: Program) : AstWalker() { - - // TODO once this works, integrate it back into expressionsimplifier - override fun after(decl: VarDecl, parent: Node): Iterable { - -// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: -// if(decl.type==VarDeclType.VAR ) { -// val binExpr = decl.value as? BinaryExpression -// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) { -// // split into a vardecl with just the left expression, and an aug. assignment with the right expression. -// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position) -// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) -// val assign = Assignment(target, augExpr, binExpr.position) -// println("SPLIT VARDECL $decl") -// return listOf( -// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl), -// IAstModification.InsertAfter(decl, assign, parent) -// ) -// } -// } - - return emptyList() - } - - // TODO once this works, integrate it back into expressionsimplifier - override fun after(assignment: Assignment, parent: Node): Iterable { - - val binExpr = assignment.value as? BinaryExpression - if (binExpr != 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 = BinExpr X = LeftExpr - followed by - / \ IF 'X' not used X = BinExpr - / \ IN LEFTEXPR ==> - / \ / \ - LeftExpr. RightExpr. / \ - / \ / \ X RightExpr. - .. .. .. .. - - */ - if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) { - if (!assignment.isAugmentable) { - val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position) - val targetExpr = assignment.target.toExpression() - val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position) - return listOf( - IAstModification.InsertBefore(assignment, firstAssign, parent), - IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) - } - } - - // TODO further unraveling of binary expression trees into flat statements. - // however this should probably be done in a more generic way to also service - // the expressiontrees that are not used in an assignment statement... - } - - 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) - target.isInRegularRAM(namespace) - else - false - } - else -> false - } - } -} diff --git a/compiler/src/prog8/optimizer/Extensions.kt b/compiler/src/prog8/optimizer/Extensions.kt index 203c1999f..87800312e 100644 --- a/compiler/src/prog8/optimizer/Extensions.kt +++ b/compiler/src/prog8/optimizer/Extensions.kt @@ -53,10 +53,3 @@ internal fun Program.simplifyExpressions() : Int { opti.visit(this) return opti.applyModifications() } - -internal fun Program.splitExpressions() : Int { - val splitter = ExpressionSplitter(this) - splitter.visit(this) - return splitter.applyModifications() -} -