From fbe3ce008bd3598bd64a9467414e30b5bba4f6e2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 30 Jul 2020 01:30:21 +0200 Subject: [PATCH] slight expression rewrite in case of certain in-place assignments, to try to get the in-place variable operand to the leftmost position --- compiler/src/prog8/ast/AstToSourceCode.kt | 1 + .../ast/processing/StatementReorderer.kt | 51 ++++++++++++++- .../src/prog8/ast/statements/AstStatements.kt | 29 +++++++++ compiler/src/prog8/compiler/Main.kt | 2 +- .../target/c64/codegen/AssignmentAsmGen.kt | 6 +- .../prog8/optimizer/ExpressionSimplifier.kt | 4 -- docs/source/syntaxreference.rst | 2 +- examples/test.p8 | 63 +++++++++---------- 8 files changed, 116 insertions(+), 42 deletions(-) diff --git a/compiler/src/prog8/ast/AstToSourceCode.kt b/compiler/src/prog8/ast/AstToSourceCode.kt index 026c4e8b3..e916cdd8a 100644 --- a/compiler/src/prog8/ast/AstToSourceCode.kt +++ b/compiler/src/prog8/ast/AstToSourceCode.kt @@ -289,6 +289,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): override fun visit(assignment: Assignment) { val binExpr = assignment.value as? BinaryExpression if(binExpr!=null && binExpr.left isSameAs assignment.target) { + // we only support the inplace assignments of the form A = A assignment.target.accept(this) output(" ${binExpr.operator}= ") binExpr.right.accept(this) diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 6d71bae82..89f8e80d7 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -14,8 +14,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() { // - in every scope, most directives and vardecls are moved to the top. // - the 'start' subroutine is moved to the top. // - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement. - // - (syntax desugaring) augmented assignment is turned into regular assignment. // - (syntax desugaring) struct value assignment is expanded into several struct member assignments. + // - in-place assignments are reordered a bit so that they are mostly of the form A = A // - sorts the choices in when statement. // - insert AddressOf (&) expression where required (string params to a UWORD function param etc). @@ -118,6 +118,55 @@ internal class StatementReorderer(val program: Program) : AstWalker() { return noModifications } + override fun after(assignment: Assignment, parent: Node): Iterable { + // rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand + val binExpr = assignment.value as? BinaryExpression + if(binExpr!=null) { + if(binExpr.left isSameAs assignment.target) { + // A = A 5, unchanged + return noModifications + } + + if(binExpr.operator in associativeOperators) { + if (binExpr.right isSameAs assignment.target) { + // A = v A ==> A = A v + return listOf(IAstModification.SwapOperands(binExpr)) + } + + val leftBinExpr = binExpr.left as? BinaryExpression + if(leftBinExpr?.operator == binExpr.operator) { + return if(leftBinExpr.left isSameAs assignment.target) { + // A = (A x) y ==> A = A (x y) + val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position) + val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position) + listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) + } else { + // A = (x A) y ==> A = A (x y) + val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position) + val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position) + listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) + } + } + val rightBinExpr = binExpr.right as? BinaryExpression + if(rightBinExpr?.operator == binExpr.operator) { + return if(rightBinExpr.left isSameAs assignment.target) { + // A = x (A y) ==> A = A (x y) + val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.right, binExpr.position) + val newValue = BinaryExpression(rightBinExpr.left, binExpr.operator, newRight, binExpr.position) + listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) + } else { + // A = x (y A) ==> A = A (x y) + val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.left, binExpr.position) + val newValue = BinaryExpression(rightBinExpr.right, binExpr.operator, newRight, binExpr.position) + listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) + } + } + } + } + + return noModifications + } + private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List { val identifier = structAssignment.target.identifier!! val identifierName = identifier.nameInSource.single() diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 95abf97fd..4d6b4d4fc 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -348,6 +348,35 @@ open class Assignment(var target: AssignTarget, var value: Expression, override override fun toString(): String { return("Assignment(target: $target, value: $value, pos=$position)") } + + val isInplace: Boolean + get() { + val binExpr = value as? BinaryExpression + if(binExpr!=null) { + if(binExpr.left isSameAs target) + return true // A = A 5 + + if(binExpr.operator in associativeOperators) { + if (binExpr.right isSameAs target) + return true // A = v A + + val leftBinExpr = binExpr.left as? BinaryExpression + if(leftBinExpr?.operator == binExpr.operator) { + // A = (A x) y + // A = (x A) y + return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target + } + val rightBinExpr = binExpr.right as? BinaryExpression + if(rightBinExpr?.operator == binExpr.operator) { + // A = y (A x) + // A = y (x y) + return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target + } + } + } + + return false + } } data class AssignTarget(var identifier: IdentifierReference?, diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 75fc87ff1..86ca86348 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -192,7 +192,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: programAst.processAstBeforeAsmGeneration(errors) errors.handle() - printAst(programAst) // TODO + // printAst(programAst) val assembly = CompilationTarget.asmGenerator( programAst, diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 6db8206f0..c69f105f6 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -18,8 +18,10 @@ import prog8.compiler.toHex internal class AssignmentAsmGen(private val program: Program, private val errors: ErrorReporter, private val asmgen: AsmGen) { internal fun translate(assign: Assignment) { - // TODO check for in-place assignment A = A X and generate better code for that - translateNormalAssignment(assign) + if(assign.isInplace) + translateNormalAssignment(assign) // TODO generate better code here for in-place assignments + else + translateNormalAssignment(assign) } // old code-generation below: diff --git a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt index 477cacd03..3e92d4a9d 100644 --- a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt @@ -84,10 +84,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() } override fun after(expr: BinaryExpression, parent: Node): Iterable { - - // TODO: (A +/- B) +/- C ==> A +/- ( B +/- C) - // TODO: (A * / B) * / C ==> A * / ( B * / C) - val leftVal = expr.left.constValue(program) val rightVal = expr.right.constValue(program) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 15d0b2deb..9da90fb04 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -404,7 +404,7 @@ assignment: ``=`` Note that an assignment sometimes is not possible or supported. augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=`` - Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx`` + This is syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx`` postfix increment and decrement: ``++`` ``--`` Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``. diff --git a/examples/test.p8 b/examples/test.p8 index c2336a8d7..18d53a3c7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,11 +4,6 @@ %zeropage basicsafe %option enable_floats -; todo: add these to the Expression Simplifier so that all the below assignments become a simple in-place assignment: -; (A +/- B) +/- C ==> A +/- ( B +/- C) -; (A * / B) * / C ==> A * / ( B * / C) - - main { sub start() { @@ -16,35 +11,37 @@ main { ubyte wv ubyte wv2 - wv *= wv2 + wv = wv + wv + wv - wv += 10 - wv += 20 - wv += 30 - - wv += 1 + wv2 - wv += 2 + wv2 - wv += 3 + wv2 - - wv += wv2 + 1 - wv += wv2 + 2 - wv += wv2 + 3 - - wv = wv + 1 + wv2 - wv = wv + 2 + wv2 - wv = wv + 3 + wv2 - - wv = 1 + wv2 + wv - wv = 2 + wv2 + wv - wv = 3 + wv2 + wv - - wv = wv + wv2 + 1 - wv = wv + wv2 + 2 - wv = wv + wv2 + 3 - - wv = wv2 + 1 + wv - wv = wv2 + 2 + wv - wv = wv2 + 3 + wv +; wv *= wv2 +; +; wv += 10 +; wv += 20 +; wv += 30 +; +; wv += 1 + wv2 +; wv += 2 + wv2 +; wv += 3 + wv2 +; +; wv += wv2 + 1 +; wv += wv2 + 2 +; wv += wv2 + 3 +; +; wv = wv + 1 + wv2 +; wv = wv + 2 + wv2 +; wv = wv + 3 + wv2 +; +; wv = 1 + wv2 + wv +; wv = 2 + wv2 + wv +; wv = 3 + wv2 + wv +; +; wv = wv + wv2 + 1 +; wv = wv + wv2 + 2 +; wv = wv + wv2 + 3 +; +; wv = wv2 + 1 + wv +; wv = wv2 + 2 + wv +; wv = wv2 + 3 + wv wv = wv2 + wv + 1 wv = wv2 + wv + 2