added first implementation of RPN 6502 codegen - all via stackeval still

This commit is contained in:
Irmen de Jong 2023-03-16 23:25:05 +01:00
parent 9241479da4
commit 8c0a93779b
11 changed files with 124 additions and 35 deletions

View File

@ -265,7 +265,7 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
return Pair(maxDepths, numPushes)
}
fun finalOperator(): String? = (children.last() as? PtRpnOperator)?.operator
fun finalOperator() = children.last() as? PtRpnOperator
fun finalFirstOperand() = children[children.size-3]
fun finalSecondOperand() = children[children.size-2]
}
@ -273,9 +273,6 @@ class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
class PtRpnOperator(val operator: String, val type: DataType, val operand1Type: DataType, val operand2Type: 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.
require(operand1Type equalsSize operand2Type) {
"operand type size(s) differ: $operand1Type $operand2Type oper: $operator"
}
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"
@ -342,6 +339,8 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
}
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
override fun toString() = "PtNumber:$type:$number"
}

View File

@ -100,8 +100,8 @@ class PtForLoop(position: Position) : PtNode(position) {
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtNode
get() = children[0]
val condition: PtExpression // either PtRpn or PtBinaryExpression
get() = children[0] as PtExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup

View File

@ -21,8 +21,10 @@ class AsmGen6502: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN)
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
val asmgen = AsmGen6502Internal(program, symbolTable, options, errors)
return asmgen.compileToAssembly()
@ -548,11 +550,32 @@ class AsmGen6502Internal (
private fun translate(stmt: PtIfElse) {
if(stmt.condition is PtRpn) {
TODO("RPN")
val condition = stmt.condition as PtRpn
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if (jump is PtJump) {
translateCompareAndJumpIfTrueRPN(condition, jump)
} else {
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalseRPN(condition, endLabel)
translate(stmt.ifScope)
out(endLabel)
}
} else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalseRPN(condition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
} else {
val condition = stmt.condition as PtBinaryExpression
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if (jump is PtJump) {
@ -578,7 +601,7 @@ class AsmGen6502Internal (
}
private fun requireComparisonExpression(condition: PtExpression) {
if (!(condition is PtRpn && condition.finalOperator() 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")
}
@ -984,7 +1007,7 @@ $repeatLabel lda $counterVar
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if (pointerOffsetExpr is PtRpn) {
TODO("RPN")
TODO("RPN determine pointer+index via reg.") // however, is this ever getting called from RPN code?
}
else if (pointerOffsetExpr is PtBinaryExpression) {
if (pointerOffsetExpr.operator != "+") return null
@ -1103,6 +1126,22 @@ $repeatLabel lda $counterVar
return node.definingSub()?.parameters?.singleOrNull { it.name===name }
}
private fun translateCompareAndJumpIfTrueRPN(expr: PtRpn, jump: PtJump) {
val label = when {
jump.generatedLabel!=null -> jump.generatedLabel!!
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
jump.address!=null -> jump.address!!.toHex()
else -> throw AssemblyError("weird jump")
}
assignExpressionToRegister(expr, RegisterOrPair.A)
out(" bne $label")
}
private fun translateCompareAndJumpIfFalseRPN(expr: PtRpn, jumpIfFalseLabel: String) {
assignExpressionToRegister(expr, RegisterOrPair.A)
out(" beq $jumpIfFalseLabel")
}
private fun translateCompareAndJumpIfTrue(expr: PtBinaryExpression, jump: PtJump) {
if(expr.operator !in ComparisonOperators)
throw AssemblyError("must be comparison expression")
@ -2850,7 +2889,8 @@ $repeatLabel lda $counterVar
}
}
is PtRpn -> {
TODO("translate RPN $expr")
// TODO RPN: optimized memread address
assignViaExprEval()
}
else -> assignViaExprEval()
}

View File

@ -662,7 +662,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
is PtRpn -> {
TODO("pokeW for RPN $addrExpr")
// TODO RPN: optimized addr+index
// for now: fall through
}
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
@ -724,7 +725,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
is PtRpn -> {
TODO("peekW for RPN $addrExpr")
// TODO RPN: optimized pointer+index
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {

View File

@ -34,7 +34,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
is PtIdentifier -> translateExpression(expression)
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is PtContainmentCheck -> translateContainmentCheck(expression)
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
@ -42,6 +42,11 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
private fun translateContainmentCheck(check: PtContainmentCheck) {
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
@ -241,7 +246,46 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
private fun translateExpression(expr: PtRpn) {
TODO("translate RPN $expr")
// 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
// comparison against zero
if(oper.operator in ComparisonOperators) {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = (expr.finalSecondOperand() as PtExpression).asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.finalFirstOperand() 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)
}
// 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()}")
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)
DataType.FLOAT -> translateBinaryOperatorFloats(it.operator)
else -> throw AssemblyError("non-numerical datatype ${it.operand1Type}")
}
depth--
} else {
translateExpressionInternal(it as PtExpression)
depth++
}
}
require(depth==1) { "unbalanced RPN: $depth ${expr.position}" }
}
private fun translateExpression(expr: PtBinaryExpression) {

View File

@ -142,7 +142,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
}
is PtRpn -> {
TODO("translate RPN ${value.address}")
// TODO RPN: optimized pointer access
assignViaExprEval(value.address)
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
@ -305,8 +306,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
fallbackToStackEval(assign)
}
}
is PtRpn ->{
TODO("assign RPN expression $value depth=${value.maxDepth()}")
is PtRpn -> {
// TODO RPN: optimized evaluation
fallbackToStackEval(assign)
}
else -> throw AssemblyError("weird assignment value type $value")
}
@ -907,7 +909,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
}
is PtRpn -> {
TODO("translate RPN ${value.address}")
// TODO RPN: optimized pointer access
assignViaExprEval(value.address)
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
@ -2831,7 +2834,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoPointerVar(addressExpr)
}
addressExpr is PtRpn -> {
TODO("translate RPN $addressExpr")
// TODO RPN: optimize pointer access
storeViaExprEval()
}
addressExpr is PtBinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))

View File

@ -2,10 +2,7 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.code.core.*
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
@ -17,8 +14,10 @@ class ExperiCodeGen: ICodeGeneratorBackend {
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN)
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:

View File

@ -93,7 +93,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
require(origAssign.operator.endsWith('='))
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN")
TODO("RPN fallbackassign alt.")
} else {
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
@ -267,7 +267,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
expressionEval.translateExpression(array.index)
} else {
if(codeGen.program.binaryExpressionsAreRPN) {
TODO("RPN")
TODO("RPN loadindexreg alt.")
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index

View File

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

View File

@ -913,7 +913,7 @@ class IRCodeGen(
}
}
is PtRpn -> {
TODO("RPN")
TODO("RPN ifelse $condition")
}
else -> {
TODO("weird condition node: $condition")

View File

@ -2,10 +2,7 @@ package prog8.codegen.vm
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.code.core.*
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
@ -18,8 +15,11 @@ class VmCodeGen: ICodeGeneratorBackend {
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN)
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()