diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt index 1f720ae79..c51f332b9 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -235,7 +235,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } else -> { - // TODO OTHER EVALUATION HERE, don't use the estack to transfer the address to read/write from + // TODO use some other evaluation here; don't use the estack to transfer the address to read/write from asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, memory.definingSubroutine)) asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") when { @@ -2081,7 +2081,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, sta ${target.asmVarname}+1 """) } - TargetStorageKind.STACK -> TODO("no asm gen for stack float negate") + TargetStorageKind.STACK -> TODO("no asm gen for float stack negate") else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}") } } diff --git a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt index 6f32aa964..8d3d21ea9 100644 --- a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt +++ b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt @@ -73,7 +73,6 @@ X = BinExpr X = LeftExpr // ) } - // TODO breaks imageviewer EHB palette if(binExpr.right.isSimple) { val firstAssign = Assignment(assignment.target.copy(), binExpr.left, binExpr.left.position) val targetExpr = assignment.target.toExpression() diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 76ef5d137..90162eec5 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -198,6 +198,22 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { val leftBinExpr = expr.left as? BinaryExpression val rightBinExpr = expr.right as? BinaryExpression if(expr.operator=="+" || expr.operator=="-") { + + if(leftBinExpr!=null && rightconst!=null) { + if(leftBinExpr.operator=="+") { + val c2 = leftBinExpr.right.constValue(program) + if(c2!=null) { + // (X + C2) +/- rightConst --> X + (C2 +/- rightConst) + // TODO SAME FOR (X - C1) +/- C2 --> X - (C1+C2) mind the operator flip? + // TODO SAME FOR (X * C1) * C2 --> X * (C1*C2) + // TODO SAME FOR (X / C1) / C2 --> X / (C1*C2) + val constants = BinaryExpression(c2, expr.operator, rightconst, c2.position) + val newExpr = BinaryExpression(leftBinExpr.left, "+", constants, expr.position) + return listOf(IAstModification.ReplaceNode(expr, newExpr, parent)) + } + } + } + if(leftBinExpr!=null && rightBinExpr!=null) { val c1 = leftBinExpr.right.constValue(program) val c2 = rightBinExpr.right.constValue(program) @@ -378,7 +394,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { { // NOTE: THIS IS ONLY VALID ON FLOATING POINT CONSTANTS - // todo: this implements only a small set of possible reorderings at this time + // todo: this implements only a small set of possible reorderings at this time, we could think of more if(expr.operator==subExpr.operator) { // both operators are the same. diff --git a/codeOptimizers/src/prog8/optimizer/Extensions.kt b/codeOptimizers/src/prog8/optimizer/Extensions.kt index 7a37b4eb1..b63c695a0 100644 --- a/codeOptimizers/src/prog8/optimizer/Extensions.kt +++ b/codeOptimizers/src/prog8/optimizer/Extensions.kt @@ -71,7 +71,7 @@ fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICom fun getTempVarName(dt: InferredTypes.InferredType): List { return when { - // TODO assume (hope) cx16.r9 isn't used for anything else... + // TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable... dt.istype(DataType.UBYTE) -> listOf("cx16", "r9L") dt.istype(DataType.BYTE) -> listOf("cx16", "r9sL") dt.istype(DataType.UWORD) -> listOf("cx16", "r9") diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 2666ec067..168c8f0f1 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -312,7 +312,8 @@ romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y) romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time() -; TODO specify the correct clobbers for all functions below, we now assume all 3 regs are clobbered + +; It's not documented what registers are clobbered, so we assume the worst for all following kernal routines...: ; high level graphics & fonts romsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 571c5a4c4..ab3de0a48 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -229,8 +229,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o // TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off, // in that case: do *not* split it off but just keep it as it is (otherwise code size increases) - // TODO: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - // it WILL result in larger code. + // TODO: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code. // TODO: this should be replaced by a general expression-evaluation optimization step. // the actual conditional expression in the statement should be no more than VARIABLE SIMPLE-EXPRESSION diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index c4de14e25..d0b086bbe 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -106,6 +106,57 @@ class TestOptimization: FunSpec({ constvalue.parent shouldBeSameInstanceAs pfx.parent } + test("const folding multiple scenarios +/-") { + val source = """ + main { + const ubyte boardHeightC = 20 + const ubyte boardOffsetC = 3 + + sub start() { + uword load_location = 12345 + cx16.r0 = load_location + 8000 + 1000 + 1000 + cx16.r1L = @(load_location + 8000 + 1000 + 1000) + cx16.r2 = 8000 + 1000 + 1000 + load_location + cx16.r3L = @(8000 + 1000 + 1000 + load_location) + cx16.r4 = load_location + boardOffsetC + boardHeightC - 1 + } + }""" + val result = compileText(C64Target, true, source, writeAssembly = false).assertSuccess() + // expected: +// uword load_location +// load_location = 12345 +// cx16.r0 = load_location +// cx16.r0 += 10000 +// cx16.r1L = @((load_location+10000)) +// cx16.r2 = load_location +// cx16.r2 += 10000 +// cx16.r3L = @((load_location+10000)) +// cx16.r4 = load_location +// cx16.r4 += 22 + val stmts = result.program.entrypoint.statements + stmts.size shouldBe 10 + + val addR0value = (stmts[3] as Assignment).value + val binexpr0 = addR0value as BinaryExpression + binexpr0.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY) + val valueR1L = (stmts[4] as Assignment).value + val addrExpr1 = ((valueR1L as DirectMemoryRead).addressExpression as BinaryExpression) + addrExpr1.left shouldBe IdentifierReference(listOf("load_location"), Position.DUMMY) + addrExpr1.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY) + + val addR2value = (stmts[6] as Assignment).value + var binexpr2 = addR2value as BinaryExpression + binexpr2.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY) + val valueR3L = (stmts[7] as Assignment).value + val addrExpr3 = ((valueR3L as DirectMemoryRead).addressExpression as BinaryExpression) + addrExpr3.left shouldBe IdentifierReference(listOf("load_location"), Position.DUMMY) + addrExpr3.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY) + + val addR4value = (stmts[9] as Assignment).value + val binexpr4 = addR4value as BinaryExpression + binexpr4.right shouldBe NumericLiteralValue(DataType.UWORD, 22.0, Position.DUMMY) + } + test("constantfolded and silently typecasted for initializervalues") { val sourcecode = """ main { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 697ef4568..2b70e34e5 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,8 @@ TODO For next compiler release (7.5) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +more const-foldings +check correctness of inplaceModification_byte_value_to_pointer() ... @@ -41,6 +43,8 @@ More code optimization ideas by rewriting while and until expressions into if+jump (just consider them syntactic sugar) but the result should not produce larger code ofcourse! - while-expression should now also get the simplifyConditionalExpression() treatment +- byte typed expressions should be evaluated in the accumulator where possible, without (temp)var + for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value - rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables' - this removes the need for the BinExprSplitter? (which is problematic and very limited now) - introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4 diff --git a/examples/test.p8 b/examples/test.p8 index 4ec656288..84cdf72c0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,10 +3,11 @@ main { sub start() { - str name = "irmen" - txt.print(name) - } - - sub func(uword[22] thing) { + ; TODO other variants of this const folding + uword load_location = $6000 + cx16.r0 = load_location + 8000 + 1000 + 1000 + cx16.r1L = @(load_location + 8000 + 1000 + 1000) + cx16.r2 = 8000 + 1000 + 1000 + load_location + cx16.r3L = @(8000 + 1000 + 1000 + load_location) } }