From 1481f92cb0e407c8c286b86a38e9c024b0330325 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 15 Jan 2021 21:33:57 +0100 Subject: [PATCH] optimize memory read expression of ptr + constant index --- .../target/c64/codegen/ExpressionsAsmGen.kt | 58 ++++++--- .../codegen/assignment/AssignmentAsmGen.kt | 117 +++++++++--------- docs/source/todo.rst | 3 +- examples/test.p8 | 7 +- 4 files changed, 109 insertions(+), 76 deletions(-) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index 21e970a81..bf3dc9df1 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -1457,6 +1457,45 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } internal fun translateDirectMemReadExpression(expr: DirectMemoryRead, pushResultOnEstack: Boolean) { + + fun assignViaExprEval() { + asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { + if (pushResultOnEstack) { + asmgen.out(" dex | lda (P8ZP_SCRATCH_W2) | sta P8ESTACK_LO+1,x") + } else { + asmgen.out(" lda (P8ZP_SCRATCH_W2)") + } + } else { + if (pushResultOnEstack) { + asmgen.out(" dex | ldy #0 | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x") + } else { + asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y") + } + } + } + + fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean { + // try to optimize simple cases like assignment from ptr+1, ptr+2... + if(expr.operator=="+") { + // we can assume const operand has been moved to the right. + val constOperand = expr.right.constValue(program) + if(constOperand!=null) { + val intIndex = constOperand.number.toInt() + if(intIndex in 1..255) { + asmgen.assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + if (pushResultOnEstack) { + asmgen.out(" dex | ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x") + } else { + asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y") + } + return true + } + } + } + return false + } + when(expr.addressExpression) { is NumericLiteralValue -> { val address = (expr.addressExpression as NumericLiteralValue).number.toInt() @@ -1470,22 +1509,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge if(pushResultOnEstack) asmgen.out(" sta P8ESTACK_LO,x | dex") } - else -> { - asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) - if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { - if (pushResultOnEstack) { - asmgen.out(" dex | lda (P8ZP_SCRATCH_W2) | sta P8ESTACK_LO+1,x") - } else { - asmgen.out(" lda (P8ZP_SCRATCH_W2)") - } - } else { - if (pushResultOnEstack) { - asmgen.out(" dex | ldy #0 | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x") - } else { - asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y") - } - } + is BinaryExpression -> { + if(!tryOptimizedPointerAccess(expr.addressExpression as BinaryExpression)) + assignViaExprEval() } + else -> assignViaExprEval() } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index 2ef069baa..11e0e61fe 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -121,6 +121,24 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen assignRegisterByte(assign.target, CpuRegister.A) } + fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean { + // try to optimize simple cases like assignment from ptr+1, ptr+2... + if(expr.operator=="+") { + // we can assume const operand has been moved to the right. + val constOperand = expr.right.constValue(program) + if(constOperand!=null) { + val intIndex = constOperand.number.toInt() + if(intIndex in 1..255) { + assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope) + asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + } + } + return false + } + val value = assign.source.memory!! when (value.addressExpression) { is NumericLiteralValue -> { @@ -131,25 +149,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference) } is BinaryExpression -> { - // try to optimize simple cases like assignment from ptr+1, ptr+2... - var optimized = false - val expr = value.addressExpression as BinaryExpression - if(expr.operator=="+") { - // we can assume const operand has been moved to the right. - val constOperand = expr.right.constValue(program) - if(constOperand!=null) { - val intIndex = constOperand.number.toInt() - if(intIndex in 1..255) { - assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope) - asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y") - assignRegisterByte(assign.target, CpuRegister.A) - optimized = true - } - } - } - - if(!optimized) - assignViaExprEval(expr) + if(!tryOptimizedPointerAccess(value.addressExpression as BinaryExpression)) + assignViaExprEval(value.addressExpression) } else -> assignViaExprEval(value.addressExpression) } @@ -1961,8 +1962,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val addressExpr = memoryAddress.addressExpression val addressLv = addressExpr as? NumericLiteralValue - fun storeViaExprEval() - { + fun storeViaExprEval() { assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) asmgen.out(" lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2)") @@ -1970,6 +1970,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.out(" lda $ldaInstructionArg | ldy #0 | sta (P8ZP_SCRATCH_W2),y") } + fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean { + // try to optimize simple cases like storing value to ptr+1, ptr+2... + if(expr.operator=="+") { + // we can assume const operand has been moved to the right. + val constOperand = expr.right.constValue(program) + if(constOperand!=null) { + val intIndex = constOperand.number.toInt() + if(intIndex in 1..255) { + assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y") + return true + } + } + } + return false + } + when { addressLv != null -> { asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}") @@ -1978,22 +1995,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg) } addressExpr is BinaryExpression -> { - // try to optimize simple cases like storing value to ptr+1, ptr+2... - var optimized = false - if(addressExpr.operator=="+") { - // we can assume const operand has been moved to the right. - val constOperand = addressExpr.right.constValue(program) - if(constOperand!=null) { - val intIndex = constOperand.number.toInt() - if(intIndex in 1..255) { - assignExpressionToVariable(addressExpr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) - asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y") - optimized = true - } - } - } - - if(!optimized) + if(!tryOptimizedPointerAccess(addressExpr)) storeViaExprEval() } else -> storeViaExprEval() @@ -2006,8 +2008,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val addressLv = addressExpr as? NumericLiteralValue val registerName = register.name.toLowerCase() - fun storeViaExprEval() - { + fun storeViaExprEval() { asmgen.saveRegisterStack(register, false) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) asmgen.restoreRegisterStack(CpuRegister.A, false) @@ -2017,6 +2018,25 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y") } + fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean { + // try to optimize simple cases like storing value to ptr+1, ptr+2... + if(expr.operator=="+") { + // we can assume const operand has been moved to the right. + val constOperand = expr.right.constValue(program) + if(constOperand!=null) { + val intIndex = constOperand.number.toInt() + if(intIndex in 1..255) { + asmgen.saveRegisterStack(register, false) + assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + asmgen.restoreRegisterStack(CpuRegister.A, false) + asmgen.out(" ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y") + return true + } + } + } + return false + } + when { addressLv != null -> { asmgen.out(" st$registerName ${addressLv.number.toHex()}") @@ -2030,24 +2050,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.storeByteIntoPointer(addressExpr, null) } addressExpr is BinaryExpression -> { - // try to optimize simple cases like storing value to ptr+1, ptr+2... - var optimized = false - if(addressExpr.operator=="+") { - // we can assume const operand has been moved to the right. - val constOperand = addressExpr.right.constValue(program) - if(constOperand!=null) { - val intIndex = constOperand.number.toInt() - if(intIndex in 1..255) { - asmgen.saveRegisterStack(register, false) - assignExpressionToVariable(addressExpr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) - asmgen.restoreRegisterStack(CpuRegister.A, false) - asmgen.out(" ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y") - optimized = true - } - } - } - - if(!optimized) + if(!tryOptimizedPointerAccess(addressExpr)) storeViaExprEval() } else -> storeViaExprEval() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 9bd4d0914..398a0764f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,7 +2,8 @@ TODO ==== -- optimize pointer access code @(pointer)? use a subroutine? macro? 65c02 vs 6502? +- optimize pointer access if it's via a var that's already in ZP. in AssignmentAsmGen 3 times, + and in translateDirectMemReadExpression. like loadByteFromPointerIntoA() is doing. - can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50 - add a compiler option to generate a symbol listing at the end - optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation diff --git a/examples/test.p8 b/examples/test.p8 index 26ed31d0a..82197f270 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -23,10 +23,11 @@ main { txt.nl() txt.nl() + cc=0 - txt.chrout(@(ptr)) - txt.chrout(@(ptr+1)) - txt.chrout(@(ptr+2)) + txt.chrout(@(ptr)+cc) + txt.chrout(@(ptr+1)+cc) + txt.chrout(@(ptr+2)+cc) txt.nl() @(ptr) = '1'