From 8c0a93779b7a1fb56aa2fddd5fbc706fd9bcbcb2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 16 Mar 2023 23:25:05 +0100 Subject: [PATCH] added first implementation of RPN 6502 codegen - all via stackeval still --- codeCore/src/prog8/code/ast/AstExpressions.kt | 7 ++- codeCore/src/prog8/code/ast/AstStatements.kt | 4 +- .../src/prog8/codegen/cpu6502/AsmGen.kt | 52 ++++++++++++++++--- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 7 ++- .../codegen/cpu6502/ExpressionsAsmGen.kt | 48 ++++++++++++++++- .../cpu6502/assignment/AssignmentAsmGen.kt | 14 +++-- .../codegen/experimental/ExperiCodeGen.kt | 9 ++-- .../codegen/intermediate/AssignmentGen.kt | 4 +- .../codegen/intermediate/ExpressionGen.kt | 2 +- .../prog8/codegen/intermediate/IRCodeGen.kt | 2 +- .../src/prog8/codegen/vm/VmCodeGen.kt | 10 ++-- 11 files changed, 124 insertions(+), 35 deletions(-) diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index fd5fce2d4..8ae53a276 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -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" } diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 46649d4ae..07860d42b 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -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 diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 34cf5875b..38885e209 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -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 ' + 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 ' - 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? { 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() } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 33285c5ce..feeb045fc 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -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) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index 97c0a02a8..b22e5e154 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -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) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 2bcd5d89d..cc85cb11f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -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)) diff --git a/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt index 00738b91c..18d5fb90a 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt @@ -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: diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index c4de629e1..29d45b142 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -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 diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index f4e135b7a..e4c20446c 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -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 { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 832d21da3..1e61785b9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -913,7 +913,7 @@ class IRCodeGen( } } is PtRpn -> { - TODO("RPN") + TODO("RPN ifelse $condition") } else -> { TODO("weird condition node: $condition") diff --git a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt index e057e6655..9d96de92a 100644 --- a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt @@ -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()