diff --git a/codeCore/src/prog8/code/ast/AstPrinter.kt b/codeCore/src/prog8/code/ast/AstPrinter.kt index d8b8717da..39aada51d 100644 --- a/codeCore/src/prog8/code/ast/AstPrinter.kt +++ b/codeCore/src/prog8/code/ast/AstPrinter.kt @@ -12,6 +12,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { return when(node) { is PtAssignTarget -> "" is PtAssignment -> "" + is PtAugmentedAssign -> " ${node.operator}" is PtBreakpoint -> "%breakpoint" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtAddressOf -> "&" @@ -30,7 +31,10 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) { is PtIdentifier -> "${node.name} ${type(node.type)}" is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}" is PtMemoryByte -> "@()" - is PtNumber -> "${node.number.toHex()} ${type(node.type)}" + is PtNumber -> { + val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex() + "$numstr ${type(node.type)}" + } is PtPrefix -> node.operator is PtRange -> "" is PtString -> "\"${node.value.escape()}\"" diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 8f9c5f440..831364e54 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -43,36 +43,22 @@ class PtAssignment(position: Position) : PtNode(position) { get() = children[0] as PtAssignTarget val value: PtExpression get() = children[1] as PtExpression +} - val isInplaceAssign: Boolean by lazy { - val target = target.children.single() as PtExpression - when(val source = value) { - is PtArrayIndexer -> { - if(target is PtArrayIndexer && source.type==target.type) { - if(target.variable isSameAs source.variable) { - target.index isSameAs source.index - } - } - false - } - is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.name==source.name - is PtMachineRegister -> target is PtMachineRegister && target.register==source.register - is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address - is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number - is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier - is PtPrefix -> { - (target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value) - || - (target is PtIdentifier && (source.value as? PtIdentifier)?.name==target.name) - } - is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value - is PtBinaryExpression -> - target isSameAs source.left - else -> false + +class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) { + val target: PtAssignTarget + get() = children[0] as PtAssignTarget + val value: PtExpression + get() = children[1] as PtExpression + init { + require(operator.endsWith('=') || operator in PrefixOperators) { + "invalid augmented assign operator $operator" } } } + class PtAssignTarget(position: Position) : PtNode(position) { val identifier: PtIdentifier? get() = children.single() as? PtIdentifier diff --git a/codeCore/src/prog8/code/core/Operators.kt b/codeCore/src/prog8/code/core/Operators.kt index 0e218aa30..ed9adb321 100644 --- a/codeCore/src/prog8/code/core/Operators.kt +++ b/codeCore/src/prog8/code/core/Operators.kt @@ -5,6 +5,7 @@ val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val LogicalOperators = setOf("and", "or", "xor", "not") val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor") val BitwiseOperators = setOf("&", "|", "^", "~") +val PrefixOperators = setOf("+", "-", "~", "not") // val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators fun invertedComparisonOperator(operator: String) = diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 4904a02a6..7da1e9bcb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -148,7 +148,7 @@ class AsmGen6502Internal ( is PtVariable, is PtMemMapped -> { val sourceName = asmVariableName(pointervar) if (isTargetCpu(CpuType.CPU65c02)) { - return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string + return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // pointervar is already in the zero page, no need to copy out(" lda ($sourceName)") sourceName @@ -162,7 +162,7 @@ class AsmGen6502Internal ( "P8ZP_SCRATCH_W1" } } else { - return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string + return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // pointervar is already in the zero page, no need to copy out(" ldy #0 | lda ($sourceName),y") sourceName @@ -185,7 +185,7 @@ class AsmGen6502Internal ( internal fun storeAIntoPointerVar(pointervar: PtIdentifier) { val sourceName = asmVariableName(pointervar) if (isTargetCpu(CpuType.CPU65c02)) { - if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string + if (allocator.isZpVar(pointervar.name.split('.'))) { // pointervar is already in the zero page, no need to copy out(" sta ($sourceName)") } else { @@ -197,7 +197,7 @@ class AsmGen6502Internal ( sta (P8ZP_SCRATCH_W2)""") } } else { - if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string + if (allocator.isZpVar(pointervar.name.split('.'))) { // pointervar is already in the zero page, no need to copy out(" ldy #0 | sta ($sourceName),y") } else { @@ -337,6 +337,7 @@ class AsmGen6502Internal ( is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt) is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt) is PtAssignment -> assignmentAsmGen.translate(stmt) + is PtAugmentedAssign -> assignmentAsmGen.translate(stmt) is PtJump -> { val (asmLabel, indirect) = getJumpTarget(stmt) jmp(asmLabel, indirect) @@ -505,7 +506,7 @@ class AsmGen6502Internal ( translateNormalAssignment( AsmAssignment( AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY), - target, false, program.memsizer, value.position + target, program.memsizer, value.position ) ) } @@ -962,7 +963,7 @@ $repeatLabel lda $counterVar } } - internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name.split('.')) // TODO dotted string + internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name.split('.')) internal fun jmp(asmLabel: String, indirect: Boolean=false) { if(indirect) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 1254981a2..e55f31845 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -335,7 +335,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null) else AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen) - val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position) + val assign = AsmAssignment(src, target, program.memsizer, fcall.position) asmgen.translateNormalAssignment(assign) } @@ -1089,7 +1089,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } } val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname) - val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) + val assign = AsmAssignment(src, tgt, program.memsizer, value.position) asmgen.translateNormalAssignment(assign) } conv.reg != null -> { @@ -1107,7 +1107,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } } val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen) - val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) + val assign = AsmAssignment(src, tgt, program.memsizer, value.position) asmgen.translateNormalAssignment(assign) } else -> throw AssemblyError("callconv") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index d0c0e9c5c..3091bc0ac 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -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, false, program.memsizer, Position.DUMMY)) + asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY)) } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 9ad389a3c..1d65428ba 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, allocateZeropageVariables() } - internal fun isZpVar(scopedName: List) = scopedName in zeropageVars // TODO as dotted string instead of list? + internal fun isZpVar(scopedName: List) = scopedName in zeropageVars // TODO as dotted string instead of list internal fun getFloatAsmConst(number: Double): String { val asmName = globalFloatConsts[number] diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index c0b615274..dda428e83 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -44,7 +44,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, asmgen.asmVariableName(array.variable) } - lateinit var origAssign: AsmAssignment + lateinit var origAssign: AsmAssignmentBase init { if(register!=null && datatype !in NumericDatatypes) @@ -52,8 +52,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, } companion object { - fun fromAstAssignment(assign: PtAssignment, asmgen: AsmGen6502Internal): AsmAssignTarget { - with(assign.target) { + fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget { + with(target) { when { identifier != null -> { val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen) @@ -64,13 +64,13 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, if(reg.statusflag!=null) throw AssemblyError("can't assign value to processor statusflag directly") else - return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this) + return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, register=reg.registerOrPair, origAstTarget = this) } } - return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingISub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) + return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) } - array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this) - memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this) + array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this) + memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this) else -> throw AssemblyError("weird target") } } @@ -191,12 +191,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind, } -internal class AsmAssignment(val source: AsmAssignSource, - val target: AsmAssignTarget, - val isAugmentable: Boolean, - memsizer: IMemSizer, - val position: Position) { - +internal sealed class AsmAssignmentBase(val source: AsmAssignSource, + val target: AsmAssignTarget, + val memsizer: IMemSizer, + val position: Position) { init { if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY)) require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" } @@ -205,3 +203,15 @@ internal class AsmAssignment(val source: AsmAssignSource, } } } + +internal class AsmAssignment(source: AsmAssignSource, + target: AsmAssignTarget, + memsizer: IMemSizer, + position: Position): AsmAssignmentBase(source, target, memsizer, position) + +internal class AsmAugmentedAssignment(source: AsmAssignSource, + val operator: String, + target: AsmAssignTarget, + memsizer: IMemSizer, + position: Position): AsmAssignmentBase(source, target, memsizer, position) + diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 9bc06cdcc..d7670c2b2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -11,23 +11,22 @@ internal class AssignmentAsmGen(private val program: PtProgram, private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator) fun translate(assignment: PtAssignment) { - val target = AsmAssignTarget.fromAstAssignment(assignment, asmgen) + val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen) val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) - val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position) + val assign = AsmAssignment(source, target, program.memsizer, assignment.position) target.origAssign = assign + translateNormalAssignment(assign) + } - if(assign.isAugmentable) - augmentableAsmGen.translate(assign) - else - translateNormalAssignment(assign) + 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) + target.origAssign = assign + augmentableAsmGen.translate(assign) } fun translateNormalAssignment(assign: AsmAssignment) { - if(assign.isAugmentable) { - augmentableAsmGen.translate(assign) - return - } - when(assign.source.kind) { SourceStorageKind.LITERALNUMBER -> { // simple case: assign a constant number @@ -277,14 +276,13 @@ internal class AssignmentAsmGen(private val program: PtProgram, translateNormalAssignment( AsmAssignment( AsmAssignSource.fromAstSource(value.value, program, asmgen), - assign.target, - false, program.memsizer, assign.position + assign.target, program.memsizer, assign.position ) ) when (value.operator) { "+" -> {} - "-" -> augmentableAsmGen.inplaceNegate(assign, true) - "~" -> augmentableAsmGen.inplaceInvert(assign) + "-" -> inplaceNegate(assign, true) + "~" -> inplaceInvert(assign) "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") else -> throw AssemblyError("invalid prefix operator") } @@ -308,7 +306,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { + private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { 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. @@ -318,8 +316,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, // array[x] = -value ... use a tempvar then store that back into the array. val tempvar = asmgen.getTempVarName(assign.target.datatype) val assignToTempvar = AsmAssignment(assign.source, - AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), - false, program.memsizer, assign.position) + AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, + variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position) asmgen.translateNormalAssignment(assignToTempvar) when(assign.target.datatype) { in ByteDatatypes -> assignVariableByte(assign.target, tempvar) @@ -1106,7 +1104,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, lsb.parent = value.parent lsb.add(value) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) - val assign = AsmAssignment(src, target, false, program.memsizer, value.position) + val assign = AsmAssignment(src, target, program.memsizer, value.position) translateNormalAssignment(assign) } @@ -2831,7 +2829,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) { val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) - val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) + val assign = AsmAssignment(src, tgt, program.memsizer, expr.position) translateNormalAssignment(assign) } @@ -2841,7 +2839,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } else { val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName) - val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) + val assign = AsmAssignment(src, tgt, program.memsizer, expr.position) translateNormalAssignment(assign) } } @@ -2849,7 +2847,218 @@ internal class AssignmentAsmGen(private val program: PtProgram, internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) { val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName) - val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY) + val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY) translateNormalAssignment(assign) } + + internal fun inplaceInvert(assign: AsmAssignment) { + val target = assign.target + when (assign.target.datatype) { + DataType.UBYTE -> { + when (target.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda ${target.asmVarname} + eor #255 + sta ${target.asmVarname}""") + } + TargetStorageKind.MEMORY -> { + val memory = target.memory!! + when (memory.address) { + is PtNumber -> { + val addr = (memory.address as PtNumber).number.toHex() + asmgen.out(""" + lda $addr + eor #255 + sta $addr""") + } + is PtIdentifier -> { + val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier) + asmgen.out(" eor #255") + asmgen.out(" sta ($sourceName),y") + } + else -> { + asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) + asmgen.out(""" + ldy #0 + lda (P8ZP_SCRATCH_W2),y + eor #255""") + asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2") + } + } + } + TargetStorageKind.REGISTER -> { + when(target.register!!) { + RegisterOrPair.A -> asmgen.out(" eor #255") + RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax") + RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay") + else -> throw AssemblyError("invalid reg dt for byte invert") + } + } + TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert") + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign)) + else -> throw AssemblyError("weird target") + } + } + DataType.UWORD -> { + when (target.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda ${target.asmVarname} + eor #255 + sta ${target.asmVarname} + lda ${target.asmVarname}+1 + eor #255 + sta ${target.asmVarname}+1""") + } + TargetStorageKind.REGISTER -> { + when(target.register!!) { + RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255") + RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255") + RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay") + in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers") + else -> throw AssemblyError("invalid reg dt for word invert") + } + } + TargetStorageKind.STACK -> TODO("no asm gen for word stack invert") + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign)) + else -> throw AssemblyError("weird target") + } + } + else -> throw AssemblyError("invert of invalid type") + } + } + + internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) { + val target = assign.target + val datatype = if(ignoreDatatype) { + when(target.datatype) { + DataType.UBYTE, DataType.BYTE -> DataType.BYTE + DataType.UWORD, DataType.WORD -> DataType.WORD + else -> target.datatype + } + } else target.datatype + when (datatype) { + DataType.BYTE -> { + when (target.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda #0 + sec + sbc ${target.asmVarname} + sta ${target.asmVarname}""") + } + TargetStorageKind.REGISTER -> { + when(target.register!!) { + RegisterOrPair.A -> { + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" eor #255 | ina") + else + asmgen.out(" eor #255 | clc | adc #1") + + } + RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx") + RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny") + else -> throw AssemblyError("invalid reg dt for byte negate") + } + } + 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)) + else -> throw AssemblyError("weird target") + } + } + DataType.WORD -> { + when (target.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda #0 + sec + sbc ${target.asmVarname} + sta ${target.asmVarname} + lda #0 + sbc ${target.asmVarname}+1 + sta ${target.asmVarname}+1""") + } + TargetStorageKind.REGISTER -> { + when(target.register!!) { //P8ZP_SCRATCH_REG + RegisterOrPair.AX -> { + asmgen.out(""" + sec + eor #255 + adc #0 + pha + txa + eor #255 + adc #0 + tax + pla""") + } + RegisterOrPair.AY -> { + asmgen.out(""" + sec + eor #255 + adc #0 + pha + tya + eor #255 + adc #0 + tay + pla""") + } + RegisterOrPair.XY -> { + asmgen.out(""" + sec + txa + eor #255 + adc #0 + tax + tya + eor #255 + adc #0 + tay""") + } + in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers") + else -> throw AssemblyError("invalid reg dt for word neg") + } + } + 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)) + else -> throw AssemblyError("weird target") + } + } + DataType.FLOAT -> { + when (target.kind) { + TargetStorageKind.REGISTER -> { + when(target.register!!) { + RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP") + RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF") + else -> throw AssemblyError("invalid float register") + } + } + TargetStorageKind.VARIABLE -> { + // simply flip the sign bit in the float + asmgen.out(""" + lda ${target.asmVarname}+1 + eor #$80 + sta ${target.asmVarname}+1 + """) + } + TargetStorageKind.STACK -> TODO("no asm gen for float stack negate") + TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign)) + else -> throw AssemblyError("weird target for in-place float negation") + } + } + else -> throw AssemblyError("negate of invalid type") + } + } + + private fun makePrefixedExprFromArrayExprAssign(operator: String, assign: AsmAssignment): AsmAssignment { + val prefix = PtPrefix(operator, assign.source.datatype, assign.source.array!!.position) + prefix.add(assign.source.array) + prefix.parent = assign.target.origAstTarget ?: program + val prefixSrc = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, assign.source.datatype, expression=prefix) + return AsmAssignment(prefixSrc, assign.target, assign.memsizer, assign.position) + } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index 29aedf238..82f86c32e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -11,134 +11,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator ) { - fun translate(assign: AsmAssignment) { - require(assign.isAugmentable) - require(assign.source.kind == SourceStorageKind.EXPRESSION) { - "non-expression assign value should be handled elsewhere ${assign.position}" - } + fun translate(assign: AsmAugmentedAssignment) { - when (val value = assign.source.expression!!) { - is PtPrefix -> { - // A = -A , A = +A, A = ~A, A = not A - when (value.operator) { - "+" -> {} - "-" -> inplaceNegate(assign, false) - "~" -> inplaceInvert(assign) - else -> throw AssemblyError("invalid prefix operator") - } + when(assign.operator) { + "-" -> { + val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position) + assignmentAsmGen.inplaceNegate(a2, false) + } + "~" -> { + val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position) + assignmentAsmGen.inplaceInvert(a2) + } + "+" -> { /* is a nop */ } + else -> { + if(assign.operator.endsWith('=')) + augmentedAssignExpr(assign) + else + throw AssemblyError("invalid augmented assign operator ${assign.operator}") } - is PtTypeCast -> inplaceCast(assign.target, value, assign.position) - is PtBinaryExpression -> inplaceBinary(assign.target, value) - else -> throw AssemblyError("invalid aug assign value type") } } - private fun inplaceBinary(target: AsmAssignTarget, binExpr: PtBinaryExpression) { - val astTarget = target.origAstTarget!! - if (binExpr.left isSameAs astTarget) { - // A = A Something - return inplaceModification(target, binExpr.operator, binExpr.right) + private fun augmentedAssignExpr(assign: AsmAugmentedAssignment) { + val srcValue = assign.source.toAstExpression(assign.target.scope as PtNamedNode) + when (assign.operator) { + "+=" -> inplaceModification(assign.target, "+", srcValue) + "-=" -> inplaceModification(assign.target, "-", srcValue) + "*=" -> inplaceModification(assign.target, "*", srcValue) + "/=" -> inplaceModification(assign.target, "/", srcValue) + "|=" -> inplaceModification(assign.target, "|", srcValue) + "&=" -> 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}") // TODO fallback to non-augmented Assign? } - - if (binExpr.operator in AssociativeOperators) { - if (binExpr.right isSameAs astTarget) { - // A = 5 A - return inplaceModification(target, binExpr.operator, binExpr.left) - } - - val leftBinExpr = binExpr.left as? PtBinaryExpression - if (leftBinExpr?.operator == binExpr.operator) { - // TODO better optimize the chained asm to avoid intermediate stores/loads? - when { - binExpr.right isSameAs astTarget -> { - // A = (x y) A - inplaceModification(target, binExpr.operator, leftBinExpr.left) - inplaceModification(target, binExpr.operator, leftBinExpr.right) - return - } - leftBinExpr.left isSameAs astTarget -> { - // A = (A x) y - inplaceModification(target, binExpr.operator, leftBinExpr.right) - inplaceModification(target, binExpr.operator, binExpr.right) - return - } - leftBinExpr.right isSameAs astTarget -> { - // A = (x A) y - inplaceModification(target, binExpr.operator, leftBinExpr.left) - inplaceModification(target, binExpr.operator, binExpr.right) - return - } - } - } - val rightBinExpr = binExpr.right as? PtBinaryExpression - if (rightBinExpr?.operator == binExpr.operator) { - when { - binExpr.left isSameAs astTarget -> { - // A = A (x y) - inplaceModification(target, binExpr.operator, rightBinExpr.left) - inplaceModification(target, binExpr.operator, rightBinExpr.right) - return - } - rightBinExpr.left isSameAs astTarget -> { - // A = y (A x) - inplaceModification(target, binExpr.operator, binExpr.left) - inplaceModification(target, binExpr.operator, rightBinExpr.right) - return - } - rightBinExpr.right isSameAs astTarget -> { - // A = y (x y) - inplaceModification(target, binExpr.operator, binExpr.left) - inplaceModification(target, binExpr.operator, rightBinExpr.left) - return - } - } - } - } - - val leftBinExpr = binExpr.left as? PtBinaryExpression - val rightBinExpr = binExpr.right as? PtBinaryExpression - if(leftBinExpr!=null && rightBinExpr==null) { - if(leftBinExpr.left isSameAs astTarget) { - // X = (X Right) Something - inplaceModification(target, leftBinExpr.operator, leftBinExpr.right) - inplaceModification(target, binExpr.operator, binExpr.right) - return - } - if(leftBinExpr.right isSameAs astTarget) { - // X = (Left X) Something - if(leftBinExpr.operator in AssociativeOperators) { - inplaceModification(target, leftBinExpr.operator, leftBinExpr.left) - inplaceModification(target, binExpr.operator, binExpr.right) - return - } else { - throw AssemblyError("operands in wrong order for non-associative operator") - } - } - } - if(leftBinExpr==null && rightBinExpr!=null) { - if(rightBinExpr.left isSameAs astTarget) { - // X = Something (X Right) - if(binExpr.operator in AssociativeOperators) { - inplaceModification(target, rightBinExpr.operator, rightBinExpr.right) - inplaceModification(target, binExpr.operator, binExpr.left) - return - } else { - throw AssemblyError("operands in wrong order for non-associative operator") - } - } - if(rightBinExpr.right isSameAs astTarget) { - // X = Something (Left X) - if(binExpr.operator in AssociativeOperators && rightBinExpr.operator in AssociativeOperators) { - inplaceModification(target, rightBinExpr.operator, rightBinExpr.left) - inplaceModification(target, binExpr.operator, binExpr.left) - return - } else { - throw AssemblyError("operands in wrong order for non-associative operator") - } - } - } - - throw AssemblyError("assignment should follow augmentable rules $binExpr") } private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: PtExpression) { @@ -303,7 +210,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, target.datatype == DataType.BYTE, null, asmgen ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position) assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A) } @@ -314,7 +221,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, target.datatype == DataType.WORD, null, asmgen ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position) assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) } @@ -325,7 +232,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, true, null, asmgen ) - val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) + val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position) assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.assignFAC1float(target) } @@ -1736,260 +1643,50 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, """) asmgen.restoreRegisterLocal(CpuRegister.X) } - - private fun inplaceCast(target: AsmAssignTarget, cast: PtTypeCast, position: Position) { - val outerCastDt = cast.type - val innerCastDt = (cast.value as? PtTypeCast)?.type - if (innerCastDt == null) { - // simple typecast where the value is the target - when (target.datatype) { - DataType.UBYTE, DataType.BYTE -> { /* byte target can't be typecasted to anything else at all */ } - DataType.UWORD, DataType.WORD -> { - when (outerCastDt) { - DataType.UBYTE, DataType.BYTE -> { - when (target.kind) { - TargetStorageKind.VARIABLE -> { - if(asmgen.isTargetCpu(CpuType.CPU65c02)) - asmgen.out(" stz ${target.asmVarname}+1") - else - asmgen.out(" lda #0 | sta ${target.asmVarname}+1") - } - TargetStorageKind.ARRAY -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true) - asmgen.out(" lda #0 | sta ${target.asmVarname},y") - } - TargetStorageKind.STACK -> { - if(asmgen.isTargetCpu(CpuType.CPU65c02)) - asmgen.out(" stz P8ESTACK_HI+1,x") - else - asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x") - } - else -> throw AssemblyError("weird target") - } - } - DataType.UWORD, DataType.WORD, in IterableDatatypes -> {} - DataType.FLOAT -> throw AssemblyError("can't cast float in-place") - else -> throw AssemblyError("weird cast type") - } - } - DataType.FLOAT -> { - if (outerCastDt != DataType.FLOAT) - throw AssemblyError("in-place cast of a float makes no sense") - } - else -> throw AssemblyError("invalid cast target type") - } - } else { - // typecast with nested typecast, that has the target as a value - // calculate singular cast that is required - val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt - val resultingCast = PtTypeCast(castDt, position) - resultingCast.add((cast.value as PtTypeCast).value) - require(castDt!=resultingCast.value.type) - inplaceCast(target, resultingCast, position) - } - } - - internal fun inplaceInvert(assign: AsmAssignment) { - val target = assign.target - when (assign.target.datatype) { - DataType.UBYTE -> { - when (target.kind) { - TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda ${target.asmVarname} - eor #255 - sta ${target.asmVarname}""") - } - TargetStorageKind.MEMORY -> { - val memory = target.memory!! - when (memory.address) { - is PtNumber -> { - val addr = (memory.address as PtNumber).number.toHex() - asmgen.out(""" - lda $addr - eor #255 - sta $addr""") - } - is PtIdentifier -> { - val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier) - asmgen.out(" eor #255") - asmgen.out(" sta ($sourceName),y") - } - else -> { - asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) - asmgen.out(""" - ldy #0 - lda (P8ZP_SCRATCH_W2),y - eor #255""") - asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2") - } - } - } - TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.A -> asmgen.out(" eor #255") - RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax") - RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay") - else -> throw AssemblyError("invalid reg dt for byte invert") - } - } - TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert") - TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign) - else -> throw AssemblyError("weird target") - } - } - DataType.UWORD -> { - when (target.kind) { - TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda ${target.asmVarname} - eor #255 - sta ${target.asmVarname} - lda ${target.asmVarname}+1 - eor #255 - sta ${target.asmVarname}+1""") - } - TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255") - RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255") - RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay") - in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers") - else -> throw AssemblyError("invalid reg dt for word invert") - } - } - TargetStorageKind.STACK -> TODO("no asm gen for word stack invert") - TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign) - else -> throw AssemblyError("weird target") - } - } - else -> throw AssemblyError("invert of invalid type") - } - } - - internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) { - val target = assign.target - val datatype = if(ignoreDatatype) { - when(target.datatype) { - DataType.UBYTE, DataType.BYTE -> DataType.BYTE - DataType.UWORD, DataType.WORD -> DataType.WORD - else -> target.datatype - } - } else target.datatype - when (datatype) { - DataType.BYTE -> { - when (target.kind) { - TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda #0 - sec - sbc ${target.asmVarname} - sta ${target.asmVarname}""") - } - TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.A -> { - if(asmgen.isTargetCpu(CpuType.CPU65c02)) - asmgen.out(" eor #255 | ina") - else - asmgen.out(" eor #255 | clc | adc #1") - - } - RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx") - RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny") - else -> throw AssemblyError("invalid reg dt for byte negate") - } - } - TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that") - TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate") - TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign) - else -> throw AssemblyError("weird target") - } - } - DataType.WORD -> { - when (target.kind) { - TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda #0 - sec - sbc ${target.asmVarname} - sta ${target.asmVarname} - lda #0 - sbc ${target.asmVarname}+1 - sta ${target.asmVarname}+1""") - } - TargetStorageKind.REGISTER -> { - when(target.register!!) { //P8ZP_SCRATCH_REG - RegisterOrPair.AX -> { - asmgen.out(""" - sec - eor #255 - adc #0 - pha - txa - eor #255 - adc #0 - tax - pla""") - } - RegisterOrPair.AY -> { - asmgen.out(""" - sec - eor #255 - adc #0 - pha - tya - eor #255 - adc #0 - tay - pla""") - } - RegisterOrPair.XY -> { - asmgen.out(""" - sec - txa - eor #255 - adc #0 - tax - tya - eor #255 - adc #0 - tay""") - } - in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers") - else -> throw AssemblyError("invalid reg dt for word neg") - } - } - TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that") - TargetStorageKind.STACK -> TODO("no asm gen for word stack negate") - TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign) - else -> throw AssemblyError("weird target") - } - } - DataType.FLOAT -> { - when (target.kind) { - TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP") - RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF") - else -> throw AssemblyError("invalid float register") - } - } - TargetStorageKind.VARIABLE -> { - // simply flip the sign bit in the float - asmgen.out(""" - lda ${target.asmVarname}+1 - eor #$80 - sta ${target.asmVarname}+1 - """) - } - TargetStorageKind.STACK -> TODO("no asm gen for float stack negate") - TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign) - else -> throw AssemblyError("weird target for in-place float negation") - } - } - else -> throw AssemblyError("negate of invalid type") - } - } - +} + +private fun AsmAssignSource.toAstExpression(scope: PtNamedNode): PtExpression { + return when(kind) { + SourceStorageKind.LITERALNUMBER -> this.number!! + SourceStorageKind.VARIABLE -> { + val ident = PtIdentifier(scope.scopedName + '.' + asmVarname, datatype, Position.DUMMY) + ident.parent = scope + ident + } + SourceStorageKind.ARRAY -> this.array!! + SourceStorageKind.MEMORY -> this.memory!! + SourceStorageKind.EXPRESSION -> this.expression!! + SourceStorageKind.REGISTER -> { + if(register in Cx16VirtualRegisters) { + val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = scope.position) + ident.parent = scope + ident + } else { + throw AssemblyError("no ast expr possible for source register $register") + } + } + else -> throw AssemblyError("invalid assign source kind $kind") + } +} + +private fun AsmAssignTarget.toAstExpression(): PtExpression { + return when(kind) { + TargetStorageKind.VARIABLE -> { + val ident = PtIdentifier((this.scope as PtNamedNode).scopedName + '.' + asmVarname, datatype, origAstTarget?.position ?: Position.DUMMY) + ident.parent = this.scope + ident + } + TargetStorageKind.ARRAY -> this.array!! + TargetStorageKind.MEMORY -> this.memory!! + TargetStorageKind.REGISTER -> { + if(register in Cx16VirtualRegisters) { + val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = this.origAssign.position) + ident.parent = (this.scope as? PtNamedNode) ?: this.origAstTarget!! + ident + } else { + throw AssemblyError("no ast expr possible for target register $register") + } + } + else -> throw AssemblyError("invalid assign target kind $kind") + } } diff --git a/codeGenCpu6502/test/Dummies.kt b/codeGenCpu6502/test/Dummies.kt new file mode 100644 index 000000000..132037f1a --- /dev/null +++ b/codeGenCpu6502/test/Dummies.kt @@ -0,0 +1,60 @@ +package prog8tests.codegencpu6502 + +import prog8.code.core.* + + +internal object DummyMemsizer : IMemSizer { + override fun memorySize(dt: DataType) = when(dt) { + in ByteDatatypes -> 1 + DataType.FLOAT -> 5 + else -> 2 + } + override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) { + DataType.ARRAY_UW -> numElements*2 + DataType.ARRAY_W -> numElements*2 + DataType.ARRAY_F -> numElements*5 + else -> numElements + } +} + +internal object DummyStringEncoder : IStringEncoding { + override fun encodeString(str: String, encoding: Encoding): List { + return emptyList() + } + + override fun decodeString(bytes: Iterable, encoding: Encoding): String { + return "" + } +} + +internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): + IErrorReporter { + + val errors = mutableListOf() + val warnings = mutableListOf() + + override fun err(msg: String, position: Position) { + errors.add("${position.toClickableStr()} $msg") + } + + override fun warn(msg: String, position: Position) { + warnings.add("${position.toClickableStr()} $msg") + } + + override fun noErrors(): Boolean = errors.isEmpty() + + override fun report() { + warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") } + errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") } + if(throwExceptionAtReportIfErrors) + finalizeNumErrors(errors.size, warnings.size) + if(!keepMessagesAfterReporting) { + clear() + } + } + + fun clear() { + errors.clear() + warnings.clear() + } +} diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index f2c447632..478d55074 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -1,10 +1,102 @@ package prog8tests.codegencpu6502 import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldNotBe +import prog8.code.SymbolTableMaker +import prog8.code.ast.* +import prog8.code.core.* +import prog8.code.target.C64Target +import prog8.codegen.cpu6502.AsmGen6502 class TestCodegen: FunSpec({ - // TODO there are no 6502 specific codegen tests yet + fun getTestOptions(): CompilationOptions { + val target = C64Target() + return CompilationOptions( + OutputType.RAW, + CbmPrgLauncherType.NONE, + ZeropageType.DONTUSE, + zpReserved = emptyList(), + floats = true, + noSysInit = false, + compTarget = target, + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + ) + } + test("augmented assign on arrays") { +//main { +// sub start() { +// ubyte[] particleX = [1,2,3] +// ubyte[] particleDX = [1,2,3] +// particleX[2] += particleDX[2] +// +// word @shared xx = 1 +// xx = -xx +// xx += 42 +// xx += cx16.r0 +// } +//} + val codegen = AsmGen6502() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) + sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) + sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) + sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) + + val assign = PtAugmentedAssign("+=", Position.DUMMY) + val target = PtAssignTarget(Position.DUMMY).also { + val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> + idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) + idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) + } + it.add(targetIdx) + } + val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY) + value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY)) + value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) + assign.add(target) + assign.add(value) + sub.add(assign) + + val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) + val prefixTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + prefixAssign.add(prefixTarget) + prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + sub.add(prefixAssign) + + val numberAssign = PtAugmentedAssign("-=", Position.DUMMY) + val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + numberAssign.add(numberAssignTarget) + numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY)) + sub.add(numberAssign) + + val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) + val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + cxregAssign.add(cxregAssignTarget) + cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY)) + sub.add(cxregAssign) + + block.add(sub) + program.add(block) + + // define the "cx16.r0" virtual register + val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) + program.add(cx16block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + codegen.generate(program, st, options, errors) shouldNotBe null + } }) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index e5b2b0428..06bfe373b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -3,6 +3,7 @@ package prog8.codegen.intermediate import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.DataType +import prog8.code.core.PrefixOperators import prog8.code.core.SignedDatatypes import prog8.intermediate.* @@ -12,22 +13,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(assignment.target.children.single() is PtMachineRegister) throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") - return if (assignment.isInplaceAssign) - translateInplaceAssign(assignment) - else - translateRegularAssign(assignment) + return translateRegularAssign(assignment) } - private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks { + internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks { + if(augmentedAssign.target.children.single() is PtMachineRegister) + throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") + + return translateInplaceAssign(augmentedAssign) + } + + private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks { val ident = assignment.target.identifier val memory = assignment.target.memory val array = assignment.target.array return if(ident!=null) { - assignSelfInMemory(ident.name, assignment.value, assignment) + assignVarAugmented(ident.name, assignment) } else if(memory != null) { if(memory.address is PtNumber) - assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment) + assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment) else fallbackAssign(assignment) } else if(array!=null) { @@ -40,120 +45,83 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } } - private fun assignSelfInMemoryKnownAddress( + private fun assignMemoryAugmented( address: Int, - value: PtExpression, - origAssign: PtAssignment + assignment: PtAugmentedAssign ): IRCodeChunks { + val value = assignment.value val vmDt = codeGen.irType(value.type) - when(value) { - is PtIdentifier -> return emptyList() // do nothing, x=x null assignment. - is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment - is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null) - is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign) - is PtMemoryByte -> { - return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt())) - emptyList() // do nothing, mem=mem null assignment. - else { - // read and write a (i/o) memory location to itself. - val tempReg = codeGen.registers.nextFree() - val code = IRCodeChunk(null, null) - code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address) - code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address) - listOf(code) - } - } - else -> return fallbackAssign(origAssign) + return when(assignment.operator) { + "+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value) + "-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value) + "*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value) + "/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value) + "|" -> expressionEval.operatorOrInplace(address, null, vmDt, value) + "&" -> expressionEval.operatorAndInplace(address, null, vmDt, value) + "^" -> expressionEval.operatorXorInplace(address, null, vmDt, value) + "<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value) + ">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value) + in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null) + else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign? } } - private fun assignSelfInMemory( - symbol: String, - value: PtExpression, - origAssign: PtAssignment - ): IRCodeChunks { - val vmDt = codeGen.irType(value.type) - return when(value) { - is PtIdentifier -> emptyList() // do nothing, x=x null assignment. - is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment - is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol) - is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign) - is PtMemoryByte -> { - val code = IRCodeChunk(null, null) - val tempReg = codeGen.registers.nextFree() - code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol) - code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol) - listOf(code) - } - - else -> fallbackAssign(origAssign) + private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks { + val value = assignment.value + val valueVmDt = codeGen.irType(value.type) + return when (assignment.operator) { + "+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value) + "-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value) + "*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value) + "/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value) + "|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value) + "&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value) + "^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value) + "<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value) + ">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value) + in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol) + else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign? } } - private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks { + private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks { if (codeGen.options.slowCodegenWarnings) codeGen.errors.warn("indirect code for in-place assignment", origAssign.position) - return translateRegularAssign(origAssign) - } - - private fun inplaceBinexpr( - operator: String, - operand: PtExpression, - vmDt: IRDataType, - signed: Boolean, - knownAddress: Int?, - symbol: String?, - origAssign: PtAssignment - ): IRCodeChunks { - if(knownAddress!=null) { - when (operator) { - "+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand) - "-" -> return expressionEval.operatorMinusInplace(knownAddress, null, vmDt, operand) - "*" -> return expressionEval.operatorMultiplyInplace(knownAddress, null, vmDt, operand) - "/" -> return expressionEval.operatorDivideInplace(knownAddress, null, vmDt, signed, operand) - "|" -> return expressionEval.operatorOrInplace(knownAddress, null, vmDt, operand) - "&" -> return expressionEval.operatorAndInplace(knownAddress, null, vmDt, operand) - "^" -> return expressionEval.operatorXorInplace(knownAddress, null, vmDt, operand) - "<<" -> return expressionEval.operatorShiftLeftInplace(knownAddress, null, vmDt, operand) - ">>" -> return expressionEval.operatorShiftRightInplace(knownAddress, null, vmDt, signed, operand) - else -> {} - } + val normalAssign = PtAssignment(origAssign.position) + normalAssign.add(origAssign.target) + val value: PtExpression + if(origAssign.operator in PrefixOperators) { + value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position) + value.add(origAssign.value) } else { - symbol!! - when (operator) { - "+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand) - "-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand) - "*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand) - "/" -> return expressionEval.operatorDivideInplace(null, symbol, vmDt, signed, operand) - "|" -> return expressionEval.operatorOrInplace(null, symbol, vmDt, operand) - "&" -> return expressionEval.operatorAndInplace(null, symbol, vmDt, operand) - "^" -> return expressionEval.operatorXorInplace(null, symbol, vmDt, operand) - "<<" -> return expressionEval.operatorShiftLeftInplace(null, symbol, vmDt, operand) - ">>" -> return expressionEval.operatorShiftRightInplace(null, symbol, vmDt, signed, operand) - else -> {} - } + require(origAssign.operator.endsWith('=')) + value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position) + val left: PtExpression = origAssign.target.children.single() as PtExpression + value.add(left) + value.add(origAssign.value) } - return fallbackAssign(origAssign) + normalAssign.add(value) + return translateRegularAssign(normalAssign) } - private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?): IRCodeChunks { + private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks { val code= IRCodeChunk(null, null) when(operator) { "+" -> { } "-" -> { - code += if(knownAddress!=null) - IRInstruction(Opcode.NEGM, vmDt, value = knownAddress) + code += if(address!=null) + IRInstruction(Opcode.NEGM, vmDt, value = address) else - IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol) + IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol) } "~" -> { val regMask = codeGen.registers.nextFree() val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask) - code += if(knownAddress!=null) - IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress) + code += if(address!=null) + IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address) else - IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol) + IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol) } else -> throw AssemblyError("weird prefix operator") } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 31a0bed03..ae0cb3605 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -238,6 +238,7 @@ class IRCodeGen( is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table is PtConstant -> emptyList() // constants have all been folded into the code is PtAssignment -> assignmentGen.translate(node) + is PtAugmentedAssign -> assignmentGen.translate(node) is PtNodeGroup -> translateGroup(node.children) is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0) is PtFunctionCall -> expressionEval.translate(node, 0, 0) @@ -1178,7 +1179,7 @@ class IRCodeGen( for (child in block.children) { when(child) { is PtNop -> { /* nothing */ } - is PtAssignment -> { /* global variable initialization is done elsewhere */ } + is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ } is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ } is PtSub -> { val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position) diff --git a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt index 0576b15f6..53d5b81d1 100644 --- a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt @@ -24,7 +24,7 @@ class VmCodeGen: ICodeGeneratorBackend { } -internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram { +internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram { override fun assemble(options: CompilationOptions): Boolean { // the VM reads the IR file from disk. diff --git a/codeGenIntermediate/test/Dummies.kt b/codeGenIntermediate/test/Dummies.kt index bfa03f50b..1bd5157f2 100644 --- a/codeGenIntermediate/test/Dummies.kt +++ b/codeGenIntermediate/test/Dummies.kt @@ -1,12 +1,18 @@ -import prog8.code.core.DataType -import prog8.code.core.Encoding -import prog8.code.core.IMemSizer -import prog8.code.core.IStringEncoding +import prog8.code.core.* internal object DummyMemsizer : IMemSizer { - override fun memorySize(dt: DataType) = 0 - override fun memorySize(arrayDt: DataType, numElements: Int) = 0 + override fun memorySize(dt: DataType) = when(dt) { + in ByteDatatypes -> 1 + DataType.FLOAT -> 5 + else -> 2 + } + override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) { + DataType.ARRAY_UW -> numElements*2 + DataType.ARRAY_W -> numElements*2 + DataType.ARRAY_F -> numElements*5 + else -> numElements + } } internal object DummyStringEncoder : IStringEncoding { @@ -18,3 +24,35 @@ internal object DummyStringEncoder : IStringEncoding { return "" } } + +internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): + IErrorReporter { + + val errors = mutableListOf() + val warnings = mutableListOf() + + override fun err(msg: String, position: Position) { + errors.add("${position.toClickableStr()} $msg") + } + + override fun warn(msg: String, position: Position) { + warnings.add("${position.toClickableStr()} $msg") + } + + override fun noErrors(): Boolean = errors.isEmpty() + + override fun report() { + warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") } + errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") } + if(throwExceptionAtReportIfErrors) + finalizeNumErrors(errors.size, warnings.size) + if(!keepMessagesAfterReporting) { + clear() + } + } + + fun clear() { + errors.clear() + warnings.clear() + } +} diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt new file mode 100644 index 000000000..9206e8b43 --- /dev/null +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -0,0 +1,103 @@ +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.ints.shouldBeGreaterThan +import prog8.code.SymbolTableMaker +import prog8.code.ast.* +import prog8.code.core.* +import prog8.code.target.VMTarget +import prog8.codegen.vm.VmAssemblyProgram +import prog8.codegen.vm.VmCodeGen +import prog8.intermediate.IRSubroutine + +class TestVmCodeGen: FunSpec({ + + fun getTestOptions(): CompilationOptions { + val target = VMTarget() + return CompilationOptions( + OutputType.RAW, + CbmPrgLauncherType.NONE, + ZeropageType.DONTUSE, + zpReserved = emptyList(), + floats = true, + noSysInit = false, + compTarget = target, + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + ) + } + + test("augmented assigns") { +//main { +// sub start() { +// ubyte[] particleX = [1,2,3] +// ubyte[] particleDX = [1,2,3] +// particleX[2] += particleDX[2] +// +// word @shared xx = 1 +// xx = -xx +// xx += 42 +// xx += cx16.r0 +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) + sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) + sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) + sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) + + val assign = PtAugmentedAssign("+=", Position.DUMMY) + val target = PtAssignTarget(Position.DUMMY).also { + val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> + idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) + idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) + } + it.add(targetIdx) + } + val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY) + value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY)) + value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) + assign.add(target) + assign.add(value) + sub.add(assign) + + val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) + val prefixTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + prefixAssign.add(prefixTarget) + prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + sub.add(prefixAssign) + + val numberAssign = PtAugmentedAssign("+=", Position.DUMMY) + val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + numberAssign.add(numberAssignTarget) + numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY)) + sub.add(numberAssign) + + val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) + val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { + it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) + } + cxregAssign.add(cxregAssignTarget) + cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY)) + sub.add(cxregAssign) + + block.add(sub) + program.add(block) + + // define the "cx16.r0" virtual register + val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) + program.add(cx16block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } +}) \ No newline at end of file diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index f21634278..b75cee4b6 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -6,9 +6,11 @@ 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 diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index d610667a1..15775be81 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -90,6 +90,54 @@ class IntermediateAstMaker(private val program: Program, private val options: Co } private fun transform(srcAssign: Assignment): PtNode { + if(srcAssign.isAugmentable) { + val srcExpr = srcAssign.value + val (operator: String, augmentedValue: Expression?) = when(srcExpr) { + is BinaryExpression -> { + if(srcExpr.operator=="==" || srcExpr.operator=="%") { + // no special code possible for 'in-place comparison and result as boolean' or 'remainder' + Pair("", null) + } + else if(srcExpr.left isSameAs srcAssign.target) { + Pair(srcExpr.operator+'=', srcExpr.right) + } else if(srcExpr.right isSameAs srcAssign.target) { + Pair(srcExpr.operator+'=', srcExpr.left) + } else { + // either left or right is same as target, other combinations are not supported here + Pair("", null) + } + } + is PrefixExpression -> { + require(srcExpr.expression isSameAs srcAssign.target) + Pair(srcExpr.operator, srcExpr.expression) + } + is TypecastExpression -> { + // At this time, there are no special optimized instructions to do an in-place type conversion. + // so we simply revert to a regular type converting assignment. + // Also, an in-place type cast is very uncommon so probably not worth optimizing anyway. + Pair("", null) + // the following is what *could* be used here if such instructions *were* available: +// if(srcExpr.expression isSameAs srcAssign.target) +// Pair("cast", srcExpr.expression) +// else { +// val subTypeCast = srcExpr.expression as? TypecastExpression +// val targetDt = srcAssign.target.inferType(program).getOrElse { DataType.UNDEFINED } +// if (subTypeCast!=null && srcExpr.type==targetDt && subTypeCast.expression isSameAs srcAssign.target) { +// Pair("cast", subTypeCast) +// } else +// Pair("", null) +// } + } + else -> Pair("", null) + } + if(augmentedValue!=null) { + val assign = PtAugmentedAssign(operator, srcAssign.position) + assign.add(transform(srcAssign.target)) + assign.add(transformExpression(augmentedValue)) + return assign + } + } + val assign = PtAssignment(srcAssign.position) assign.add(transform(srcAssign.target)) assign.add(transformExpression(srcAssign.value)) diff --git a/compiler/test/helpers/Dummies.kt b/compiler/test/helpers/Dummies.kt index 0915d98b6..e1ce7d4d2 100644 --- a/compiler/test/helpers/Dummies.kt +++ b/compiler/test/helpers/Dummies.kt @@ -21,8 +21,17 @@ internal object DummyFunctions : IBuiltinFunctions { } internal object DummyMemsizer : IMemSizer { - override fun memorySize(dt: DataType) = 0 - override fun memorySize(arrayDt: DataType, numElements: Int) = 0 + override fun memorySize(dt: DataType) = when(dt) { + in ByteDatatypes -> 1 + DataType.FLOAT -> 5 + else -> 2 + } + override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) { + DataType.ARRAY_UW -> numElements*2 + DataType.ARRAY_W -> numElements*2 + DataType.ARRAY_F -> numElements*5 + else -> numElements + } } internal object DummyStringEncoder : IStringEncoding { diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index 8c06d17ec..f27373bd8 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -1,7 +1,6 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe -import io.kotest.matchers.types.instanceOf import prog8.code.StStaticVariable import prog8.code.core.CbmPrgLauncherType import prog8.code.core.CompilationOptions