optimize memory read expression of ptr + constant index

This commit is contained in:
Irmen de Jong 2021-01-15 21:33:57 +01:00
parent 76d54fbe5c
commit 1481f92cb0
4 changed files with 109 additions and 76 deletions

View File

@ -1457,6 +1457,45 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
internal fun translateDirectMemReadExpression(expr: DirectMemoryRead, pushResultOnEstack: Boolean) { 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) { when(expr.addressExpression) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
val address = (expr.addressExpression as NumericLiteralValue).number.toInt() val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
@ -1470,22 +1509,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(pushResultOnEstack) if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex") asmgen.out(" sta P8ESTACK_LO,x | dex")
} }
else -> { is BinaryExpression -> {
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) if(!tryOptimizedPointerAccess(expr.addressExpression as BinaryExpression))
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { assignViaExprEval()
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")
}
}
} }
else -> assignViaExprEval()
} }
} }

View File

@ -121,6 +121,24 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignRegisterByte(assign.target, CpuRegister.A) 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!! val value = assign.source.memory!!
when (value.addressExpression) { when (value.addressExpression) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
@ -131,25 +149,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference) assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
} }
is BinaryExpression -> { is BinaryExpression -> {
// try to optimize simple cases like assignment from ptr+1, ptr+2... if(!tryOptimizedPointerAccess(value.addressExpression as BinaryExpression))
var optimized = false assignViaExprEval(value.addressExpression)
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)
} }
else -> 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 addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue val addressLv = addressExpr as? NumericLiteralValue
fun storeViaExprEval() fun storeViaExprEval() {
{
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2)") 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") 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 { when {
addressLv != null -> { addressLv != null -> {
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}") 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) asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
} }
addressExpr is BinaryExpression -> { addressExpr is BinaryExpression -> {
// try to optimize simple cases like storing value to ptr+1, ptr+2... if(!tryOptimizedPointerAccess(addressExpr))
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)
storeViaExprEval() storeViaExprEval()
} }
else -> storeViaExprEval() else -> storeViaExprEval()
@ -2006,8 +2008,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val addressLv = addressExpr as? NumericLiteralValue val addressLv = addressExpr as? NumericLiteralValue
val registerName = register.name.toLowerCase() val registerName = register.name.toLowerCase()
fun storeViaExprEval() fun storeViaExprEval() {
{
asmgen.saveRegisterStack(register, false) asmgen.saveRegisterStack(register, false)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.A, false) 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") 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 { when {
addressLv != null -> { addressLv != null -> {
asmgen.out(" st$registerName ${addressLv.number.toHex()}") 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) asmgen.storeByteIntoPointer(addressExpr, null)
} }
addressExpr is BinaryExpression -> { addressExpr is BinaryExpression -> {
// try to optimize simple cases like storing value to ptr+1, ptr+2... if(!tryOptimizedPointerAccess(addressExpr))
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)
storeViaExprEval() storeViaExprEval()
} }
else -> storeViaExprEval() else -> storeViaExprEval()

View File

@ -2,7 +2,8 @@
TODO 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 - 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 - 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 - 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

View File

@ -23,10 +23,11 @@ main {
txt.nl() txt.nl()
txt.nl() txt.nl()
cc=0
txt.chrout(@(ptr)) txt.chrout(@(ptr)+cc)
txt.chrout(@(ptr+1)) txt.chrout(@(ptr+1)+cc)
txt.chrout(@(ptr+2)) txt.chrout(@(ptr+2)+cc)
txt.nl() txt.nl()
@(ptr) = '1' @(ptr) = '1'