From acc942f690413e26cfdf3bc93450f8c81846f18e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 22 Aug 2020 22:53:21 +0200 Subject: [PATCH] added some more asm code optimizations by splitting certain assignments --- .../prog8/ast/expressions/AstExpressions.kt | 1 + .../src/prog8/ast/processing/AstWalker.kt | 12 ++ .../compiler/BeforeAsmGenerationAstChanger.kt | 19 ++++ compiler/src/prog8/compiler/Main.kt | 2 +- .../codegen/AugmentableAssignmentAsmGen.kt | 103 +++++++++++++----- 5 files changed, 111 insertions(+), 26 deletions(-) diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index a5cd21e2e..cdcac3f25 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -16,6 +16,7 @@ import kotlin.math.abs val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") +val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") sealed class Expression: Node { diff --git a/compiler/src/prog8/ast/processing/AstWalker.kt b/compiler/src/prog8/ast/processing/AstWalker.kt index 750e2408b..0852af221 100644 --- a/compiler/src/prog8/ast/processing/AstWalker.kt +++ b/compiler/src/prog8/ast/processing/AstWalker.kt @@ -61,6 +61,18 @@ interface IAstModification { } } + class InsertBefore(val before: Statement, val stmt: Statement, val parent: Node) : IAstModification { + override fun perform() { + if(parent is INameScope) { + val idx = parent.statements.indexOfFirst { it===before } + parent.statements.add(idx, stmt) + stmt.linkParents(parent) + } else { + throw FatalAstException("parent of an insert modification is not an INameScope") + } + } + } + class ReplaceNode(val node: Node, val replacement: Node, val parent: Node) : IAstModification { override fun perform() { parent.replaceChildNode(node, replacement) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 7ed95a3ee..02335fa2a 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -21,6 +21,25 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E return noModifications } + override fun after(assignment: Assignment, parent: Node): Iterable { + // Try to replace A = B Something by A= B, A = A Something + // this triggers the more efficent augmented assignment code generation more often. + if(!assignment.isAugmentable + && assignment.target.identifier != null + && assignment.target.isNotMemory(program.namespace)) { + val binExpr = assignment.value as? BinaryExpression + if(binExpr!=null && binExpr.operator !in comparisonOperators) { + if(binExpr.left !is BinaryExpression) { + val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position) + return listOf( + IAstModification.InsertBefore(assignment, assignLeft, parent), + IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)) + } + } + } + return noModifications + } + override fun after(scope: AnonymousScope, parent: Node): Iterable { val decls = scope.statements.filterIsInstance() val sub = scope.definingSubroutine() diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index d62586ed0..0a59c6441 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -193,7 +193,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: programAst.processAstBeforeAsmGeneration(errors) errors.handle() - printAst(programAst) + // printAst(programAst) val assembly = CompilationTarget.asmGenerator( programAst, diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt index d11e5ec4a..e9f3ba609 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt @@ -188,13 +188,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // TODO more specialized code for types such as memory read etc. value is TypecastExpression -> { if (tryRemoveRedundantCast(value, target, operator, origAssign)) return - inplaceModification_byte_value_to_memory(name, operator, value, origAssign) + inplaceModification_byte_value_to_memory(name, operator, value) } - else -> inplaceModification_byte_value_to_memory(name, operator, value, origAssign) + else -> inplaceModification_byte_value_to_memory(name, operator, value) } } else -> { - println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO + println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize... asmgen.translateExpression(memory.addressExpression) asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta ${C64Zeropage.SCRATCH_B1}") // the original memory byte's value is now in the scratch B1 location. @@ -245,8 +245,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, stx ${C64Zeropage.SCRATCH_REG_X} lda #<$name ldy #>$name - jsr c64flt.CONUPK - jsr c64flt.FADDT + jsr c64flt.FADD ldx #<$name ldy #>$name jsr c64flt.MOVMF @@ -259,8 +258,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, stx ${C64Zeropage.SCRATCH_REG_X} lda #<$name ldy #>$name - jsr c64flt.CONUPK - jsr c64flt.FSUBT + jsr c64flt.FSUB ldx #<$name ldy #>$name jsr c64flt.MOVMF @@ -284,9 +282,33 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference) { val valueDt = ident.targetVarDecl(program.namespace)!!.datatype - // TODO check valueDt + if(valueDt != DataType.FLOAT) + throw AssemblyError("float variable expected") + val otherName = asmgen.asmIdentifierName(ident) - TODO("Not yet implemented $name $operator= $otherName") + when (operator) { + "**" -> TODO("pow") + "+" -> TODO("+") + "-" -> TODO("-") + "*" -> { + asmgen.out(""" + stx ${C64Zeropage.SCRATCH_REG_X} + lda #<$name + ldy #>$name + jsr c64flt.MOVFM + lda #<$otherName + ldy #>$otherName + jsr c64flt.FMULT + ldx #<$name + ldy #>$name + jsr c64flt.MOVMF + ldx ${C64Zeropage.SCRATCH_REG_X} + """) + } + "/" -> TODO("div") + "%" -> TODO("float remainder???") + else -> throw AssemblyError("invalid operator for in-place float modification $operator") + } } private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) { @@ -303,8 +325,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, jsr c64flt.MOVFM lda #<$constValueName ldy #>$constValueName - jsr c64flt.CONUPK - jsr c64flt.FADDT + jsr c64flt.FADD ldx #<$name ldy #>$name jsr c64flt.MOVMF @@ -321,32 +342,29 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, jsr c64flt.MOVFM lda #<$name ldy #>$name - jsr c64flt.CONUPK - jsr c64flt.FSUBT + jsr c64flt.FSUB ldx #<$name ldy #>$name jsr c64flt.MOVMF ldx ${C64Zeropage.SCRATCH_REG_X} """) } - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "*" -> TODO("mul") "/" -> { if (value == 0.0) throw AssemblyError("division by zero") TODO("div") - // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") } "%" -> { + if (value == 0.0) + throw AssemblyError("division by zero") TODO("float remainder???") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") } else -> throw AssemblyError("invalid operator for in-place float modification $operator") } } - private fun inplaceModification_byte_value_to_memory(pointername: String, operator: String, value: Expression, origAssign: Assignment) { + private fun inplaceModification_byte_value_to_memory(pointername: String, operator: String, value: Expression) { println("warning: slow stack evaluation used (3): @($pointername) $operator= ${value::class.simpleName} at ${value.position}") // TODO asmgen.translateExpression(value) fun loadByteFromPointerIntoA() { @@ -463,8 +481,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.out(" sec | sbc #$value | sta (${C64Zeropage.SCRATCH_W1}),y") } "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "/" -> { + if(value==0) + throw AssemblyError("division by zero") + TODO("div") + // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + } "%" -> { + if(value==0) + throw AssemblyError("division by zero") TODO("byte remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") @@ -567,8 +592,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, lda math.multiply_words.result+1 sta $name+1""") } - "/" -> TODO("word $name /= $value")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "/" -> { + if(value==0) + throw AssemblyError("division by zero") + TODO("word $name /= $value") + // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + } "%" -> { + if(value==0) + throw AssemblyError("division by zero") TODO("word remainder $value") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") @@ -903,6 +935,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, "&" -> asmgen.out(" lda $name | and $otherName | sta $name") "^" -> asmgen.out(" lda $name | xor $otherName | sta $name") "|" -> asmgen.out(" lda $name | ora $otherName | sta $name") + "and" -> asmgen.out(""" + lda $name + and $otherName + beq + + lda #1 ++ sta $name""") + "or" -> asmgen.out(""" + lda $name + ora $otherName + beq + + lda #1 ++ sta $name""") + "xor" -> asmgen.out(""" + lda $name + eor $otherName + beq + + lda #1 ++ sta $name""") else -> throw AssemblyError("invalid operator for in-place modification $operator") } } @@ -932,10 +982,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } "%" -> { - TODO("$dt remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") + if(dt==DataType.BYTE) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(""" + lda $name + ldy #$value + jsr math.divmod_ub + sta $name""") } "<<" -> { repeat(value) { asmgen.out(" asl $name") }