a little rpn refactor

This commit is contained in:
Irmen de Jong 2023-03-17 00:45:35 +01:00
parent 8c0a93779b
commit 5b0e1b4f9e
11 changed files with 211 additions and 51 deletions

View File

@ -78,10 +78,10 @@ class PtProgram(
return rpn
}
val rpn = PtRpn(originalExpr.type, position)
val rpn = PtRpn(originalExpr.type, originalExpr.position)
rpn.addRpnNode(makeRpn(originalExpr.left))
rpn.addRpnNode(makeRpn(originalExpr.right))
rpn.addRpnNode(PtRpnOperator(originalExpr.operator, originalExpr.type, originalExpr.left.type, originalExpr.right.type, position))
rpn.addRpnNode(PtRpnOperator(originalExpr.operator, originalExpr.type, originalExpr.left.type, originalExpr.right.type, originalExpr.position))
return rpn
}

View File

@ -251,8 +251,8 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
children.withIndex().forEach { (index, node) ->
if (node is PtRpnOperator) {
pop(node.operand1Type)
pop(node.operand2Type)
pop(node.leftType)
pop(node.rightType)
push(node.type)
}
else {
@ -265,17 +265,18 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
return Pair(maxDepths, numPushes)
}
fun finalOperator() = children.last() as? PtRpnOperator
fun finalFirstOperand() = children[children.size-3]
fun finalSecondOperand() = children[children.size-2]
fun finalOperator() = children.last() as PtRpnOperator
fun finalLeftOperand() = children[children.size-3]
fun finalRightOperand() = children[children.size-2]
fun finalOperation() = Triple(finalLeftOperand(), finalOperator(), finalRightOperand())
}
class PtRpnOperator(val operator: String, val type: DataType, val operand1Type: DataType, val operand2Type: DataType, position: Position): PtNode(position) {
class PtRpnOperator(val operator: String, val type: DataType, val leftType: DataType, val rightType: DataType, position: Position): PtNode(position) {
init {
// NOTE: For now, we require that the types of the operands are the same size as the output type of the operator node.
if(operator !in ComparisonOperators) {
require(type equalsSize operand1Type && type equalsSize operand2Type) {
"operand type size(s) differ from operator result type $type: $operand1Type $operand2Type oper: $operator"
require(type equalsSize leftType && type equalsSize rightType) {
"operand type size(s) differ from operator result type $type: $leftType $rightType oper: $operator"
}
}
}

View File

@ -50,8 +50,9 @@ class AsmGen6502Internal (
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, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, symbolTable, this, assignmentAsmGen)
private val rpnAssignmentAsmGen = RpnExpressionAsmGen(program, symbolTable, assignmentAsmGen, this)
fun compileToAssembly(): IAssemblyProgram? {
@ -601,7 +602,7 @@ class AsmGen6502Internal (
}
private fun requireComparisonExpression(condition: PtExpression) {
if (!(condition is PtRpn && condition.finalOperator()?.operator in ComparisonOperators) && !(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
if (!(condition is PtRpn && condition.finalOperator().operator in ComparisonOperators) && !(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
throw AssemblyError("expected boolean comparison expression $condition")
}
@ -1007,7 +1008,7 @@ $repeatLabel lda $counterVar
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if (pointerOffsetExpr is PtRpn) {
TODO("RPN determine pointer+index via reg.") // however, is this ever getting called from RPN code?
return rpnAssignmentAsmGen.pointerViaIndexRegisterPossible(pointerOffsetExpr)
}
else if (pointerOffsetExpr is PtBinaryExpression) {
if (pointerOffsetExpr.operator != "+") return null
@ -2889,8 +2890,12 @@ $repeatLabel lda $counterVar
}
}
is PtRpn -> {
// TODO RPN: optimized memread address
assignViaExprEval()
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(expr.address as PtRpn, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {
assignViaExprEval()
}
}
else -> assignViaExprEval()
}

View File

@ -1,14 +1,18 @@
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)
}
@ -661,10 +665,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return
}
}
is PtRpn -> {
// TODO RPN: optimized addr+index
// for now: fall through
}
is PtRpn -> if(rpnAssignmentAsmGen.funcPokeW(fcall)) return
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
@ -724,11 +725,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
is PtRpn -> {
// TODO RPN: optimized pointer+index
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
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)

View File

@ -247,37 +247,37 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
private fun translateExpression(expr: PtRpn) {
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
val oper = expr.finalOperator()!!
val leftDt = oper.operand1Type
val rightDt = oper.operand2Type
val oper = expr.finalOperator()
val leftDt = oper.leftType
val rightDt = oper.rightType
// comparison against zero
if(oper.operator in ComparisonOperators) {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = (expr.finalSecondOperand() as PtExpression).asConstInteger()
val rightVal = (expr.finalRightOperand() as PtExpression).asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.finalFirstOperand() as PtExpression, leftDt, oper.operator)
return translateComparisonWithZero(expr.finalLeftOperand() as PtExpression, leftDt, oper.operator)
}
}
// string compare
if(leftDt==DataType.STR && rightDt==DataType.STR && oper.operator in ComparisonOperators) {
return translateCompareStrings(expr.finalFirstOperand() as PtExpression, oper.operator, expr.finalSecondOperand() as PtExpression)
return translateCompareStrings(expr.finalLeftOperand() as PtExpression, oper.operator, expr.finalRightOperand() as PtExpression)
}
// any other expression
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("operator ${oper.operator} left/right dt not identical: $leftDt $rightDt right=${expr.finalSecondOperand()}")
throw AssemblyError("operator ${oper.operator} left/right dt not identical: $leftDt $rightDt right=${expr.finalRightOperand()}")
var depth=0
expr.children.forEach {
if(it is PtRpnOperator) {
when(it.operand1Type) {
in ByteDatatypes -> translateBinaryOperatorBytes(it.operator, it.operand1Type)
in WordDatatypes -> translateBinaryOperatorWords(it.operator, it.operand1Type)
when(it.leftType) {
in ByteDatatypes -> translateBinaryOperatorBytes(it.operator, it.leftType)
in WordDatatypes -> translateBinaryOperatorWords(it.operator, it.leftType)
DataType.FLOAT -> translateBinaryOperatorFloats(it.operator)
else -> throw AssemblyError("non-numerical datatype ${it.operand1Type}")
else -> throw AssemblyError("non-numerical datatype ${it.leftType}")
}
depth--
} else {

View File

@ -1,5 +1,6 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
@ -8,9 +9,11 @@ import prog8.codegen.cpu6502.returnsWhatWhere
internal class AssignmentAsmGen(private val program: PtProgram,
private val 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)
fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
@ -142,8 +145,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
}
is PtRpn -> {
// TODO RPN: optimized pointer access
assignViaExprEval(value.address)
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(value.address as PtRpn, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.address)
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
@ -307,8 +313,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
is PtRpn -> {
// TODO RPN: optimized evaluation
fallbackToStackEval(assign)
if(!rpnAssignmentAsmGen.attemptAssignOptimizedExpr(value, 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,
// because the code here is the implementation of exactly that...)
fallbackToStackEval(assign)
}
}
else -> throw AssemblyError("weird assignment value type $value")
}
@ -909,8 +920,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
}
is PtRpn -> {
// TODO RPN: optimized pointer access
assignViaExprEval(value.address)
if(rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(value.address as PtRpn, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
assignViaExprEval(value.address)
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
@ -2834,8 +2849,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoPointerVar(addressExpr)
}
addressExpr is PtRpn -> {
// TODO RPN: optimize pointer access
storeViaExprEval()
if(!rpnAssignmentAsmGen.tryOptimizedPointerAccessWithA(addressExpr, true))
storeViaExprEval()
}
addressExpr is PtBinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))

View File

@ -0,0 +1,129 @@
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.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")
}
}
}
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

@ -93,7 +93,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
require(origAssign.operator.endsWith('='))
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN fallbackassign alt.")
value = PtRpn(origAssign.value.type, origAssign.value.position)
val left = origAssign.target.children.single() as PtExpression
val right = origAssign.value
value.add(left)
value.add(right)
value.add(PtRpnOperator(origAssign.operator.dropLast(1), origAssign.target.type, left.type, right.type, origAssign.position))
} else {
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
@ -266,14 +271,20 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val tr = if(itemsize==1) {
expressionEval.translateExpression(array.index)
} else {
val mult : PtExpression
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN loadindexreg alt.")
mult = PtRpn(DataType.UBYTE, array.position)
val left = array.index
val right = PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
mult.add(left)
mult.add(right)
mult.add(PtRpnOperator("*", DataType.UBYTE, left.type, right.type, array.position))
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
expressionEval.translateExpression(mult)
}
expressionEval.translateExpression(mult)
}
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)

View File

@ -317,7 +317,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
private fun translate(rpn: PtRpn): ExpressionCodeResult {
TODO("RPN expression $rpn")
TODO("RPN expression (intermediate codegen) $rpn")
}
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {

View File

@ -913,7 +913,7 @@ class IRCodeGen(
}
}
is PtRpn -> {
TODO("RPN ifelse $condition")
TODO("RPN ifelse (intermediate codegen) $condition")
}
else -> {
TODO("weird condition node: $condition")
@ -923,7 +923,7 @@ class IRCodeGen(
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
if(program.binaryExpressionsAreRPN) {
TODO ("RPN")
TODO ("RPN (intermediate codegen)")
} else {
val result = mutableListOf<IRCodeChunkBase>()
val condition = ifElse.condition as PtBinaryExpression

View File

@ -1,6 +1,8 @@
TODO
====
BRANCH: Fix the TODO RPN routines to be optimized assembly
For next minor release
^^^^^^^^^^^^^^^^^^^^^^
...