implementing Rpn optimizations

This commit is contained in:
Irmen de Jong 2023-03-18 00:16:18 +01:00
parent 5b0e1b4f9e
commit e8bebe5a75
7 changed files with 143 additions and 189 deletions

View File

@ -48,11 +48,11 @@ class AsmGen6502Internal (
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, symbolTable, this, assignmentAsmGen)
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, assignmentAsmGen, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, this)
fun compileToAssembly(): IAssemblyProgram? {
@ -1007,36 +1007,53 @@ $repeatLabel lda $counterVar
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if (pointerOffsetExpr is PtRpn) {
return rpnAssignmentAsmGen.pointerViaIndexRegisterPossible(pointerOffsetExpr)
}
else if (pointerOffsetExpr is PtBinaryExpression) {
if (pointerOffsetExpr.operator != "+") return null
val leftDt = pointerOffsetExpr.left.type
val rightDt = pointerOffsetExpr.left.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = pointerOffsetExpr.right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
return Pair(pointerOffsetExpr.left, PtNumber(DataType.UBYTE, constIdx.number, constIdx.position))
}
// could be that the index was typecasted into uword, check that
val rightTc = pointerOffsetExpr.right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
return Pair(pointerOffsetExpr.left, rightTc.value)
val leftTc = pointerOffsetExpr.left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
return Pair(pointerOffsetExpr.right, leftTc.value)
val left: PtExpression
val right: PtExpression
val operator: String
when (pointerOffsetExpr) {
is PtRpn -> {
if(pointerOffsetExpr.children.size>3)
return null // expression is too complex, we need just a pointer var + index
val (leftNode, oper, rightNode) = pointerOffsetExpr.finalOperation()
operator=oper.operator
if (leftNode !is PtExpression || rightNode !is PtExpression) return null
left = leftNode
right = rightNode
}
is PtBinaryExpression -> {
operator = pointerOffsetExpr.operator
left = pointerOffsetExpr.left
right = pointerOffsetExpr.right
}
else -> return null
}
if (operator != "+") return null
val leftDt = left.type
val rightDt = right.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
return Pair(left, right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
return Pair(right, left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
return Pair(left, PtNumber(DataType.UBYTE, constIdx.number, constIdx.position))
}
// could be that the index was typecasted into uword, check that
val rightTc = right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
return Pair(left, rightTc.value)
val leftTc = left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
return Pair(right, leftTc.value)
}
return null
}
internal fun tryOptimizedPointerAccessWithA(expr: PtBinaryExpression, write: Boolean): Boolean {
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtExpression, operator: String, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
@ -1052,8 +1069,8 @@ $repeatLabel lda $counterVar
}
}
if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? PtIdentifier
val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode
@ -2882,7 +2899,8 @@ $repeatLabel lda $counterVar
out(" sta P8ESTACK_LO,x | dex")
}
is PtBinaryExpression -> {
if(tryOptimizedPointerAccessWithA(expr.address as PtBinaryExpression, false)) {
val addrExpr = expr.address as PtBinaryExpression
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {
@ -2890,7 +2908,8 @@ $repeatLabel lda $counterVar
}
}
is PtRpn -> {
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(expr.address as PtRpn, false)) {
val addrExpr = expr.address as PtRpn
if(addrExpr.children.size==3 && tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {

View File

@ -1,18 +1,14 @@
package prog8.codegen.cpu6502
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
private val asmgen: AsmGen6502Internal,
private val assignAsmGen: AssignmentAsmGen) {
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, assignAsmGen, asmgen)
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
}
@ -665,7 +661,29 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return
}
}
is PtRpn -> if(rpnAssignmentAsmGen.funcPokeW(fcall)) return
is PtRpn -> {
if(addrExpr.children.size==3) {
// we want just one '+' operator
val (left, oper, right) = addrExpr.finalOperation()
if(oper.operator=="+" && left is PtIdentifier && right is PtNumber) {
val varname = asmgen.asmVariableName(left)
if(asmgen.isZpVar(left)) {
// pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val index = right.number.toHex()
asmgen.out("""
ldy #$index
sta ($varname),y
txa
iny
sta ($varname),y""")
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
}
}
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
@ -695,6 +713,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
fun fallback() {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
when(val addrExpr = fcall.args[0]) {
is PtNumber -> {
val addr = addrExpr.number.toHex()
@ -720,12 +742,29 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
tay
pla""")
}
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
} else fallback()
}
is PtRpn -> {
if(addrExpr.children.size==3) {
// must be 3 (one '+' operator), otherwise expression is too complex for this
val (left, oper, right) = addrExpr.finalOperation()
if(oper.operator=="+" && left is PtIdentifier && right is PtNumber) {
val varname = asmgen.asmVariableName(left)
if(asmgen.isZpVar(left)) {
// pointervar is already in the zero page, no need to copy
val index = right.number.toHex()
asmgen.out("""
ldy #$index
lda ($varname),y
pha
iny
lda ($varname),y
tay
pla""")
} else fallback()
} else fallback()
} else fallback()
}
is PtRpn -> rpnAssignmentAsmGen.funcPeekW(fcall, resultToStack, resultRegister)
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
@ -740,19 +779,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda ($varname),y
tay
pla""")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
else -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
} else fallback()
} else fallback()
}
else -> fallback()
}
if(resultToStack){

View File

@ -25,7 +25,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
when(expression) {
is PtPrefix -> translateExpression(expression)
is PtBinaryExpression -> translateExpression(expression)
is PtRpn -> translateExpression(expression)
is PtRpn -> translateRpnExpression(expression)
is PtArrayIndexer -> translateExpression(expression)
is PtTypeCast -> translateExpression(expression)
is PtAddressOf -> translateExpression(expression)
@ -245,7 +245,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
private fun translateExpression(expr: PtRpn) {
private fun translateRpnExpression(expr: PtRpn) {
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
val oper = expr.finalOperator()
val leftDt = oper.leftType
@ -265,6 +265,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
return translateCompareStrings(expr.finalLeftOperand() as PtExpression, oper.operator, expr.finalRightOperand() as PtExpression)
}
// TODO: RPN: add the other optimizations that BinaryExpression has, to avoid eval stack usage
// any other expression
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))

View File

@ -9,11 +9,11 @@ import prog8.codegen.cpu6502.returnsWhatWhere
internal class AssignmentAsmGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
symbolTable: SymbolTable,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, this, asmgen)
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, asmgen)
fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
@ -145,14 +145,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
}
is PtRpn -> {
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(value.address as PtRpn, false)) {
val addrExpr = value.address as PtRpn
if(addrExpr.children.size==3 && asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.address)
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.address)
@ -313,7 +315,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
is PtRpn -> {
if(!rpnAssignmentAsmGen.attemptAssignOptimizedExpr(value, assign)) {
if(!rpnAssignmentAsmGen.attemptAssignOptimizedExpr(assign)) {
// All remaining binary expressions just evaluate via the stack for now.
// TODO: For RPN expressions this should never occur anymore and the eval stack should be removed when we achieve this
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
@ -920,7 +922,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
}
is PtRpn -> {
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(value.address as PtRpn, false)) {
val addrExpr = value.address as PtRpn
if(addrExpr.children.size==3 && asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
@ -928,7 +931,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
@ -2849,11 +2853,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoPointerVar(addressExpr)
}
addressExpr is PtRpn -> {
if(!rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(addressExpr, true))
if(addressExpr.children.size!=3 || !asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.finalOperator().operator, true))
storeViaExprEval()
}
addressExpr is PtBinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
storeViaExprEval()
}
else -> storeViaExprEval()

View File

@ -1,129 +1,21 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.CpuRegister
import prog8.code.core.DataType
import prog8.code.core.RegisterOrPair
import prog8.code.ast.PtProgram
import prog8.code.ast.PtRpn
import prog8.codegen.cpu6502.AsmGen6502Internal
internal class RpnExpressionAsmGen(
val program: PtProgram,
val symbolTable: SymbolTable,
val assignmentAsmGen: AssignmentAsmGen,
val asmgen: AsmGen6502Internal
) {
internal fun tryOptimizedPointerAccessWithA(expr: PtRpn, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
val dt = expr.type
if(dt != DataType.UBYTE && dt != DataType.BYTE)
return true
return when(expr) {
is PtIdentifier -> false
is PtNumber -> false
is PtMemoryByte -> expr.address !is PtIdentifier && expr.address !is PtNumber
is PtTypeCast -> evalBytevalueWillClobberA(expr.value)
else -> true
}
}
if(expr.finalOperator().operator=="+") {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? PtIdentifier
val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode
when(target) {
is PtLabel -> {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
asmgen.out(" lda ${asmgen.asmSymbolName(pointervar!!)},y")
return true
}
is IPtVariable, null -> {
if(write) {
if(pointervar!=null && asmgen.isZpVar(pointervar)) {
val saveA = evalBytevalueWillClobberA(ptrAndIndex.second)
if(saveA)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA)
asmgen.out(" pla")
asmgen.out(" sta (${asmgen.asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
if(saveA)
asmgen.out(" pha")
if(ptrAndIndex.second.isSimple()) {
asmgen. assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA)
asmgen.out(" pla")
asmgen.out(" sta (P8ZP_SCRATCH_W2),y")
} else {
asmgen.pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
asmgen.assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(saveA)
asmgen.out(" pla")
asmgen.out(" sta (P8ZP_SCRATCH_W2),y")
}
}
} else {
if(pointervar!=null && asmgen.isZpVar(pointervar)) {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
asmgen.out(" lda (${asmgen.asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
if(ptrAndIndex.second.isSimple()) {
asmgen.assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
asmgen.out(" lda (P8ZP_SCRATCH_W2),y")
} else {
asmgen.pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
asmgen.assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda (P8ZP_SCRATCH_W2),y")
}
}
}
return true
}
else -> throw AssemblyError("invalid pointervar $pointervar")
}
}
}
fun attemptAssignOptimizedExpr(assign: AsmAssignment): Boolean {
val value = assign.source.expression as PtRpn
println("TODO: RPN: optimized assignment ${value.position}") // TODO RPN: optimized assignment
// NOTE: don't forgot to evaluate the rest of the RPN expr as well
return false
}
fun attemptAssignOptimizedExpr(expr: PtRpn, assign: AsmAssignment): Boolean {
println("TODO: RPN: optimized assignment ${expr.position}") // TODO RPN: optimized assignment
return false
}
fun funcPeekW(
fcall: PtBuiltinFunctionCall,
resultToStack: Boolean,
resultRegister: RegisterOrPair?
) {
println("TODO: RPN: peekw optimized pointer+index ${fcall.position}") // TODO RPN: peekw optimized pointer+index
// val (left, oper, right) = addrExpr.finalOperation()
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
fun funcPokeW(fcall: PtBuiltinFunctionCall): Boolean {
println("TODO: RPN: pokew optimized pointer+index ${fcall.position}") // TODO RPN: pokew optimized pointer+index
// val (left, oper, right) = addrExpr.finalOperation()
// for now: fall through
return false
}
fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtRpn): Pair<PtExpression, PtExpression>? {
TODO("RPN determine pointer+index via reg.") // however, is this ever getting called from RPN code?
}
}

View File

@ -1,7 +1,9 @@
TODO
====
BRANCH: Fix the TODO RPN routines to be optimized assembly
BRANCH: Fix the TODO RPN routines to be optimized assembly in RpnExpressionAsmGen.kt
Implement RPN codegen for IR.
For next minor release
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -3,6 +3,8 @@
%zeropage basicsafe
%option no_sysinit
; $1e4 size
main {
sub start() {
@ -10,17 +12,20 @@ main {
uword xx=32
cx16.r0L = 3
if cx16.r0L in "derp" {
xx++
}
cx16.r0 = peekw(xx + 44)
@(xx+44) = cx16.r0L
xx = xx+(3*func(xx)+xx*2*cx16.r0L)
txt.print_uw(xx)
; if cx16.r0L in "derp" {
; xx++
; }
;
; xx = xx+(3*func(xx)+xx*2*cx16.r0L)
; txt.print_uw(xx)
test_stack.test()
}
sub func(uword value) -> uword {
value ++
return value
}
; sub func(uword value) -> uword {
; value ++
; return value
; }
}