mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
more Rpn optimizations
This commit is contained in:
parent
52b560e72d
commit
6e4ae034b2
@ -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<String, StNode> by lazy {
|
||||
private var cachedFlat: Map<String, StNode>? = null
|
||||
|
||||
val flat: Map<String, StNode> get() {
|
||||
if(cachedFlat!=null)
|
||||
return cachedFlat!!
|
||||
|
||||
val result = mutableMapOf<String, StNode>()
|
||||
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<StStaticVariable> by lazy {
|
||||
val vars = mutableListOf<StStaticVariable>()
|
||||
fun collect(node: StNode) {
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<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(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")
|
||||
}
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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<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> {
|
||||
if(program.binaryExpressionsAreRPN) {
|
||||
TODO ("RPN (intermediate codegen)")
|
||||
require(!program.binaryExpressionsAreRPN)
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
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<IRCodeChunkBase>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
45
compiler/test/codegeneration/TestRPNCodeGen.kt
Normal file
45
compiler/test/codegeneration/TestRPNCodeGen.kt
Normal file
@ -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
|
||||
}
|
||||
|
||||
})
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user