get rid of binexpr splitter

This commit is contained in:
Irmen de Jong 2023-07-20 01:36:43 +02:00
parent d5707b7bf3
commit 70a78e74f6
6 changed files with 46 additions and 154 deletions

View File

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

View File

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

View File

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

View File

@ -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<VarDecl>().size shouldBe 3
stmts.filterIsInstance<Assignment>().size shouldBe 5
stmts.filterIsInstance<Assignment>().size shouldBe 4
}
test("only substitue assignments with 0 after a =0 initializer if it is the same variable") {

View File

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

View File

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