diff --git a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt deleted file mode 100644 index 3b57e03ba..000000000 --- a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt +++ /dev/null @@ -1,71 +0,0 @@ -package prog8.optimizer - -import prog8.ast.IStatementContainer -import prog8.ast.Node -import prog8.ast.Program -import prog8.ast.expressions.BinaryExpression -import prog8.ast.statements.AssignTarget -import prog8.ast.statements.Assignment -import prog8.ast.statements.AssignmentOrigin -import prog8.ast.walk.AstWalker -import prog8.ast.walk.IAstModification -import prog8.code.core.AugmentAssignmentOperators -import prog8.code.core.CompilationOptions -import prog8.code.core.DataType -import prog8.code.target.VMTarget - - -class BinExprSplitter(private val program: Program, private val options: CompilationOptions) : AstWalker() { - - override fun after(assignment: Assignment, parent: Node): Iterable { - - if(options.compTarget.name == VMTarget.NAME) - return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well - - if(assignment.value.inferType(program) istype DataType.FLOAT) - return noModifications - - val binExpr = assignment.value as? BinaryExpression - if (binExpr != null) { - - if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) { - if(assignment.target isSameAs binExpr.right) - return noModifications - if(assignment.target isSameAs binExpr.left) { - if(binExpr.right.isSimple) - return noModifications - val leftBx = binExpr.left as? BinaryExpression - if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple)) - return noModifications - val rightBx = binExpr.right as? BinaryExpression - if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple)) - return noModifications - } - - if(binExpr.right.isSimple) { - val firstAssign = Assignment(assignment.target.copy(), binExpr.left, AssignmentOrigin.OPTIMIZER, binExpr.left.position) - val targetExpr = assignment.target.toExpression() - val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position) - return listOf( - IAstModification.ReplaceNode(binExpr, augExpr, assignment), - IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer) - ) - } - } - - // Further unraveling of binary expressions is really complicated here and - // often results in much bigger code, thereby defeating the purpose a bit. - // All in all this should probably be fixed in a better code generation backend - // that doesn't require this at all. - } - - return noModifications - } - - private fun isSimpleTarget(target: AssignTarget) = - if (target.identifier!=null || target.memoryAddress!=null) - !target.isIOAddress(options.compTarget.machine) - else - false - -} diff --git a/codeOptimizers/src/prog8/optimizer/Extensions.kt b/codeOptimizers/src/prog8/optimizer/Extensions.kt index 66c8ac89f..e5542b627 100644 --- a/codeOptimizers/src/prog8/optimizer/Extensions.kt +++ b/codeOptimizers/src/prog8/optimizer/Extensions.kt @@ -65,9 +65,3 @@ fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarg opti.visit(this) return opti.applyModifications() } - -fun Program.splitBinaryExpressions(options: CompilationOptions) : Int { - val opti = BinExprSplitter(this, options) - opti.visit(this) - return opti.applyModifications() -} diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 5e65e4fff..9848db539 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -355,12 +355,11 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e while (true) { // keep optimizing expressions and statements until no more steps remain val optsDone1 = program.simplifyExpressions(errors, compTarget) - val optsDone2 = program.splitBinaryExpressions(compilerOptions) - val optsDone3 = program.optimizeStatements(errors, functions, compilerOptions) - val optsDone4 = program.inlineSubroutines(compilerOptions) + val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions) + val optsDone3 = program.inlineSubroutines(compilerOptions) program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away errors.report() - if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0) + if (optsDone1 + optsDone2 + optsDone3 == 0) break } val remover2 = UnusedCodeRemover(program, errors, compTarget) diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 31b97659e..2754f5bbd 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -120,36 +120,31 @@ class TestOptimization: FunSpec({ // load_location = 12345 // word llw // llw = 12345 -// cx16.r0 = load_location -// cx16.r0 += 10000 -// cx16.r2 = load_location -// cx16.r2 += 10000 -// cx16.r4 = load_location -// cx16.r4 += 22 -// cx16.r5s = llw -// cx16.r5s -= 1899 -// cx16.r7s = llw -// cx16.r7s += 99 +// cx16.r0 = load_location + 10000 +// cx16.r2 = load_location + 10000 +// cx16.r4 = load_location + 22 +// cx16.r5s = llw - 1899 +// cx16.r7s = llw + 99 val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 14 + stmts.size shouldBe 9 - val addR0value = (stmts[5] as Assignment).value + val addR0value = (stmts[4] as Assignment).value val binexpr0 = addR0value as BinaryExpression binexpr0.operator shouldBe "+" binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY) - val addR2value = (stmts[7] as Assignment).value + val addR2value = (stmts[5] as Assignment).value val binexpr2 = addR2value as BinaryExpression binexpr2.operator shouldBe "+" binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY) - val addR4value = (stmts[9] as Assignment).value + val addR4value = (stmts[6] as Assignment).value val binexpr4 = addR4value as BinaryExpression binexpr4.operator shouldBe "+" binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 22.0, Position.DUMMY) - val subR5value = (stmts[11] as Assignment).value + val subR5value = (stmts[7] as Assignment).value val binexpr5 = subR5value as BinaryExpression binexpr5.operator shouldBe "-" binexpr5.right shouldBe NumericLiteral(DataType.UWORD, 1899.0, Position.DUMMY) - val subR7value = (stmts[13] as Assignment).value + val subR7value = (stmts[8] as Assignment).value val binexpr7 = subR7value as BinaryExpression binexpr7.operator shouldBe "+" binexpr7.right shouldBe NumericLiteral(DataType.UWORD, 99.0, Position.DUMMY) @@ -171,44 +166,34 @@ class TestOptimization: FunSpec({ // expected: // word llw // llw = 300 -// cx16.r0s = llw -// cx16.r0s *= 180 -// cx16.r1s = llw -// cx16.r1s *= 180 -// cx16.r2s = llw -// cx16.r2s /= 90 -// cx16.r3s = llw -// cx16.r3s *= 5 -// cx16.r4s = llw -// cx16.r4s *= 90 -// cx16.r4s /= 5 +// cx16.r0s = llw * 180 +// cx16.r1s = llw * 180 +// cx16.r2s = llw / 90 +// cx16.r3s = llw * 5 +// cx16.r4s = llw * 90 / 5 val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 13 + stmts.size shouldBe 7 - val mulR0Value = (stmts[3] as Assignment).value + val mulR0Value = (stmts[2] as Assignment).value val binexpr0 = mulR0Value as BinaryExpression binexpr0.operator shouldBe "*" binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY) - val mulR1Value = (stmts[5] as Assignment).value + val mulR1Value = (stmts[3] as Assignment).value val binexpr1 = mulR1Value as BinaryExpression binexpr1.operator shouldBe "*" binexpr1.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY) - val divR2Value = (stmts[7] as Assignment).value + val divR2Value = (stmts[4] as Assignment).value val binexpr2 = divR2Value as BinaryExpression binexpr2.operator shouldBe "/" binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY) - val mulR3Value = (stmts[9] as Assignment).value + val mulR3Value = (stmts[5] as Assignment).value val binexpr3 = mulR3Value as BinaryExpression binexpr3.operator shouldBe "*" binexpr3.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY) - val mulR4Value = (stmts[11] as Assignment).value + val mulR4Value = (stmts[6] as Assignment).value val binexpr4 = mulR4Value as BinaryExpression - binexpr4.operator shouldBe "*" - binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY) - val divR4Value = (stmts[12] as Assignment).value - val binexpr4b = divR4Value as BinaryExpression - binexpr4b.operator shouldBe "/" - binexpr4b.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY) + binexpr4.operator shouldBe "/" + binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY) } test("constantfolded and silently typecasted for initializervalues") { @@ -377,14 +362,12 @@ class TestOptimization: FunSpec({ uword z4 z4 = 0 ubyte z5 - z5 = z1 - z5 += 5 + z5 = z1 + 5 ubyte z6 - z6 = z1 - z6 -= 5 + z6 = z1 - 5 */ val statements = result.compilerAst.entrypoint.statements - statements.size shouldBe 14 + statements.size shouldBe 12 val z1decl = statements[0] as VarDecl val z1init = statements[1] as Assignment val z2decl = statements[2] as VarDecl @@ -395,10 +378,8 @@ class TestOptimization: FunSpec({ val z4init = statements[7] as Assignment val z5decl = statements[8] as VarDecl val z5init = statements[9] as Assignment - val z5plus = statements[10] as Assignment - val z6decl = statements[11] as VarDecl - val z6init = statements[12] as Assignment - val z6plus = statements[13] as Assignment + val z6decl = statements[10] as VarDecl + val z6init = statements[11] as Assignment z1decl.name shouldBe "z1" z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY) @@ -409,15 +390,11 @@ class TestOptimization: FunSpec({ z4decl.name shouldBe "z4" z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) z5decl.name shouldBe "z5" - (z5init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1") - z5plus.isAugmentable shouldBe true - (z5plus.value as BinaryExpression).operator shouldBe "+" - (z5plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY) + (z5init.value as BinaryExpression).operator shouldBe "+" + (z5init.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY) z6decl.name shouldBe "z6" - (z6init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1") - z6plus.isAugmentable shouldBe true - (z6plus.value as BinaryExpression).operator shouldBe "-" - (z6plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY) + (z6init.value as BinaryExpression).operator shouldBe "-" + (z6init.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY) } test("force_output option should work with optimizing memwrite assignment") { @@ -435,7 +412,7 @@ class TestOptimization: FunSpec({ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 6 + stmts.size shouldBe 5 val assign=stmts.last() as Assignment (assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa") } @@ -452,7 +429,7 @@ class TestOptimization: FunSpec({ """ val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!! val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 6 + stmts.size shouldBe 5 val assign=stmts.last() as Assignment (assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa") } @@ -548,9 +525,9 @@ class TestOptimization: FunSpec({ xx += 6 */ val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 8 + stmts.size shouldBe 7 stmts.filterIsInstance().size shouldBe 3 - stmts.filterIsInstance().size shouldBe 5 + stmts.filterIsInstance().size shouldBe 4 } test("only substitue assignments with 0 after a =0 initializer if it is the same variable") { diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 889d2032c..52464c000 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -95,19 +95,16 @@ main { ubyte ub3 ub3 = 1 ubyte @shared bvalue - bvalue = ub1 - bvalue ^= ub2 - bvalue ^= ub3 - bvalue ^= 1 + bvalue = ub1 ^ ub2 ^ ub3 ^ true bvalue = (((ub1^ub2)^ub3)^(ftrue(99)!=0)) bvalue = ((ub1&ub2)&(ftrue(99)!=0)) return */ - stmts.size shouldBe 14 - val assignValue1 = (stmts[7] as Assignment).value as IdentifierReference - val assignValue2 = (stmts[11] as Assignment).value as BinaryExpression - val assignValue3 = (stmts[12] as Assignment).value as BinaryExpression - assignValue1.nameInSource shouldBe listOf("ub1") + stmts.size shouldBe 11 + val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression + val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression + val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression + assignValue1.operator shouldBe "^" assignValue2.operator shouldBe "^" assignValue3.operator shouldBe "&" val right2 = assignValue2.right as BinaryExpression diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 241c17245..6289ce662 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,10 +1,6 @@ TODO ==== -- (branch): fix float expressions codegen, it relied heavily on the evalstack (mandelbrot example zero division error) -- (branch): improve integer expression codegen even more to support even more cases? -- (branch): fully remove BinExprSplitter??? - - IR: instructions that do type conversion (SZ etc, CONCAT, SGN) should put the result in a DIFFERENT register. - IR: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction