mirror of
https://github.com/irmen/prog8.git
synced 2025-02-02 19:32:21 +00:00
got rid of rpn deadend code...
This commit is contained in:
parent
ca2af2ca63
commit
629ed74d09
@ -53,8 +53,7 @@ sealed class PtNamedNode(var name: String, position: Position): PtNode(position)
|
||||
class PtProgram(
|
||||
val name: String,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding,
|
||||
var binaryExpressionsAreRPN: Boolean = false
|
||||
val encoding: IStringEncoding
|
||||
) : PtNode(Position.DUMMY) {
|
||||
|
||||
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||
@ -65,53 +64,6 @@ class PtProgram(
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
|
||||
|
||||
// If the code generator wants, it can transform binary expression nodes into flat RPN nodes.
|
||||
// This will destroy the original binaryexpression nodes!
|
||||
fun transformBinExprToRPN() {
|
||||
if(binaryExpressionsAreRPN)
|
||||
return
|
||||
fun transformToRPN(originalExpr: PtBinaryExpression): PtRpn {
|
||||
fun makeRpn(expr: PtExpression): PtRpn {
|
||||
val rpn = PtRpn(expr.type, expr.position)
|
||||
if(expr is PtBinaryExpression)
|
||||
rpn.addRpnNode(transformToRPN(expr))
|
||||
else
|
||||
rpn.addRpnNode(expr)
|
||||
return rpn
|
||||
}
|
||||
|
||||
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, originalExpr.position))
|
||||
return rpn
|
||||
}
|
||||
|
||||
fun transformBinExprToRPN(node: PtNode, parent: PtNode) {
|
||||
if(node is PtBinaryExpression) {
|
||||
val rpn = transformToRPN(node)
|
||||
val idx = parent.children.indexOf(node)
|
||||
rpn.parent = parent
|
||||
parent.children[idx] = rpn
|
||||
}
|
||||
|
||||
node.children.forEach {child ->
|
||||
transformBinExprToRPN(child, node)
|
||||
}
|
||||
}
|
||||
|
||||
children.forEach { transformBinExprToRPN(it, this) }
|
||||
binaryExpressionsAreRPN = true
|
||||
|
||||
// extra check to see that all PtBinaryExpressions have been transformed
|
||||
fun binExprCheck(node: PtNode) {
|
||||
if(node is PtBinaryExpression)
|
||||
throw IllegalArgumentException("still got binexpr $node ${node.position}")
|
||||
node.children.forEach { binExprCheck(it) }
|
||||
}
|
||||
binExprCheck(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,7 +3,6 @@ package prog8.code.ast
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.round
|
||||
|
||||
|
||||
@ -28,7 +27,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
||||
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||
is PtRpn -> other is PtRpn && this.isSame(other)
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||
@ -64,7 +62,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
is PtArray -> true
|
||||
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||
is PtBinaryExpression -> false
|
||||
is PtRpn -> false
|
||||
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||
is PtContainmentCheck -> false
|
||||
is PtFunctionCall -> false
|
||||
@ -161,146 +158,6 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
|
||||
}
|
||||
|
||||
|
||||
class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
|
||||
// contains only PtExpression (not PtRpn!) and PtRpnOperator nodes
|
||||
// not created directly by the compiler for now, if code generators prefer this over PtBinaryExpression,
|
||||
// they have to transform the ast themselves first using the utility routine on PtProgram for it.
|
||||
|
||||
fun addRpnNode(node: PtNode) {
|
||||
require(node is PtRpnOperator || node is PtExpression)
|
||||
if(node is PtRpn) {
|
||||
node.children.forEach {
|
||||
children.add(it)
|
||||
it.parent = this
|
||||
}
|
||||
}
|
||||
else {
|
||||
require(node !is PtBinaryExpression)
|
||||
children.add(node)
|
||||
node.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
fun print() {
|
||||
children.forEach {
|
||||
when(it) {
|
||||
is PtRpnOperator -> println(it.operator)
|
||||
is PtExpression -> println("expr $it ${it.position}")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isSame(other: PtRpn): Boolean {
|
||||
if(other.children.size==children.size) {
|
||||
return other.children.zip(this.children).all { (first, second) ->
|
||||
when (first) {
|
||||
is PtRpnOperator -> second is PtRpnOperator && first.operator==second.operator
|
||||
is PtExpression -> second is PtExpression && first isSameAs second
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun maxDepth(): Map<DataType, Int> {
|
||||
val depths = mutableMapOf(
|
||||
DataType.UBYTE to 0,
|
||||
DataType.UWORD to 0,
|
||||
DataType.FLOAT to 0
|
||||
)
|
||||
val maxDepths = mutableMapOf(
|
||||
DataType.UBYTE to 0,
|
||||
DataType.UWORD to 0,
|
||||
DataType.FLOAT to 0
|
||||
)
|
||||
var numPushes = 0
|
||||
var numPops = 0
|
||||
|
||||
fun push(type: DataType) {
|
||||
when (type) {
|
||||
in ByteDatatypes -> {
|
||||
val depth = depths.getValue(DataType.UBYTE) + 1
|
||||
depths[DataType.UBYTE] = depth
|
||||
maxDepths[DataType.UBYTE] = max(maxDepths.getValue(DataType.UBYTE), depth)
|
||||
}
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> {
|
||||
val depth = depths.getValue(DataType.UWORD) + 1
|
||||
depths[DataType.UWORD] = depth
|
||||
maxDepths[DataType.UWORD] = max(maxDepths.getValue(DataType.UWORD), depth)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
val depth = depths.getValue(DataType.FLOAT) + 1
|
||||
depths[DataType.FLOAT] = depth
|
||||
maxDepths[DataType.FLOAT] = max(maxDepths.getValue(DataType.FLOAT), depth)
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid dt")
|
||||
}
|
||||
numPushes++
|
||||
}
|
||||
|
||||
fun pop(type: DataType) {
|
||||
when (type) {
|
||||
in ByteDatatypes -> depths[DataType.UBYTE]=depths.getValue(DataType.UBYTE) - 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> depths[DataType.UWORD]=depths.getValue(DataType.UWORD) - 1
|
||||
DataType.FLOAT -> depths[DataType.FLOAT]=depths.getValue(DataType.FLOAT) - 1
|
||||
else -> throw IllegalArgumentException("invalid dt")
|
||||
}
|
||||
numPops++
|
||||
}
|
||||
|
||||
children.withIndex().forEach { (index, node) ->
|
||||
if (node is PtRpnOperator) {
|
||||
pop(node.leftType)
|
||||
pop(node.rightType)
|
||||
push(node.type)
|
||||
}
|
||||
else {
|
||||
push((node as PtExpression).type)
|
||||
}
|
||||
}
|
||||
require(numPushes==numPops+1 && numPushes==children.size) {
|
||||
"RPN not balanced, pushes=$numPushes pops=$numPops childs=${children.size}"
|
||||
}
|
||||
return maxDepths
|
||||
}
|
||||
|
||||
fun finalOperator() = children.last() as PtRpnOperator
|
||||
fun finalLeftOperand() = children[children.size-3]
|
||||
fun finalRightOperand() = children[children.size-2]
|
||||
fun finalOperation() = Triple(finalLeftOperand(), finalOperator(), finalRightOperand())
|
||||
fun truncateLastOperator(): PtExpression {
|
||||
// NOTE: this is a destructive operation!
|
||||
children.removeLast()
|
||||
children.removeLast()
|
||||
if(children.last() !is PtRpnOperator) {
|
||||
if(children.size==1 && children[0] is PtExpression)
|
||||
return children[0] as PtExpression
|
||||
else
|
||||
throw IllegalArgumentException("don't know what to do with children: $children")
|
||||
}
|
||||
val finalOper = finalOperator()
|
||||
if(finalOper.type==type) return this
|
||||
val typeAdjusted = PtRpn(finalOper.type, this.position)
|
||||
typeAdjusted.children.addAll(children)
|
||||
typeAdjusted.parent = parent
|
||||
return typeAdjusted
|
||||
}
|
||||
}
|
||||
|
||||
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 && operator != "<<" && operator != ">>") {
|
||||
require(type equalsSize leftType && type equalsSize rightType) {
|
||||
"operand type size(s) differ from operator result type $type: $leftType $rightType oper: $operator"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||
val element: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
|
@ -19,8 +19,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
is PtRpn -> "<rpnexpr> ${type(node.type)}"
|
||||
is PtRpnOperator -> "${node.operator} ${type(node.type)}"
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val str = if(node.void) "void " else ""
|
||||
str + node.name + "()"
|
||||
|
@ -20,7 +20,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var asmListfile: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHigh: Boolean = false,
|
||||
var useRPN: Boolean = false,
|
||||
var useNewExprCode: Boolean = false,
|
||||
var evalStackBaseAddress: UInt? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
@ -21,9 +21,9 @@ class AsmGen6502: ICodeGeneratorBackend {
|
||||
options: CompilationOptions,
|
||||
errors: IErrorReporter
|
||||
): IAssemblyProgram? {
|
||||
if(options.useRPN) {
|
||||
program.transformBinExprToRPN()
|
||||
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
|
||||
if(options.useNewExprCode) {
|
||||
// TODO("transform BinExprs?")
|
||||
// errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY)
|
||||
}
|
||||
|
||||
// printAst(program, true) { println(it) }
|
||||
@ -551,59 +551,33 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtIfElse) {
|
||||
if(stmt.condition is PtRpn) {
|
||||
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)
|
||||
}
|
||||
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) {
|
||||
translateCompareAndJumpIfTrue(condition, jump)
|
||||
} else {
|
||||
// both true and else parts
|
||||
val elseLabel = makeLabel("if_else")
|
||||
val endLabel = makeLabel("if_end")
|
||||
translateCompareAndJumpIfFalseRPN(condition, elseLabel)
|
||||
translateCompareAndJumpIfFalse(condition, endLabel)
|
||||
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) {
|
||||
translateCompareAndJumpIfTrue(condition, jump)
|
||||
} else {
|
||||
val endLabel = makeLabel("if_end")
|
||||
translateCompareAndJumpIfFalse(condition, endLabel)
|
||||
translate(stmt.ifScope)
|
||||
out(endLabel)
|
||||
}
|
||||
} else {
|
||||
// both true and else parts
|
||||
val elseLabel = makeLabel("if_else")
|
||||
val endLabel = makeLabel("if_end")
|
||||
translateCompareAndJumpIfFalse(condition, elseLabel)
|
||||
translate(stmt.ifScope)
|
||||
jmp(endLabel)
|
||||
out(elseLabel)
|
||||
translate(stmt.elseScope)
|
||||
out(endLabel)
|
||||
}
|
||||
// both true and else parts
|
||||
val elseLabel = makeLabel("if_else")
|
||||
val endLabel = makeLabel("if_end")
|
||||
translateCompareAndJumpIfFalse(condition, elseLabel)
|
||||
translate(stmt.ifScope)
|
||||
jmp(endLabel)
|
||||
out(elseLabel)
|
||||
translate(stmt.elseScope)
|
||||
out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireComparisonExpression(condition: PtExpression) {
|
||||
if (!(condition is PtRpn && condition.finalOperator().operator in ComparisonOperators) && !(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
|
||||
if (!(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
|
||||
throw AssemblyError("expected boolean comparison expression $condition")
|
||||
}
|
||||
|
||||
@ -766,7 +740,7 @@ $repeatLabel lda $counterVar
|
||||
}
|
||||
val isNested = parent is PtRepeatLoop
|
||||
|
||||
if(!isNested && !options.useRPN) {
|
||||
if(!isNested && !options.useNewExprCode) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("counter") }
|
||||
if(existingVar!=null) {
|
||||
@ -1012,45 +986,12 @@ $repeatLabel lda $counterVar
|
||||
val right: PtExpression
|
||||
val operator: String
|
||||
|
||||
when (pointerOffsetExpr) {
|
||||
is PtRpn -> {
|
||||
if(pointerOffsetExpr.children.size>3) {
|
||||
val rightmostOperator = pointerOffsetExpr.finalOperator()
|
||||
if(rightmostOperator.operator=="+") {
|
||||
val rightmostOperand = pointerOffsetExpr.finalRightOperand()
|
||||
if ((rightmostOperand is PtNumber && rightmostOperand.type in IntegerDatatypes && rightmostOperand.number.toInt() in 0..255)
|
||||
|| (rightmostOperand is PtExpression && rightmostOperand.type == DataType.UBYTE)
|
||||
|| (rightmostOperand is PtTypeCast && rightmostOperand.value.type == DataType.UBYTE)
|
||||
) {
|
||||
// split up the big expression in 2 parts so that we CAN use ZP,Y indexing after all
|
||||
val truncatedExpr = pointerOffsetExpr.truncateLastOperator()
|
||||
val tempvar = getTempVarName(DataType.UWORD)
|
||||
assignExpressionToVariable(truncatedExpr, tempvar, DataType.UWORD)
|
||||
val smallExpr = PtRpn(DataType.UWORD, truncatedExpr.position)
|
||||
smallExpr.addRpnNode(PtIdentifier(tempvar, DataType.UWORD, truncatedExpr.position))
|
||||
smallExpr.addRpnNode(rightmostOperand)
|
||||
smallExpr.addRpnNode(rightmostOperator)
|
||||
smallExpr.parent = truncatedExpr.parent
|
||||
val result = pointerViaIndexRegisterPossible(smallExpr)
|
||||
require(result != null)
|
||||
return result
|
||||
}
|
||||
}
|
||||
return null // expression is too complex
|
||||
}
|
||||
val (leftNode, oper, rightNode) = pointerOffsetExpr.finalOperation()
|
||||
operator=oper.operator
|
||||
if (leftNode !is PtExpression || rightNode !is PtExpression) return null
|
||||
left = leftNode
|
||||
right = rightNode
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
operator = pointerOffsetExpr.operator
|
||||
left = pointerOffsetExpr.left
|
||||
right = pointerOffsetExpr.right
|
||||
}
|
||||
else -> return null
|
||||
if (pointerOffsetExpr is PtBinaryExpression) {
|
||||
operator = pointerOffsetExpr.operator
|
||||
left = pointerOffsetExpr.left
|
||||
right = pointerOffsetExpr.right
|
||||
}
|
||||
else return null
|
||||
|
||||
if (operator != "+") return null
|
||||
val leftDt = left.type
|
||||
@ -1173,53 +1114,6 @@ $repeatLabel lda $counterVar
|
||||
return node.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
}
|
||||
|
||||
private fun translateCompareAndJumpIfTrueRPN(expr: PtRpn, jump: PtJump) {
|
||||
val (leftRpn, oper, right) = expr.finalOperation()
|
||||
if(oper.operator !in ComparisonOperators)
|
||||
throw AssemblyError("must be comparison expression")
|
||||
val left: PtExpression = if(expr.children.size>3 || leftRpn !is PtExpression)
|
||||
expr.truncateLastOperator()
|
||||
else
|
||||
leftRpn
|
||||
|
||||
// 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 label = when {
|
||||
jump.generatedLabel!=null -> jump.generatedLabel!!
|
||||
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
|
||||
jump.address!=null -> jump.address!!.toHex()
|
||||
else -> throw AssemblyError("weird jump")
|
||||
}
|
||||
val rightConstVal = right as? PtNumber
|
||||
if (rightConstVal!=null && rightConstVal.number == 0.0) {
|
||||
testZeroAndJump(left, invertedComparisonOperator, label)
|
||||
}
|
||||
else {
|
||||
require(right is PtExpression)
|
||||
val leftConstVal = left as? PtNumber
|
||||
testNonzeroComparisonAndJump(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateCompareAndJumpIfFalseRPN(expr: PtRpn, jumpIfFalseLabel: String) {
|
||||
val (leftRpn, oper, right) = expr.finalOperation()
|
||||
val left: PtExpression = if(expr.children.size>3 || leftRpn !is PtExpression)
|
||||
expr.truncateLastOperator()
|
||||
else
|
||||
leftRpn
|
||||
|
||||
require(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) {
|
||||
if(expr.operator !in ComparisonOperators)
|
||||
throw AssemblyError("must be comparison expression")
|
||||
@ -2967,15 +2861,6 @@ $repeatLabel lda $counterVar
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
is PtRpn -> {
|
||||
val addrExpr = expr.address as PtRpn
|
||||
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
else -> assignViaExprEval()
|
||||
}
|
||||
}
|
||||
@ -3205,7 +3090,6 @@ internal class SubroutineExtraAsmInfo {
|
||||
var usedRegsaveY = false
|
||||
var usedFloatEvalResultVar1 = false
|
||||
var usedFloatEvalResultVar2 = false
|
||||
var rpnDepth = 0 // 'depth' tracking of the RPN expression evaluator
|
||||
|
||||
val extraVars = mutableListOf<Triple<DataType, String, UInt?>>()
|
||||
}
|
@ -661,7 +661,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
}
|
||||
is PtRpn, is PtBinaryExpression -> {
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
@ -723,7 +723,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
} else fallback()
|
||||
}
|
||||
is PtRpn, is PtBinaryExpression -> {
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
|
@ -25,7 +25,6 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
when(expression) {
|
||||
is PtPrefix -> translateExpression(expression)
|
||||
is PtBinaryExpression -> translateExpression(expression)
|
||||
is PtRpn -> translateRpnExpression(expression)
|
||||
is PtArrayIndexer -> translateExpression(expression)
|
||||
is PtTypeCast -> translateExpression(expression)
|
||||
is PtAddressOf -> translateExpression(expression)
|
||||
@ -245,70 +244,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateRpnExpression(expr: PtRpn) {
|
||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||
val (left, oper, right) = expr.finalOperation()
|
||||
if(left is PtExpression && right is PtExpression && translateSomewhatOptimized(left, oper.operator, right))
|
||||
return
|
||||
|
||||
val leftDt = oper.leftType
|
||||
val rightDt = oper.rightType
|
||||
|
||||
// comparison with zero
|
||||
if(oper.operator in ComparisonOperators && leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||
val rightVal = (expr.finalRightOperand() as PtExpression).asConstInteger()
|
||||
if (rightVal == 0) {
|
||||
when (left) {
|
||||
is PtExpression -> {
|
||||
translateComparisonWithZero(left, leftDt, oper.operator)
|
||||
}
|
||||
is PtRpnOperator -> {
|
||||
val truncated = expr.truncateLastOperator()
|
||||
translateComparisonWithZero(truncated, leftDt, oper.operator)
|
||||
}
|
||||
else -> throw AssemblyError("weird rpn node")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// string compare
|
||||
if(left is PtExpression && leftDt==DataType.STR && rightDt==DataType.STR && oper.operator in ComparisonOperators)
|
||||
return translateCompareStrings(left, 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.finalRightOperand()}")
|
||||
|
||||
val asmExtra = asmgen.subroutineExtra(expr.definingISub()!!)
|
||||
val startDepth = asmExtra.rpnDepth
|
||||
expr.children.forEach {
|
||||
if(it is PtRpnOperator) {
|
||||
when(it.leftType) {
|
||||
in ByteDatatypes -> translateBinaryOperatorBytes(it.operator, it.leftType)
|
||||
in WordDatatypes -> translateBinaryOperatorWords(it.operator, it.leftType)
|
||||
DataType.FLOAT -> translateBinaryOperatorFloats(it.operator)
|
||||
DataType.STR -> {
|
||||
if(it.operator !in ComparisonOperators)
|
||||
throw AssemblyError("expected only comparison operators on string, not ${oper.operator}")
|
||||
asmgen.out(" jsr prog8_lib.strcmp_stack")
|
||||
compareStringsProcessResultInA(it.operator)
|
||||
}
|
||||
else -> throw AssemblyError("non-numerical datatype ${it.leftType}")
|
||||
}
|
||||
asmExtra.rpnDepth--
|
||||
} else {
|
||||
translateExpressionInternal(it as PtExpression)
|
||||
asmExtra.rpnDepth++
|
||||
}
|
||||
}
|
||||
require(asmExtra.rpnDepth-startDepth==1) { "unbalanced RPN: ${expr.position}" }
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtBinaryExpression) {
|
||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||
return
|
||||
|
||||
|
@ -146,14 +146,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
is PtIdentifier -> {
|
||||
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
|
||||
}
|
||||
is PtRpn -> {
|
||||
val addrExpr = value.address as PtRpn
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
} else {
|
||||
assignViaExprEval(value.address)
|
||||
}
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
@ -309,7 +301,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
||||
// All remaining binary expressions just evaluate via the stack for now.
|
||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||
@ -317,15 +308,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
}
|
||||
is PtRpn -> {
|
||||
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,
|
||||
// because the code here is the implementation of exactly that...)
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird assignment value type $value")
|
||||
}
|
||||
}
|
||||
@ -363,234 +345,6 @@ 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.type != value.type)
|
||||
throw AssemblyError("rpn node type error, expected ${value.type} got ${oper.type}")
|
||||
|
||||
if(value.children.size==3 && left is PtExpression && right is PtExpression) {
|
||||
|
||||
// TODO re-enable after compiler errors are fixed:
|
||||
// if(right.asConstInteger() == 0) {
|
||||
// if(oper.operator == "==" || oper.operator=="!=") {
|
||||
// when(assign.target.datatype) {
|
||||
// in ByteDatatypes -> if(attemptAssignToByteCompareZeroRPN(value, assign)) return true
|
||||
// else -> {
|
||||
// // do nothing, this is handled by a type cast.
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if(assign.target.datatype == oper.type && oper.leftType == oper.type) {
|
||||
println("TODO simple augmented assign operator into target: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
println(" left=$left right=$right")
|
||||
//augmentableAsmGen.translate()
|
||||
} else {
|
||||
println("TODO augmented assign operator into target but needs tempvar: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
println(" left=$left right=$right")
|
||||
}
|
||||
// if(oper.operator in ComparisonOperators) {
|
||||
// println("TODO simple comparison: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
// }
|
||||
// else if(oper.operator in setOf("&", "|", "^", "and", "or", "xor")) {
|
||||
// println("TODO simple logical: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
// }
|
||||
// else if(oper.operator=="+" || oper.operator=="-") {
|
||||
// println("TODO simple plus or minus: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
// }
|
||||
// else if(oper.operator=="<<" || oper.operator==">>") {
|
||||
// println("TODO simple bitshift: ${assign.position} ${oper.leftType} ${oper.operator} ${oper.rightType} -> ${oper.type}")
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO RPN optimizations for simple cases
|
||||
// if(oper.operator in ComparisonOperators) {
|
||||
// assignRPNComparison(assign, value)
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// if(right is PtExpression) {
|
||||
// if (simpleEqualityExprRPN(value, oper, right, assign.target))
|
||||
// return true
|
||||
// if (simpleLogicalExprRPN(value, oper, right, assign.target))
|
||||
// return true
|
||||
// if (simplePlusOrMinusExprRPN(value, oper, right, assign.target))
|
||||
// return true
|
||||
// if (simpleBitshiftExprRPN(value, oper, right, assign.target))
|
||||
// return true
|
||||
// }
|
||||
|
||||
val asmExtra = asmgen.subroutineExtra(scope)
|
||||
val evalVars = mutableMapOf (
|
||||
DataType.UBYTE to Stack<String>(),
|
||||
DataType.UWORD to Stack<String>(),
|
||||
DataType.FLOAT to Stack<String>()
|
||||
)
|
||||
|
||||
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(varDt, name, null))
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
val startDepth = asmExtra.rpnDepth
|
||||
value.children.forEach {
|
||||
when (it) {
|
||||
is PtRpnOperator -> {
|
||||
val rightvar = evalVars.getValue(getVarDt(it.rightType)).pop()
|
||||
val leftvar = evalVars.getValue(getVarDt(it.leftType)).pop()
|
||||
asmExtra.rpnDepth -= 2
|
||||
val resultVarname = evalVarName(it.type, asmExtra.rpnDepth)
|
||||
asmExtra.rpnDepth++
|
||||
symbolTable.resetCachedFlat()
|
||||
|
||||
val scopeName = (scope as PtNamedNode).scopedName
|
||||
val leftVarPt = PtIdentifier("$scopeName.$leftvar", it.leftType, it.position)
|
||||
leftVarPt.parent = scope
|
||||
if(it.rightType largerThan it.type) {
|
||||
if(it.operator !in ComparisonOperators)
|
||||
throw AssemblyError("only expected a boolean comparison, got ${it.operator}")
|
||||
if(it.rightType !in WordDatatypes || it.type !in ByteDatatypes) {
|
||||
when (it.rightType) {
|
||||
DataType.STR -> {
|
||||
val rightString = PtIdentifier("$scopeName.$rightvar", DataType.STR, it.position)
|
||||
rightString.parent = scope
|
||||
asmgen.assignExpressionToVariable(leftVarPt, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
|
||||
asmgen.assignExpressionToVariable(rightString, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
|
||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||
when(it.operator) {
|
||||
"==" -> asmgen.out(" and #1 | eor #1")
|
||||
"!=" -> asmgen.out(" and #1")
|
||||
"<=" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
">=" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
"<" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
">" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
asmgen.out(" sta $resultVarname")
|
||||
}
|
||||
in NumericDatatypes -> {
|
||||
val jumpIfFalseLabel = asmgen.makeLabel("cmp")
|
||||
val rightVarPt = PtIdentifier("$scopeName.$rightvar", it.rightType, it.position)
|
||||
rightVarPt.parent = scope
|
||||
asmgen.testNonzeroComparisonAndJump(leftVarPt, it.operator, rightVarPt, jumpIfFalseLabel, null, null)
|
||||
asmgen.out("""
|
||||
lda #1
|
||||
bne +
|
||||
$jumpIfFalseLabel lda #0
|
||||
+ sta $resultVarname""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type for operator: ${it.rightType}")
|
||||
}
|
||||
} else {
|
||||
// use in-place assignment with optional cast to the target variable
|
||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, it.rightType, variableAsmName = rightvar)
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, it.leftType, scope, assign.position, variableAsmName = leftvar)
|
||||
val operator = if(it.operator in ComparisonOperators) it.operator else it.operator+'='
|
||||
val augAssign = AsmAugmentedAssignment(src, operator, target, program.memsizer, assign.position)
|
||||
augmentableAsmGen.translate(augAssign, scope)
|
||||
if(resultVarname!=leftvar)
|
||||
assignTypeCastedIdentifier(resultVarname, it.type, leftvar, it.leftType)
|
||||
}
|
||||
} else {
|
||||
// use in-place assignment with optional cast to the target variable
|
||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, it.rightType, variableAsmName = rightvar)
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, it.leftType, scope, assign.position, variableAsmName = leftvar)
|
||||
val operator = if(it.operator in ComparisonOperators) it.operator else it.operator+'='
|
||||
val augAssign = AsmAugmentedAssignment(src, operator, target, program.memsizer, assign.position)
|
||||
augmentableAsmGen.translate(augAssign, scope)
|
||||
if(resultVarname!=leftvar)
|
||||
assignTypeCastedIdentifier(resultVarname, it.type, leftvar, it.leftType)
|
||||
}
|
||||
}
|
||||
is PtExpression -> {
|
||||
val varname = evalVarName(it.type, asmExtra.rpnDepth)
|
||||
assignExpressionToVariable(it, varname, it.type)
|
||||
asmExtra.rpnDepth++
|
||||
}
|
||||
else -> throw AssemblyError("weird rpn node")
|
||||
}
|
||||
}
|
||||
require(asmExtra.rpnDepth-startDepth == 1) {
|
||||
"unbalanced RPN ${value.position}"
|
||||
}
|
||||
|
||||
val resultVariable = evalVars.getValue(getVarDt(value.type)).pop()
|
||||
asmExtra.rpnDepth--
|
||||
if(!(assign.target.datatype equalsSize value.type)) {
|
||||
// we only allow for transparent byte -> word / ubyte -> uword assignments
|
||||
// any other type difference is an error
|
||||
if(assign.target.datatype in WordDatatypes && value.type in ByteDatatypes) {
|
||||
assignVariableToRegister(resultVariable, RegisterOrPair.A, value.type==DataType.BYTE, scope, assign.position)
|
||||
asmgen.signExtendAYlsb(value.type)
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
} else {
|
||||
throw AssemblyError("data type mismatch, missing typecast ${value.type} -> ${assign.target.datatype}")
|
||||
}
|
||||
} else {
|
||||
when (value.type) {
|
||||
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 attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
if(expr.right.asConstInteger() == 0) {
|
||||
@ -973,82 +727,6 @@ $jumpIfFalseLabel lda #0
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
|
||||
private fun attemptAssignToByteCompareZeroRPN(expr: PtRpn, assign: AsmAssignment): Boolean {
|
||||
val (leftRpn, oper, right) = expr.finalOperation()
|
||||
val left = if(expr.children.size!=3 || leftRpn !is PtExpression)
|
||||
expr.truncateLastOperator()
|
||||
else
|
||||
leftRpn
|
||||
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) {
|
||||
"==" -> {
|
||||
@ -1228,15 +906,6 @@ $jumpIfFalseLabel lda #0
|
||||
is PtIdentifier -> {
|
||||
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
|
||||
}
|
||||
is PtRpn -> {
|
||||
val addrExpr = value.address as PtRpn
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
} else {
|
||||
assignViaExprEval(value.address)
|
||||
}
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
@ -3159,10 +2828,6 @@ $jumpIfFalseLabel lda #0
|
||||
addressExpr is PtIdentifier -> {
|
||||
asmgen.storeAIntoPointerVar(addressExpr)
|
||||
}
|
||||
addressExpr is PtRpn -> {
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.finalOperator().operator, true))
|
||||
storeViaExprEval()
|
||||
}
|
||||
addressExpr is PtBinaryExpression -> {
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
||||
storeViaExprEval()
|
||||
|
@ -14,9 +14,9 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
errors: IErrorReporter
|
||||
): IAssemblyProgram? {
|
||||
|
||||
if(options.useRPN) {
|
||||
program.transformBinExprToRPN()
|
||||
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
|
||||
if(options.useNewExprCode) {
|
||||
// TODO("transform BinExprs?")
|
||||
// errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY)
|
||||
}
|
||||
|
||||
// you could write a code generator directly on the PtProgram AST,
|
||||
|
@ -92,13 +92,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
value.add(origAssign.value)
|
||||
} else {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
if(codeGen.program.binaryExpressionsAreRPN) {
|
||||
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))
|
||||
if(codeGen.options.useNewExprCode) {
|
||||
TODO("use something else than a BinExpr?")
|
||||
} else {
|
||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||
@ -272,13 +267,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
expressionEval.translateExpression(array.index)
|
||||
} else {
|
||||
val mult : PtExpression
|
||||
if(codeGen.program.binaryExpressionsAreRPN) {
|
||||
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))
|
||||
if(codeGen.options.useNewExprCode) {
|
||||
TODO("use something else than a BinExpr?")
|
||||
} else {
|
||||
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||
mult.children += array.index
|
||||
|
@ -93,7 +93,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
is PtPrefix -> translate(expr)
|
||||
is PtArrayIndexer -> translate(expr)
|
||||
is PtBinaryExpression -> translate(expr)
|
||||
is PtRpn -> translate(expr)
|
||||
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
|
||||
is PtFunctionCall -> translate(expr)
|
||||
is PtContainmentCheck -> translate(expr)
|
||||
@ -316,10 +315,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, codeGen.irType(cast.type), actualResultReg2, actualResultFpReg2)
|
||||
}
|
||||
|
||||
private fun translate(rpn: PtRpn): ExpressionCodeResult {
|
||||
TODO("RPN expression (intermediate codegen) $rpn")
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
val vmDt = codeGen.irType(binExpr.left.type)
|
||||
val signed = binExpr.left.type in SignedDatatypes
|
||||
|
@ -269,8 +269,6 @@ class IRCodeGen(
|
||||
is PtProgram,
|
||||
is PtArrayIndexer,
|
||||
is PtBinaryExpression,
|
||||
is PtRpn,
|
||||
is PtRpnOperator,
|
||||
is PtIdentifier,
|
||||
is PtWhenChoice,
|
||||
is PtPrefix,
|
||||
@ -912,39 +910,14 @@ class IRCodeGen(
|
||||
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
|
||||
}
|
||||
}
|
||||
is PtRpn -> {
|
||||
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 -> {
|
||||
throw AssemblyError("weird condition node: $condition")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfFollowedByJustGotoRPN(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
|
||||
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<IRCodeChunkBase> {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if (irDtLeft == IRDataType.FLOAT) {
|
||||
@ -1010,7 +983,6 @@ class IRCodeGen(
|
||||
irDtLeft: IRDataType,
|
||||
goto: PtJump
|
||||
): MutableList<IRCodeChunkBase> {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
@ -1039,7 +1011,6 @@ class IRCodeGen(
|
||||
irDtLeft: IRDataType,
|
||||
goto: PtJump
|
||||
): MutableList<IRCodeChunkBase> {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
@ -1093,7 +1064,6 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranch: Opcode
|
||||
val compResultReg: Int
|
||||
@ -1155,7 +1125,6 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranchOpcode: Opcode
|
||||
val elseBranchFirstReg: Int
|
||||
|
@ -15,9 +15,9 @@ class VmCodeGen: ICodeGeneratorBackend {
|
||||
errors: IErrorReporter
|
||||
): IAssemblyProgram? {
|
||||
|
||||
if(options.useRPN) {
|
||||
program.transformBinExprToRPN()
|
||||
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
|
||||
if(options.useNewExprCode) {
|
||||
// TODO("transform BinExprs?")
|
||||
// errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY)
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,7 +52,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
|
||||
val useRPN by cli.option(ArgType.Boolean, fullName = "rpn", description = "use RPN for expression code-generation (experimental)")
|
||||
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
@ -127,7 +127,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
asmListfile == true,
|
||||
experimentalCodegen == true,
|
||||
varsHigh == true,
|
||||
useRPN == true,
|
||||
useNewExprCode == true,
|
||||
compilationTarget,
|
||||
evalStackAddr,
|
||||
processedSymbols,
|
||||
@ -192,7 +192,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
asmListfile == true,
|
||||
experimentalCodegen == true,
|
||||
varsHigh == true,
|
||||
useRPN == true,
|
||||
useNewExprCode == true,
|
||||
compilationTarget,
|
||||
evalStackAddr,
|
||||
processedSymbols,
|
||||
|
@ -6,11 +6,9 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.printProgram
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.printAst
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.*
|
||||
import prog8.codegen.vm.VmCodeGen
|
||||
@ -38,7 +36,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val asmListfile: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
val varsHigh: Boolean,
|
||||
val useRPN: Boolean,
|
||||
val useNewExprCode: Boolean,
|
||||
val compilationTarget: String,
|
||||
val evalStackBaseAddress: UInt?,
|
||||
val symbolDefs: Map<String, String>,
|
||||
@ -79,7 +77,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
asmListfile = args.asmListfile
|
||||
experimentalCodegen = args.experimentalCodegen
|
||||
varsHigh = args.varsHigh
|
||||
useRPN = args.useRPN
|
||||
useNewExprCode = args.useNewExprCode
|
||||
evalStackBaseAddress = args.evalStackBaseAddress
|
||||
outputDir = args.outputDir.normalize()
|
||||
symbolDefs = args.symbolDefs
|
||||
|
@ -452,8 +452,6 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||
// NOTE: the Ast maker here will NOT create RPN-expression nodes for the code generator.
|
||||
// If the code generator prefers RPN expressions, it has to transform the PtBinaryExpression itself into PtRpn.
|
||||
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val actualType = if(type==DataType.BOOL) DataType.UBYTE else type
|
||||
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)
|
||||
|
@ -33,7 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = false,
|
||||
useNewExprCode = false,
|
||||
compilationTarget = target.name,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
|
@ -50,7 +50,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = false,
|
||||
useNewExprCode = false,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
|
@ -1,45 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
})
|
@ -160,9 +160,9 @@ main {
|
||||
qq = 16000 + c*${'$'}0008
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, text, writeAssembly = true, useRPN = false) shouldNotBe null
|
||||
compileText(C64Target(), true, text, writeAssembly = true, useRPN = true) shouldNotBe null
|
||||
compileText(VMTarget(), true, text, writeAssembly = true, useRPN = false) shouldNotBe null
|
||||
// TODO RPN once IR RPN codegen is done: compileText(VMTarget(), true, text, writeAssembly = true, useRPN = true) shouldNotBe null
|
||||
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
||||
compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
||||
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
||||
compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
||||
}
|
||||
})
|
@ -18,7 +18,7 @@ internal fun compileFile(
|
||||
errors: IErrorReporter? = null,
|
||||
writeAssembly: Boolean = true,
|
||||
optFloatExpr: Boolean = true,
|
||||
useRPN: Boolean = false
|
||||
useNewExprCode: Boolean = false
|
||||
) : CompilationResult? {
|
||||
val filepath = fileDir.resolve(fileName)
|
||||
assumeReadableFile(filepath)
|
||||
@ -32,7 +32,7 @@ internal fun compileFile(
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = useRPN,
|
||||
useNewExprCode = useNewExprCode,
|
||||
platform.name,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
@ -54,11 +54,11 @@ internal fun compileText(
|
||||
errors: IErrorReporter? = null,
|
||||
writeAssembly: Boolean = true,
|
||||
optFloatExpr: Boolean = true,
|
||||
useRPN: Boolean = false
|
||||
useNewExprCode: 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, useRPN=useRPN)
|
||||
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, useNewExprCode=useNewExprCode)
|
||||
}
|
||||
|
@ -1,26 +1,14 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
replace RPN by something that actually works and is efficient...
|
||||
|
||||
RPN: assembler fails selftest (something with output file name extension being wrong!)
|
||||
RPN: swirl is MUCH slower
|
||||
RPN: wizzine is slightly slower
|
||||
RPN: bsieve is much slower
|
||||
|
||||
then:
|
||||
RPN: swirl is bigger
|
||||
RPN: petaxian is 900 bytes larger, chess is a lot bigger
|
||||
RPN: cube3d is much larger, but a bit faster
|
||||
RPN: cube3d-float is massive and slow
|
||||
RPN: mandelbrot is bigger, but seems faster
|
||||
RPN: Implement RPN codegen for IR.
|
||||
|
||||
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
- ubyte fits = cx<numCellsHoriz-1 much larger code than when declared as bool. (RPN only?)
|
||||
- if fits and @(celladdr(cx+1)) much larger code than if fits and not @(celladdr(cx+1)) (RPN only?)
|
||||
- @($5000 + c<<$0003) = 22 why is 22 pushed on the stack first and then popped after the address is calcualted
|
||||
- ubyte fits = cx<numCellsHoriz-1 much larger code than when declared as bool ????
|
||||
- if fits and @(celladdr(cx+1)) much larger code than if fits and not @(celladdr(cx+1)) ????
|
||||
- @($5000 + c<<$0003) = 22 why is 22 pushed on the stack first and then popped after the address is calculated
|
||||
|
||||
...
|
||||
|
||||
|
@ -43,7 +43,7 @@ class RequestParser : Take {
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHigh = false,
|
||||
useRPN = false
|
||||
useNewExprCode = false
|
||||
)
|
||||
val compilationResult = compileProgram(args)
|
||||
return RsJson(Jsonding())
|
||||
|
Loading…
x
Reference in New Issue
Block a user