diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index c80f8ef4f..8ffee348c 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -36,7 +36,7 @@ init_system .proc .pend -read_byte_from_address .proc +read_byte_from_address_on_stack .proc ; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged) lda c64.ESTACK_LO+1,x ldy c64.ESTACK_HI+1,x @@ -2079,7 +2079,7 @@ ror2_array_uw .proc + rts .pend - + strcpy .proc ; copy a string (0-terminated) from A/Y to (ZPWORD1) ; it is assumed the target string is large enough. @@ -2092,8 +2092,8 @@ strcpy .proc bne - rts .pend - - + + func_leftstr .proc ; leftstr(source, target, length) with params on stack inx @@ -2175,12 +2175,12 @@ func_substr .proc lda c64.ESTACK_LO,x ; start sta c64.SCRATCH_ZPB1 inx - lda c64.ESTACK_LO,x + lda c64.ESTACK_LO,x sta c64.SCRATCH_ZPWORD2 lda c64.ESTACK_HI,x sta c64.SCRATCH_ZPWORD2+1 inx - lda c64.ESTACK_LO,x + lda c64.ESTACK_LO,x sta c64.SCRATCH_ZPWORD1 lda c64.ESTACK_HI,x sta c64.SCRATCH_ZPWORD1+1 diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 4e3a5a393..6fc170ec0 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -328,6 +328,10 @@ open class Assignment(var target: AssignTarget, var value: Expression, override return("Assignment(target: $target, value: $value, pos=$position)") } + /** + * Is the assigment value an expression that references the assignment target itself? + * The expression can be a BinaryExpression, PrefixExpression or TypecastExpression (possibly with one sub-cast). + */ val isAugmentable: Boolean get() { val binExpr = value as? BinaryExpression @@ -354,6 +358,16 @@ open class Assignment(var target: AssignTarget, var value: Expression, override } } + val prefixExpr = value as? PrefixExpression + if(prefixExpr!=null) + return prefixExpr.expression isSameAs target + + val castExpr = value as? TypecastExpression + if(castExpr!=null) { + val subCast = castExpr.expression as? TypecastExpression + return if(subCast!=null) subCast.expression isSameAs target else castExpr.expression isSameAs target + } + return false } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index c22838ecb..8f39fbbad 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -1008,7 +1008,6 @@ $counterVar .byte 0""") val indexName = asmIdentifierName(index) out(" lda $indexName") } - // TODO optimize more cases else -> { expressionsAsmGen.translateExpression(index) out(" inx | lda $ESTACK_LO_HEX,x") diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 2bb5517b8..80e08b09a 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -14,14 +14,13 @@ import prog8.compiler.toHex internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) { + private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen) + internal fun translate(assign: Assignment) { when { assign.value is NumericLiteralValue -> translateConstantValueAssignment(assign) assign.value is IdentifierReference -> translateVariableAssignment(assign) - assign.isAugmentable -> { - println("TODO: optimize augmentable assignment ${assign.position}") // TODO - translateOtherAssignment(assign) // TODO generate better code here for augmentable assignments - } + assign.isAugmentable -> augmentableAsmGen.translate(assign) else -> translateOtherAssignment(assign) } } @@ -56,7 +55,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun translateOtherAssignment(assign: Assignment) { + internal fun translateOtherAssignment(assign: Assignment) { when (assign.value) { is AddressOf -> { val identifier = (assign.value as AddressOf).identifier @@ -74,23 +73,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } else -> { asmgen.translateExpression(read.addressExpression) - asmgen.out(" jsr prog8_lib.read_byte_from_address | inx") + asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx") assignFromRegister(assign.target, CpuRegister.A) } } } is PrefixExpression -> { - // TODO optimize common cases asmgen.translateExpression(assign.value as PrefixExpression) assignFromEvalResult(assign.target) } is BinaryExpression -> { - // TODO optimize common cases asmgen.translateExpression(assign.value as BinaryExpression) assignFromEvalResult(assign.target) } is ArrayIndexedExpression -> { - // TODO optimize common cases val arrayExpr = assign.value as ArrayIndexedExpression val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT) val index = arrayExpr.arrayspec.index @@ -141,6 +137,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen private fun assignFromEvalResult(target: AssignTarget) { val targetIdent = target.identifier + val targetArrayIdx = target.arrayindexed + val targetMemory = target.memoryAddress when { targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) @@ -167,14 +165,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen else -> throw AssemblyError("weird target variable type $targetDt") } } - target.memoryAddress != null -> { + targetMemory != null -> { asmgen.out(" inx") - storeByteViaRegisterAInMemoryAddress("$ESTACK_LO_HEX,x", target.memoryAddress) + storeByteViaRegisterAInMemoryAddress("$ESTACK_LO_HEX,x", targetMemory) } - target.arrayindexed != null -> { - val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT) - val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier) - asmgen.translateExpression(target.arrayindexed!!.arrayspec.index) + targetArrayIdx != null -> { + val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT) + val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier) + asmgen.translateExpression(targetArrayIdx.arrayspec.index) asmgen.out(" inx | lda $ESTACK_LO_HEX,x") popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName) } @@ -285,6 +283,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val sourceName = asmgen.asmIdentifierName(variable) val targetIdent = target.identifier val targetArrayIdx = target.arrayindexed + val targetMemory = target.memoryAddress when { targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) @@ -293,6 +292,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen sta $targetName """) } + targetMemory != null -> { + storeByteViaRegisterAInMemoryAddress(sourceName, targetMemory) + } targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier) @@ -302,9 +304,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.out(" inx | lda $ESTACK_LO_HEX,x") popAndWriteArrayvalueWithIndexA(arrayDt, targetName) } - target.memoryAddress != null -> { - storeByteViaRegisterAInMemoryAddress(sourceName, target.memoryAddress) - } else -> throw AssemblyError("no asm gen for assign bytevar to $target") } } @@ -374,11 +373,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen when { addressLv != null -> asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}") addressExpr is IdentifierReference -> { - val targetName = asmgen.asmIdentifierName(addressExpr) + val pointerVarName = asmgen.asmIdentifierName(addressExpr) asmgen.out(""" - lda $targetName + lda $pointerVarName sta ${C64Zeropage.SCRATCH_W1} - lda $targetName+1 + lda $pointerVarName+1 sta ${C64Zeropage.SCRATCH_W1+1} lda $ldaInstructionArg ldy #0 @@ -465,7 +464,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier) - // TODO optimize common cases asmgen.translateExpression(index) asmgen.out(""" inx @@ -486,7 +484,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val targetIdent = target.identifier val targetArrayIdx = target.arrayindexed val targetMemory = target.memoryAddress - // TODO all via method? when { targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) @@ -498,7 +495,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier) - // TODO optimize common cases asmgen.translateExpression(index) asmgen.out(""" inx diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt new file mode 100644 index 000000000..ad0aef0e7 --- /dev/null +++ b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt @@ -0,0 +1,34 @@ +package prog8.compiler.target.c64.codegen + +import prog8.ast.Program +import prog8.ast.expressions.BinaryExpression +import prog8.ast.expressions.PrefixExpression +import prog8.ast.expressions.TypecastExpression +import prog8.ast.statements.Assignment +import prog8.compiler.AssemblyError + +internal class AugmentableAssignmentAsmGen(private val program: Program, + private val assignmentAsmGen: AssignmentAsmGen, + private val asmgen: AsmGen) { + fun translate(assign: Assignment) { + require(assign.isAugmentable) + assignmentAsmGen.translateOtherAssignment(assign) // TODO get rid of this fallback + +// when (assign.value) { +// is PrefixExpression -> { +// TODO("aug prefix") +// } +// is TypecastExpression -> { +// TODO("aug typecast") +// } +// is BinaryExpression -> { +// TODO("aug binary") +// } +// else -> { +// throw AssemblyError("invalid aug assign value type") +// } +// } + + } + +} diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index b8a2308c5..72929a481 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -150,7 +150,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } else -> { translateExpression(expr.addressExpression) - asmgen.out(" jsr prog8_lib.read_byte_from_address") + asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack") asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") } } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index c59aae4b0..af024124b 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -412,8 +412,8 @@ private fun numericLiteral(value: Number, position: Position): NumericLiteralVal floatNum return when(tweakedValue) { - is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position) - is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position) + is Int -> NumericLiteralValue.optimalInteger(value.toInt(), position) + is Short -> NumericLiteralValue.optimalInteger(value.toInt(), position) is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position) is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position) is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position) diff --git a/compiler/src/prog8/optimizer/ConstExprEvaluator.kt b/compiler/src/prog8/optimizer/ConstExprEvaluator.kt index 42de42e13..42c980259 100644 --- a/compiler/src/prog8/optimizer/ConstExprEvaluator.kt +++ b/compiler/src/prog8/optimizer/ConstExprEvaluator.kt @@ -163,7 +163,7 @@ class ConstExprEvaluator { val error = "cannot add $left and $right" return when (left.type) { in IntegerDatatypes -> when (right.type) { - in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position) + in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position) DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position) else -> throw ExpressionError(error, left.position) } @@ -180,7 +180,7 @@ class ConstExprEvaluator { val error = "cannot subtract $left and $right" return when (left.type) { in IntegerDatatypes -> when (right.type) { - in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position) + in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position) DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position) else -> throw ExpressionError(error, left.position) } @@ -197,7 +197,7 @@ class ConstExprEvaluator { val error = "cannot multiply ${left.type} and ${right.type}" return when (left.type) { in IntegerDatatypes -> when (right.type) { - in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position) + in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position) DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position) else -> throw ExpressionError(error, left.position) } @@ -220,7 +220,7 @@ class ConstExprEvaluator { in IntegerDatatypes -> { if(right.number.toInt()==0) divideByZeroError(right.position) val result: Int = left.number.toInt() / right.number.toInt() - NumericLiteralValue.optimalNumeric(result, left.position) + NumericLiteralValue.optimalInteger(result, left.position) } DataType.FLOAT -> { if(right.number.toDouble()==0.0) divideByZeroError(right.position) diff --git a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 0b9b9fab6..cebb0aed9 100644 --- a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -203,7 +203,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke "-" -> when (subexpr.type) { in IntegerDatatypes -> { listOf(IAstModification.ReplaceNode(expr, - NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position), + NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position), parent)) } DataType.FLOAT -> { @@ -216,7 +216,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke "~" -> when (subexpr.type) { in IntegerDatatypes -> { listOf(IAstModification.ReplaceNode(expr, - NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position), + NumericLiteralValue.optimalInteger(subexpr.number.toInt().inv(), subexpr.position), parent)) } else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position) diff --git a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt index 26c88264b..cf8739e69 100644 --- a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt @@ -34,15 +34,25 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast) } - // remove redundant nested typecasts: - // if the typecast casts a value to the same type, remove the cast. - // if the typecast contains another typecast, remove the inner typecast. + // remove redundant nested typecasts val subTypecast = typecast.expression as? TypecastExpression if (subTypecast != null) { - mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast) + // remove the sub-typecast if its datatype is larger than the outer typecast + if(subTypecast.type largerThan typecast.type) { + mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast) + } else { + if(subTypecast.type == DataType.UBYTE && typecast.type == DataType.UWORD) { + // (X as ubyte) as uword -> X & 255 + // TODO don't optimize this because the asm code generated by it is worse... +// val and255 = BinaryExpression(subTypecast.expression, "&", NumericLiteralValue.optimalInteger(255, subTypecast.position), subTypecast.position) +// mods += IAstModification.ReplaceNode(typecast, and255, parent) + } + } } else { - if (typecast.expression.inferType(program).istype(typecast.type)) + if (typecast.expression.inferType(program).istype(typecast.type)) { + // remove duplicate cast mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent) + } } return mods diff --git a/examples/test.p8 b/examples/test.p8 index 7503dc39a..a66f8f7cb 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,12 +7,18 @@ main { sub start() { - ubyte A=5 - uword clr = $d020 - A = @(clr) - A++ - @(clr) = A + byte A + byte B = +A + byte C = -A + uword W = 43210 + A = -A + + c64scr.print_uw(W) + c64.CHROUT('\n') + + W = W as ubyte ; TODO cast(W as ubyte) as uword -> W and 255 + c64scr.print_uw(W) + c64.CHROUT('\n') -; uword xx = @(clr+1) } }