diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 33e6eea95..a2b6a6c69 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -15,7 +15,12 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL * This gives the fastest lookup possible (no need to traverse tree nodes) */ - val flat: Map by lazy { + private var cachedFlat: Map? = null + + val flat: Map get() { + if(cachedFlat!=null) + return cachedFlat!! + val result = mutableMapOf() fun collect(node: StNode) { for(child in node.children) { @@ -24,9 +29,14 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL } } collect(this) - result + cachedFlat = result + return result } +// fun resetCachedFlat() { +// cachedFlat = null +// } + val allVariables: Collection by lazy { val vars = mutableListOf() fun collect(node: StNode) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 7c795736d..3d9c40496 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -472,8 +472,8 @@ class AsmGen6502Internal ( internal fun restoreXafterCall(functionCall: PtFunctionCall) = functioncallAsmGen.restoreXafterCall(functionCall) - internal fun translateNormalAssignment(assign: AsmAssignment) = - assignmentAsmGen.translateNormalAssignment(assign) + internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) = + assignmentAsmGen.translateNormalAssignment(assign, scope) internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean=false) = assignmentAsmGen.assignExpressionToRegister(expr, register, signed) @@ -481,8 +481,8 @@ class AsmGen6502Internal ( internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) = assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope) - internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, pos: Position, signed: Boolean=false) = - assignmentAsmGen.assignVariableToRegister(asmVarName, register, signed, pos) + internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, scope: IPtSubroutine?, pos: Position, signed: Boolean=false) = + assignmentAsmGen.assignVariableToRegister(asmVarName, register, signed, scope, pos) internal fun assignRegister(reg: RegisterOrPair, target: AsmAssignTarget) { when(reg) { @@ -512,7 +512,7 @@ class AsmGen6502Internal ( AsmAssignment( AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY), target, program.memsizer, value.position - ) + ), value.definingISub() ) } DataType.FLOAT -> { @@ -626,11 +626,11 @@ class AsmGen6502Internal ( val name = asmVariableName(stmt.count as PtIdentifier) when(vardecl.type) { DataType.UBYTE, DataType.BYTE -> { - assignVariableToRegister(name, RegisterOrPair.Y, stmt.count.position) + assignVariableToRegister(name, RegisterOrPair.Y, stmt.definingISub(), stmt.count.position) repeatCountInY(stmt, endLabel) } DataType.UWORD, DataType.WORD -> { - assignVariableToRegister(name, RegisterOrPair.AY, stmt.count.position) + assignVariableToRegister(name, RegisterOrPair.AY, stmt.definingISub(), stmt.count.position) repeatWordCountInAY(endLabel, stmt) } else -> throw AssemblyError("invalid loop variable datatype $vardecl") @@ -1062,7 +1062,9 @@ $repeatLabel lda $counterVar // 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)) + val num = PtNumber(DataType.UBYTE, constIdx.number, constIdx.position) + num.parent = right.parent + return Pair(left, num) } // could be that the index was typecasted into uword, check that val rightTc = right as? PtTypeCast @@ -1160,26 +1162,61 @@ $repeatLabel lda $counterVar } internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? { - val node = asmgen.symbolTable.lookup(name)!!.astNode + val stScope = asmgen.symbolTable.lookup(name) + require(stScope!=null) { + "invalid name lookup $name" + } + val node = stScope.astNode if(node is PtSubroutineParameter) return node return node.definingSub()?.parameters?.singleOrNull { it.name===name } } private fun translateCompareAndJumpIfTrueRPN(expr: PtRpn, jump: PtJump) { + val (left, oper, right) = expr.finalOperation() + if(expr.children.size>3) { + TODO("RPN comparison too complex ${expr.position}") + } + + require(left is PtExpression && right is PtExpression) + + if(oper.operator !in ComparisonOperators) + throw AssemblyError("must be comparison expression") + + // invert the comparison, so we can reuse the JumpIfFalse code generation routines + val invertedComparisonOperator = invertedComparisonOperator(oper.operator) + ?: throw AssemblyError("can't invert comparison $expr") + + val rightConstVal = right as? PtNumber + 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") + if (rightConstVal!=null && rightConstVal.number == 0.0) + testZeroAndJump(left, invertedComparisonOperator, label) + else { + val leftConstVal = left as? PtNumber + testNonzeroComparisonAndJump(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal) + } } private fun translateCompareAndJumpIfFalseRPN(expr: PtRpn, jumpIfFalseLabel: String) { - assignExpressionToRegister(expr, RegisterOrPair.A) - out(" beq $jumpIfFalseLabel") + val (left, oper, right) = expr.finalOperation() + if(expr.children.size>3) { + TODO("RPN comparison too complex ${expr.position}") + } + + require(left is PtExpression && right is PtExpression) + val leftConstVal = left as? PtNumber + val rightConstVal = right as? PtNumber + + if (rightConstVal!=null && rightConstVal.number == 0.0) + testZeroAndJump(left, oper.operator, jumpIfFalseLabel) + else + testNonzeroComparisonAndJump(left, oper.operator, right, jumpIfFalseLabel, leftConstVal, rightConstVal) } private fun translateCompareAndJumpIfTrue(expr: PtBinaryExpression, jump: PtJump) { @@ -1374,7 +1411,7 @@ $repeatLabel lda $counterVar } } - private fun testNonzeroComparisonAndJump( + internal fun testNonzeroComparisonAndJump( left: PtExpression, operator: String, right: PtExpression, diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 772e4cbbe..6c0b028b2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -250,7 +250,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, else AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen) val assign = AsmAssignment(src, target, program.memsizer, fcall.position) - asmgen.translateNormalAssignment(assign) + asmgen.translateNormalAssignment(assign, fcall.definingISub()) } private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { @@ -998,7 +998,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname) val assign = AsmAssignment(src, tgt, program.memsizer, value.position) - asmgen.translateNormalAssignment(assign) + asmgen.translateNormalAssignment(assign, scope) } conv.reg != null -> { val src = when (conv.dt) { @@ -1016,7 +1016,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen) val assign = AsmAssignment(src, tgt, program.memsizer, value.position) - asmgen.translateNormalAssignment(assign) + asmgen.translateNormalAssignment(assign, scope) } else -> throw AssemblyError("callconv") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index 5271e7d44..b2afb7134 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -478,15 +478,15 @@ internal class ExpressionsAsmGen(private val program: PtProgram, asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x") return true } - var left = amount - while (left >= 7) { + var amountLeft = amount + while (amountLeft >= 7) { asmgen.out(" jsr math.shift_right_uw_7") - left -= 7 + amountLeft -= 7 } - if (left in 0..2) - repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") } + if (amountLeft in 0..2) + repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") } else - asmgen.out(" jsr math.shift_right_uw_$left") + asmgen.out(" jsr math.shift_right_uw_$amountLeft") } DataType.WORD -> { if(amount>=16) { @@ -503,15 +503,15 @@ internal class ExpressionsAsmGen(private val program: PtProgram, +""") return true } - var left = amount - while (left >= 7) { + var amountLeft = amount + while (amountLeft >= 7) { asmgen.out(" jsr math.shift_right_w_7") - left -= 7 + amountLeft -= 7 } - if (left in 0..2) - repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") } + if (amountLeft in 0..2) + repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") } else - asmgen.out(" jsr math.shift_right_w_$left") + asmgen.out(" jsr math.shift_right_w_$amountLeft") } else -> throw AssemblyError("weird type") } @@ -531,15 +531,15 @@ internal class ExpressionsAsmGen(private val program: PtProgram, asmgen.out(" sta P8ESTACK_LO+1,x") } } else { - var left = amount - while (left >= 7) { + var amountLeft = amount + while (amountLeft >= 7) { asmgen.out(" jsr math.shift_left_w_7") - left -= 7 + amountLeft -= 7 } - if (left in 0..2) - repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") } + if (amountLeft in 0..2) + repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") } else - asmgen.out(" jsr math.shift_left_w_$left") + asmgen.out(" jsr math.shift_left_w_$amountLeft") } return true } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index e60638d78..76c88720e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -200,7 +200,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as // we need to sign extend the source, do this via temporary word variable asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub) asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type) - asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, Position.DUMMY) + asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY) } else { val target: AsmAssignTarget = if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) @@ -221,7 +221,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as } else { AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) } - asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY)) + asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), sub) } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 32dd83182..e5fa99193 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1,35 +1,36 @@ package prog8.codegen.cpu6502.assignment +import prog8.code.StStaticVariable import prog8.code.SymbolTable import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.VariableAllocator import prog8.codegen.cpu6502.returnsWhatWhere +import java.util.* internal class AssignmentAsmGen(private val program: PtProgram, - symbolTable: SymbolTable, + 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, asmgen) fun translate(assignment: PtAssignment) { val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen) val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) val assign = AsmAssignment(source, target, program.memsizer, assignment.position) - translateNormalAssignment(assign) + translateNormalAssignment(assign, assignment.definingISub()) } fun translate(augmentedAssign: PtAugmentedAssign) { val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen) val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target) val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, augmentedAssign.position) - augmentableAsmGen.translate(assign) + augmentableAsmGen.translate(assign, augmentedAssign.definingISub()) } - fun translateNormalAssignment(assign: AsmAssignment) { + fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) { when(assign.source.kind) { SourceStorageKind.LITERALNUMBER -> { // simple case: assign a constant number @@ -164,7 +165,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } SourceStorageKind.EXPRESSION -> { - assignExpression(assign) + assignExpression(assign, scope) } SourceStorageKind.REGISTER -> { asmgen.assignRegister(assign.source.register!!, assign.target) @@ -176,7 +177,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - private fun assignExpression(assign: AsmAssignment) { + private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) { when(val value = assign.source.expression!!) { is PtAddressOf -> { val sourceName = asmgen.asmSymbolName(value.identifier) @@ -289,17 +290,17 @@ internal class AssignmentAsmGen(private val program: PtProgram, AsmAssignment( AsmAssignSource.fromAstSource(value.value, program, asmgen), assign.target, program.memsizer, assign.position - ) + ), scope ) when (value.operator) { "+" -> {} - "-" -> inplaceNegate(assign, true) - "~" -> inplaceInvert(assign) + "-" -> inplaceNegate(assign, true, scope) + "~" -> inplaceInvert(assign, scope) "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") else -> throw AssemblyError("invalid prefix operator") } } else { - assignPrefixedExpressionToArrayElt(assign) + assignPrefixedExpressionToArrayElt(assign, scope) } } is PtContainmentCheck -> { @@ -316,7 +317,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } is PtRpn -> { - if(!rpnAssignmentAsmGen.attemptAssignOptimizedExpr(assign)) { + if(!attemptAssignOptimizedExprRPN(assign, scope!!)) { // 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, @@ -328,7 +329,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { + private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) { require(assign.source.expression is PtPrefix) if(assign.source.datatype==DataType.FLOAT) { // floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array. @@ -340,7 +341,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, val assignToTempvar = AsmAssignment(assign.source, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position, variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position) - asmgen.translateNormalAssignment(assignToTempvar) + asmgen.translateNormalAssignment(assignToTempvar, scope) when(assign.target.datatype) { in ByteDatatypes -> assignVariableByte(assign.target, tempvar) in WordDatatypes -> assignVariableWord(assign.target, tempvar) @@ -361,6 +362,221 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } + private fun attemptAssignOptimizedExprRPN(assign: AsmAssignment, scope: IPtSubroutine): Boolean { + val value = assign.source.expression as PtRpn + val (left, oper, right) = value.finalOperation() + if(oper.operator in ComparisonOperators) { + assignRPNComparison(assign, value) + return true + } + + fun simpleLogicalBytesExpr() { + // both left and right expression operands are simple. + require(left is PtExpression && right is PtExpression) + if (right is PtNumber || right is PtIdentifier) + assignLogicalWithSimpleRightOperandByte(assign.target, left, oper.operator, right) + else if (left is PtNumber || left is PtIdentifier) + assignLogicalWithSimpleRightOperandByte(assign.target, right, oper.operator, left) + else { + assignExpressionToRegister(left, RegisterOrPair.A, false) + asmgen.saveRegisterStack(CpuRegister.A, false) + assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, scope) + asmgen.restoreRegisterStack(CpuRegister.A, false) + when (oper.operator) { + "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") + "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") + "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") + else -> throw AssemblyError("invalid operator") + } + assignRegisterByte(assign.target, CpuRegister.A) + } + } + + fun simpleLogicalWordsExpr() { + // both left and right expression operands are simple. + require(left is PtExpression && right is PtExpression) + if (right is PtNumber || right is PtIdentifier) + assignLogicalWithSimpleRightOperandWord(assign.target, left, oper.operator, right) + else if (left is PtNumber || left is PtIdentifier) + assignLogicalWithSimpleRightOperandWord(assign.target, right, oper.operator, left) + else { + assignExpressionToRegister(left, RegisterOrPair.AY, false) + asmgen.saveRegisterStack(CpuRegister.A, false) + asmgen.saveRegisterStack(CpuRegister.Y, false) + assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.UWORD, scope) + when (oper.operator) { + "&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1") + "|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1") + "^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1") + else -> throw AssemblyError("invalid operator") + } + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + } + } + + if(value.children.size==3 && oper.operator in setOf("&", "|", "^", "and", "or", "xor")) { + if(left is PtExpression && right is PtExpression) { + if (left.type in ByteDatatypes && right.type in ByteDatatypes) { + if (right.isSimple()) { + simpleLogicalBytesExpr() + return true + } + } + if (left.type in WordDatatypes && right.type in WordDatatypes) { + if (right.isSimple()) { + simpleLogicalWordsExpr() + return true + } + } + } + } + + // TODO RPN add +,-,<<,>> and perhaps even == and != special behaviors of BinExpr + + val asmExtra = asmgen.subroutineExtra(scope) + val evalVars = mutableMapOf ( + DataType.UBYTE to Stack(), + DataType.UWORD to Stack(), + DataType.FLOAT to Stack() + ) + + fun getVarDt(dt: DataType) = + when(dt) { + in ByteDatatypes -> DataType.UBYTE + in WordDatatypes, in PassByReferenceDatatypes -> DataType.UWORD + else -> dt + } + + fun evalVarName(dt: DataType, depth: Int): String { + val name: String + val varDt: DataType + when(dt) { + in ByteDatatypes -> { + name = "p8_rpn_eval_byte_$depth" + varDt = DataType.UBYTE + } + in WordDatatypes, in PassByReferenceDatatypes -> { + name = "p8_rpn_eval_word_$depth" + varDt = DataType.UWORD + } + DataType.FLOAT -> { + name = "p8_rpn_eval_float_$depth" + varDt = DataType.FLOAT + } + else -> throw AssemblyError("weird dt") + } + evalVars.getValue(varDt).push(name) + if(!asmExtra.extraVars.any { it.second==name }) { + val stScope = symbolTable.lookup((scope as PtNamedNode).scopedName)!! + val dummyNode = PtVariable(name, varDt, ZeropageWish.DONTCARE, null, null, Position.DUMMY) + dummyNode.parent = scope + stScope.add(StStaticVariable(name, varDt, null, null, null, null, ZeropageWish.DONTCARE, dummyNode)) + asmExtra.extraVars.add(Triple(dt, name, null)) + } + return name + } + + + var depth=0 + value.children.forEach { + when (it) { + is PtRpnOperator -> { + val varDt = getVarDt(it.type) + val rightvar = evalVars.getValue(varDt).pop() + val leftvar = evalVars.getValue(varDt).pop() + depth-=2 + val resultVarname = evalVarName(it.type, depth) + depth++ + require(resultVarname==leftvar) + // TODO no longer needed? symbolTable.resetCachedFlat() + if(it.operator in ComparisonOperators) { + val scopeName = (scope as PtNamedNode).scopedName + val comparison = PtRpn(DataType.UBYTE, assign.position) + comparison.addRpnNode(PtIdentifier("$scopeName.$resultVarname", it.type, value.position)) + comparison.addRpnNode(PtIdentifier("$scopeName.$rightvar", it.type, value.position)) + comparison.addRpnNode(PtRpnOperator(it.operator, it.type, it.leftType, it.rightType, it.position)) + comparison.parent = scope + val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = comparison) + val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, scope, assign.position, variableAsmName = resultVarname) + val normalAssign = AsmAssignment(src, target, program.memsizer, assign.position) + assignRPNComparison(normalAssign, comparison) + } else { + val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, variableAsmName = rightvar) + val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, scope, assign.position, variableAsmName = resultVarname) + val augAssign = AsmAugmentedAssignment(src, it.operator+"=", target, program.memsizer, assign.position) + augmentableAsmGen.translate(augAssign, scope) + } + } + is PtExpression -> { + val varname = evalVarName(it.type, depth) + assignExpressionToVariable(it, varname, it.type, scope) + depth++ + } + else -> throw AssemblyError("weird rpn node") + } + } + require(depth==1) { "unbalanced RPN: $depth ${value.position}" } + val resultVariable = evalVars.getValue(getVarDt(value.type)).pop() + when(assign.target.datatype) { + in ByteDatatypes -> assignVariableByte(assign.target, resultVariable) + in WordDatatypes -> assignVariableWord(assign.target, resultVariable) + DataType.FLOAT -> assignVariableFloat(assign.target, resultVariable) + else -> throw AssemblyError("weird dt") + } + + require(evalVars.all { it.value.isEmpty() }) { "invalid rpn evaluation" } + + return true + } + + private fun assignRPNComparison(assign: AsmAssignment, comparison: PtRpn) { + val (left, oper, right) = comparison.finalOperation() + val constRight = (right as PtExpression).asConstInteger() + if(constRight == 0) { + if(oper.operator == "==" || oper.operator == "!=") { + when(assign.target.datatype) { + in ByteDatatypes -> { + if(attemptAssignToByteCompareZeroRPN(comparison, assign)) + return + } + in WordDatatypes -> { + assignConstantWord(assign.target, 0) + if(attemptAssignToByteCompareZeroRPN(comparison, assign)) + return + } + else -> { + // do nothing, this is handled by a type cast. + } + } + } + } + + if(comparison.children.size>3) { + TODO("RPN comparison too complex ${comparison.position}") + } + + require(left is PtExpression) + val leftNum = left as? PtNumber + val rightNum = right as? PtNumber + val jumpIfFalseLabel = asmgen.makeLabel("cmp") + + when(assign.target.datatype) { + in ByteDatatypes -> assignConstantByte(assign.target, 0) + in WordDatatypes -> assignConstantWord(assign.target, 0) + DataType.FLOAT -> assignConstantFloat(assign.target, 0.0) + else -> throw AssemblyError("invalid dt") + } + asmgen.testNonzeroComparisonAndJump(left, oper.operator, right, jumpIfFalseLabel, leftNum, rightNum) + when(assign.target.datatype) { + in ByteDatatypes -> assignConstantByte(assign.target, 1) + in WordDatatypes -> assignConstantWord(assign.target, 1) + DataType.FLOAT -> assignConstantFloat(assign.target, 1.0) + else -> throw AssemblyError("invalid dt") + } + asmgen.out(jumpIfFalseLabel) + asmgen.out("; cmp done") + } + private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { if(expr.operator in ComparisonOperators) { if(expr.right.asConstInteger() == 0) { @@ -743,6 +959,80 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignRegisterpairWord(target, RegisterOrPair.AY) } + private fun attemptAssignToByteCompareZeroRPN(expr: PtRpn, assign: AsmAssignment): Boolean { + val (left, oper, right) = expr.finalOperation() + if(expr.children.size!=3 || left !is PtExpression) + return false + when (oper.operator) { + "==" -> { + when(val dt = left.type) { + in ByteDatatypes -> { + assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) + asmgen.out(""" + beq + + lda #0 + beq ++ ++ lda #1 ++""") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + in WordDatatypes -> { + assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) + asmgen.out(""" + sty P8ZP_SCRATCH_B1 + ora P8ZP_SCRATCH_B1 + beq + + lda #0 + beq ++ ++ lda #1 ++""") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + DataType.FLOAT -> { + assignExpressionToRegister(left, RegisterOrPair.FAC1, true) + asmgen.out(" jsr floats.SIGN | and #1 | eor #1") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + else->{ + return false + } + } + } + "!=" -> { + when(val dt = left.type) { + in ByteDatatypes -> { + assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE) + asmgen.out(" beq + | lda #1") + asmgen.out("+") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + in WordDatatypes -> { + assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) + asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1") + asmgen.out(" beq + | lda #1") + asmgen.out("+") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + DataType.FLOAT -> { + assignExpressionToRegister(left, RegisterOrPair.FAC1, true) + asmgen.out(" jsr floats.SIGN") + assignRegisterByte(assign.target, CpuRegister.A) + return true + } + else->{ + return false + } + } + } + else -> return false + } + } + private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { when (expr.operator) { "==" -> { @@ -1030,7 +1320,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, // have to typecast the float number on the fly down to an integer assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes) assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype) - assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, target.position) + assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, origTypeCastExpression.definingISub(), target.position) return } else { if(!(valueDt isAssignableTo targetDt)) { @@ -1135,7 +1425,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, lsb.add(value) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val assign = AsmAssignment(src, target, program.memsizer, value.position) - translateNormalAssignment(assign) + translateNormalAssignment(assign, value.definingISub()) } private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) { @@ -2869,7 +3159,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, expr.position, null, asmgen) val assign = AsmAssignment(src, tgt, program.memsizer, expr.position) - translateNormalAssignment(assign) + translateNormalAssignment(assign, expr.definingISub()) } internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) { @@ -2879,18 +3169,18 @@ internal class AssignmentAsmGen(private val program: PtProgram, val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, expr.position, variableAsmName = asmVarName) val assign = AsmAssignment(src, tgt, program.memsizer, expr.position) - translateNormalAssignment(assign) + translateNormalAssignment(assign, expr.definingISub()) } } - internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, pos: Position) { + internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, pos: Position) { val tgt = AsmAssignTarget.fromRegisters(register, signed, pos, null, asmgen) val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName) val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY) - translateNormalAssignment(assign) + translateNormalAssignment(assign, scope) } - internal fun inplaceInvert(assign: AsmAssignment) { + internal fun inplaceInvert(assign: AsmAssignment, scope: IPtSubroutine?) { val target = assign.target when (assign.target.datatype) { DataType.UBYTE -> { @@ -2935,7 +3225,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert") - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign)) + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope) else -> throw AssemblyError("weird target") } } @@ -2960,7 +3250,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.STACK -> TODO("no asm gen for word stack invert") - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign)) + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope) else -> throw AssemblyError("weird target") } } @@ -2968,7 +3258,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) { + internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean, scope: IPtSubroutine?) { val target = assign.target val datatype = if(ignoreDatatype) { when(target.datatype) { @@ -3003,7 +3293,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that") TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate") - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign)) + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope) else -> throw AssemblyError("weird target") } } @@ -3063,7 +3353,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that") TargetStorageKind.STACK -> TODO("no asm gen for word stack negate") - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign)) + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope) else -> throw AssemblyError("weird target") } } @@ -3085,7 +3375,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, """) } TargetStorageKind.STACK -> TODO("no asm gen for float stack negate") - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign)) + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope) else -> throw AssemblyError("weird target for in-place float negation") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index 7e02abe22..8bbdf0857 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -11,16 +11,16 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator ) { - fun translate(assign: AsmAugmentedAssignment) { + fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) { when(assign.operator) { "-" -> { val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position) - assignmentAsmGen.inplaceNegate(a2, false) + assignmentAsmGen.inplaceNegate(a2, false, scope) } "~" -> { val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position) - assignmentAsmGen.inplaceInvert(a2) + assignmentAsmGen.inplaceInvert(a2, scope) } "+" -> { /* is a nop */ } else -> { @@ -44,6 +44,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "^=" -> inplaceModification(assign.target, "^", srcValue) "<<=" -> inplaceModification(assign.target, "<<", srcValue) ">>=" -> inplaceModification(assign.target, ">>", srcValue) + "%=" -> inplaceModification(assign.target, "%", srcValue) else -> throw AssemblyError("invalid augmented assign operator ${assign.operator}") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/RpnExpressionAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/RpnExpressionAsmGen.kt deleted file mode 100644 index a93c73706..000000000 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/RpnExpressionAsmGen.kt +++ /dev/null @@ -1,21 +0,0 @@ -package prog8.codegen.cpu6502.assignment - -import prog8.code.SymbolTable -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 asmgen: AsmGen6502Internal -) { - - fun attemptAssignOptimizedExpr(assign: AsmAssignment): Boolean { - val value = assign.source.expression as PtRpn - println("TODO: RPN: optimized assignment ${value.position} maxdepth=${value.maxDepth()}") // TODO RPN: optimized assignment - // NOTE: don't forgot to evaluate the rest of the RPN expr as well - return false - } - -} diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 3eadb698d..918eaeb0e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -898,6 +898,7 @@ class IRCodeGen( private fun translate(ifElse: PtIfElse): IRCodeChunks { val condition = ifElse.condition + val goto = ifElse.ifScope.children.firstOrNull() as? PtJump when (condition) { is PtBinaryExpression -> { if(condition.operator !in ComparisonOperators) @@ -905,7 +906,6 @@ class IRCodeGen( val signed = condition.left.type in SignedDatatypes val irDtLeft = irType(condition.left.type) - val goto = ifElse.ifScope.children.firstOrNull() as? PtJump return when { goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed) constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed) @@ -913,52 +913,92 @@ class IRCodeGen( } } is PtRpn -> { - TODO("RPN ifelse (intermediate codegen) $condition") + val (left, oper, right) = condition.finalOperation() + if(oper.operator !in ComparisonOperators) + throw AssemblyError("if condition should only be a binary comparison expression") + + val signed = oper.leftType in SignedDatatypes + val irDtLeft = irType(oper.leftType) + return when { + goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGotoRPN(ifElse, goto, irDtLeft, signed) + constValue(right as PtExpression) == 0.0 -> translateIfElseZeroComparisonRPN(ifElse, irDtLeft, signed) + else -> translateIfElseNonZeroComparisonRPN(ifElse, irDtLeft, signed) + } } else -> { - TODO("weird condition node: $condition") + throw AssemblyError("weird condition node: $condition") } } } + private fun translateIfFollowedByJustGotoRPN(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList { + TODO("RPN translateIfFollowedByJustGotoRPN") + } + + private fun translateIfElseZeroComparisonRPN(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks { + TODO("RPN translateIfElseZeroComparisonRPN") + } + + private fun translateIfElseNonZeroComparisonRPN(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks { + TODO("RPN translateIfElseNonZeroComparisonRPN") + } + private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList { - if(program.binaryExpressionsAreRPN) { - TODO ("RPN (intermediate codegen)") + require(!program.binaryExpressionsAreRPN) + val condition = ifElse.condition as PtBinaryExpression + val result = mutableListOf() + if (irDtLeft == IRDataType.FLOAT) { + val leftTr = expressionEval.translateExpression(condition.left) + addToResult(result, leftTr, -1, leftTr.resultFpReg) + val rightTr = expressionEval.translateExpression(condition.right) + addToResult(result, rightTr, -1, rightTr.resultFpReg) + result += IRCodeChunk(null, null).also { + val compResultReg = registers.nextFree() + it += IRInstruction( + Opcode.FCOMP, + IRDataType.FLOAT, + reg1 = compResultReg, + fpReg1 = leftTr.resultFpReg, + fpReg2 = rightTr.resultFpReg + ) + val gotoOpcode = when (condition.operator) { + "==" -> Opcode.BZ + "!=" -> Opcode.BNZ + "<" -> Opcode.BLEZS + ">" -> Opcode.BGEZS + "<=" -> Opcode.BLZS + ">=" -> Opcode.BGZS + else -> throw AssemblyError("weird operator") + } + it += if (goto.address != null) + IRInstruction( + gotoOpcode, + IRDataType.BYTE, + reg1 = compResultReg, + value = goto.address?.toInt() + ) + else if (goto.generatedLabel != null) + IRInstruction( + gotoOpcode, + IRDataType.BYTE, + reg1 = compResultReg, + labelSymbol = goto.generatedLabel + ) + else + IRInstruction( + gotoOpcode, + IRDataType.BYTE, + reg1 = compResultReg, + labelSymbol = goto.identifier!!.name + ) + } + return result } else { - val result = mutableListOf() - val condition = ifElse.condition as PtBinaryExpression - if(irDtLeft==IRDataType.FLOAT) { - val leftTr = expressionEval.translateExpression(condition.left) - addToResult(result, leftTr, -1, leftTr.resultFpReg) - val rightTr = expressionEval.translateExpression(condition.right) - addToResult(result, rightTr, -1, rightTr.resultFpReg) - result += IRCodeChunk(null,null).also { - val compResultReg = registers.nextFree() - it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg) - val gotoOpcode = when (condition.operator) { - "==" -> Opcode.BZ - "!=" -> Opcode.BNZ - "<" -> Opcode.BLEZS - ">" -> Opcode.BGEZS - "<=" -> Opcode.BLZS - ">=" -> Opcode.BGZS - else -> throw AssemblyError("weird operator") - } - it += if (goto.address != null) - IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, value = goto.address?.toInt()) - else if (goto.generatedLabel != null) - IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.generatedLabel) - else - IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name) - } - return result - } else { - val rightConst = condition.right.asConstInteger() - return if(rightConst==0) - ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto) - else { - ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto) - } + val rightConst = condition.right.asConstInteger() + return if (rightConst == 0) + ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto) + else { + ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto) } } } diff --git a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt index de5f0dae2..61aa7e994 100644 --- a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt +++ b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt @@ -21,6 +21,8 @@ class BinExprSplitter(private val program: Program, private val options: Compila if(options.compTarget.name == VMTarget.NAME) return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well + if(options.useRPN) // TODO RPN does this make a difference? + return noModifications if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions) return noModifications diff --git a/compiler/test/codegeneration/TestRPNCodeGen.kt b/compiler/test/codegeneration/TestRPNCodeGen.kt new file mode 100644 index 000000000..64b29321c --- /dev/null +++ b/compiler/test/codegeneration/TestRPNCodeGen.kt @@ -0,0 +1,45 @@ +package prog8tests.codegeneration + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import prog8.code.ast.PtAssignment +import prog8.code.ast.PtBuiltinFunctionCall +import prog8.code.ast.PtRpn +import prog8.code.core.DataType +import prog8.code.target.C64Target +import prog8tests.helpers.compileText + +class TestRPNCodeGen: FunSpec({ + test("rpn 6502") { + val text=""" +main { + sub start() { + uword pointer = 4000 + uword a = 11 + uword b = 22 + uword c = 33 + cx16.r0 = peekw(pointer+a+b*c+42) + pokew(pointer+a+b*c+42, 4242) + } +}""" + val result = compileText(C64Target(), false, text, writeAssembly = true, useRPN = true)!! + val ast = result.codegenAst!! + val statements = ast.entrypoint()!!.children + statements.size shouldBe 11 + val peekw = (statements[8] as PtAssignment).value as PtBuiltinFunctionCall + val pokew = (statements[9] as PtBuiltinFunctionCall) + val rpn1 = peekw.args.first() as PtRpn + val rpn2 = pokew.args.first() as PtRpn + rpn1.children.size shouldBe 7 + val depth1 = rpn1.maxDepth() + depth1.getValue(DataType.UBYTE) shouldBe 0 + depth1.getValue(DataType.UWORD) shouldBe 3 + depth1.getValue(DataType.FLOAT) shouldBe 0 + rpn2.children.size shouldBe 7 + val depth2 = rpn2.maxDepth() + depth2.getValue(DataType.UBYTE) shouldBe 0 + depth2.getValue(DataType.UWORD) shouldBe 3 + depth2.getValue(DataType.FLOAT) shouldBe 0 + } + +}) \ No newline at end of file diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index 4603528d6..a2362e8bb 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -17,7 +17,8 @@ internal fun compileFile( outputDir: Path = prog8tests.helpers.outputDir, errors: IErrorReporter? = null, writeAssembly: Boolean = true, - optFloatExpr: Boolean = true + optFloatExpr: Boolean = true, + useRPN: Boolean = false ) : CompilationResult? { val filepath = fileDir.resolve(fileName) assumeReadableFile(filepath) @@ -31,7 +32,7 @@ internal fun compileFile( asmListfile = false, experimentalCodegen = false, varsHigh = false, - useRPN = false, + useRPN = useRPN, platform.name, evalStackBaseAddress = null, symbolDefs = emptyMap(), @@ -52,11 +53,12 @@ internal fun compileText( sourceText: String, errors: IErrorReporter? = null, writeAssembly: Boolean = true, - optFloatExpr: Boolean = true + optFloatExpr: Boolean = true, + useRPN: Boolean = false ) : CompilationResult? { val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8") // we don't assumeNotExists(filePath) - should be ok to just overwrite it filePath.toFile().writeText(sourceText) return compileFile(platform, optimize, filePath.parent, filePath.name, - errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr) + errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, useRPN=useRPN) } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 8dcf1d569..260feb810 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,8 @@ TODO ==== -BRANCH: Write some unit tests for the RPN. -BRANCH: Fix the TODO RPN routines to be optimized assembly in RpnExpressionAsmGen.kt +BRANCH: Fix the TODO RPN routines to be optimized assembly in RpnExpressionAsmGen.kt +BRANCH: check BinExprSplitter disablement any effect for RPN? BRANCH: Implement RPN codegen for IR. For next minor release diff --git a/examples/test.p8 b/examples/test.p8 index 336aec8c3..54dbcf0c9 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,36 +1,17 @@ -%import textio -%import test_stack +%import math %zeropage basicsafe -%option no_sysinit -; $1e4 size +; Note: this program is compatible with C64 and CX16. main { - sub start() { - uword xx=4000 - ubyte a=11 - ubyte b=22 - ubyte c=33 + sub start() { + ubyte[255] BC + bool[255] DX -; cx16.r0 = peekw(xx+a+b+c) -; cx16.r1 = peekw(xx+a+b+c+42) -; pokew(xx+a+b+c, xx) -; pokew(xx+a+b+c+42, xx) - - if a and a & $40 == 0 - cx16.r0++ - -; if cx16.r0L in "derp" { -; xx++ -; } -; -; xx = xx+(3*func(xx)+xx*2*cx16.r0L) -; txt.print_uw(xx) - } - -; sub func(uword value) -> uword { -; value ++ -; return value -; } + BC[2] = math.rnd() & 15 + BC[3] = math.rnd() & 15 + BC[4] = math.rnd() & 15 + ;DX[2] = math.rnd() & 1 + } }