diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index c7cc2bc39..b35c04839 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -109,12 +109,12 @@ class AstChecker(private val namespace: INameScope, } else { // @todo this is a little hack to make the assembler happy; // certain assembler routines are -for now- always included and *require* an irq.irq routine to be present - val pos = module.statements.last().position + val pos = module.statements.first().position val dummyIrqBlock = Block("irq", address = null, statements = mutableListOf( Subroutine("irq", listOf(), listOf(), listOf(), listOf(), setOf(), null, true,mutableListOf(), pos) ), position = pos) dummyIrqBlock.linkParents(module) - module.statements.add(dummyIrqBlock) + module.statements.add(0, dummyIrqBlock) } } diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index dfe4a90b2..2c0e30402 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -236,12 +236,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, private fun block2asm(blk: IntermediateProgram.ProgramBlock) { block = blk out("\n; ---- block: '${block.shortname}' ----") + if(!blk.force_output) + out("${block.shortname}\t.proc\n") if(block.address!=null) { out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'") out("* = ${block.address?.toHex()}") } - if(!blk.force_output) - out("${block.shortname}\t.proc\n") out("\n; memdefs and kernel subroutines") memdefs2asm(block) out("\n; variables") @@ -872,6 +872,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, sta ${(ESTACK_LO + 1).toHex()},x """ } + Opcode.XOR_BYTE -> { + """ + lda ${(ESTACK_LO + 2).toHex()},x + eor ${(ESTACK_LO + 1).toHex()},x + inx + sta ${(ESTACK_LO + 1).toHex()},x + """ + } Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub" Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw" diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index a42468d48..e3c255277 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -11,6 +11,8 @@ import kotlin.math.log2 X*Y - X -> X*(Y-1) ??? Y*X - X -> X*(Y-1) ??? + + value X --> X value <<<<< should be done already @@ -32,6 +34,43 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H // +X --> X optimizationsDone++ return expr.expression.process(this) + } else if (expr.operator == "not") { + (expr.expression as? BinaryExpression)?.let { + // NOT (...) -> invert ... + when(it.operator) { + "<" -> { + it.operator = ">=" + optimizationsDone++ + return it + } + ">" -> { + it.operator = "<=" + optimizationsDone++ + return it + } + "<=" -> { + it.operator = ">" + optimizationsDone++ + return it + } + ">=" -> { + it.operator = "<" + optimizationsDone++ + return it + } + "==" -> { + it.operator = "!=" + optimizationsDone++ + return it + } + "!=" -> { + it.operator = "==" + optimizationsDone++ + return it + } + else -> {} + } + } } return super.process(expr) } diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index dd526b43d..7bc240417 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -4,25 +4,23 @@ import prog8.ast.* import prog8.compiler.HeapValues import prog8.compiler.target.c64.Petscii import prog8.functions.BuiltinFunctions +import sun.font.TrueTypeFont import kotlin.math.floor /* - todo remove empty blocks? already done? - todo remove empty subs? already done? - todo remove unused blocks todo remove unused variables todo remove unused subroutines todo remove unused strings and arrays from the heap - todo remove if/while/repeat/for statements with empty statement blocks - todo replace if statements with only else block - todo regular subroutines that have 1 or 2 (u)byte or 1 (u)word parameters -> change to asmsub to accept these in A/Y registers instead of on stack - todo optimize integer addition with self into shift 1 (A+=A -> A<<=1) todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + + todo regular subroutines that have 1 or 2 (u)byte or 1 (u)word parameters -> change to asmsub to accept these in A/Y registers instead of on stack + todo merge sequence of assignments into one to avoid repeated value loads (as long as the value is a constant and the target not a MEMORY type!) - todo report more always true/always false conditions - todo (optionally?) inline subroutines that are "sufficiently small" (=VERY small, say 0-3 statements, otherwise code size will explode and short branches will suffer) + + todo inline subroutines that are called exactly once (regardless of their size) + todo inline subroutines that are only called a few times (3?) and that are "sufficiently small" (0-3 statements) */ class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor { @@ -32,6 +30,33 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He private set private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure } + override fun process(block: Block): IStatement { + if(block.statements.isEmpty()) { + // remove empty block + optimizationsDone++ + statementsToRemove.add(block) + } + return super.process(block) + } + + override fun process(subroutine: Subroutine): IStatement { + if(subroutine.asmAddress==null) { + if(subroutine.statements.isEmpty()) { + // remove empty subroutine + optimizationsDone++ + statementsToRemove.add(subroutine) + } else if(subroutine.statements.size==1) { + val stmt = subroutine.statements[0] + if(stmt is ReturnFromIrq || stmt is Return) { + // remove empty subroutine + optimizationsDone++ + statementsToRemove.add(subroutine) + } + } + } + return super.process(subroutine) + } + override fun process(functionCall: FunctionCallStatement): IStatement { if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) { val functionName = functionCall.target.nameInSource[0] @@ -101,6 +126,22 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He override fun process(ifStatement: IfStatement): IStatement { super.process(ifStatement) + + if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty()) { + statementsToRemove.add(ifStatement) + optimizationsDone++ + return ifStatement + } + + if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) { + // invert the condition and move else part to true part + ifStatement.truepart = ifStatement.elsepart + ifStatement.elsepart = AnonymousScope(mutableListOf(), ifStatement.elsepart.position) + ifStatement.condition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position) + optimizationsDone++ + return ifStatement + } + val constvalue = ifStatement.condition.constValue(namespace, heap) if(constvalue!=null) { return if(constvalue.asBooleanValue){ @@ -120,6 +161,22 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He override fun process(forLoop: ForLoop): IStatement { super.process(forLoop) + if(forLoop.body.isEmpty()) { + // remove empty for loop + statementsToRemove.add(forLoop) + optimizationsDone++ + return forLoop + } else if(forLoop.body.statements.size==1) { + val loopvar = forLoop.body.statements[0] as? VarDecl + if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) { + // remove empty for loop + statementsToRemove.add(forLoop) + optimizationsDone++ + return forLoop + } + } + + val range = forLoop.iterable as? RangeExpr if(range!=null) { if(range.size(heap)==1) { @@ -136,6 +193,12 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He override fun process(whileLoop: WhileLoop): IStatement { super.process(whileLoop) + if(whileLoop.body.isEmpty()) { + statementsToRemove.add(whileLoop) + optimizationsDone++ + return whileLoop + } + val constvalue = whileLoop.condition.constValue(namespace, heap) if(constvalue!=null) { return if(constvalue.asBooleanValue){ @@ -160,6 +223,11 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He override fun process(repeatLoop: RepeatLoop): IStatement { super.process(repeatLoop) + if(repeatLoop.body.isEmpty()) { + statementsToRemove.add(repeatLoop) + optimizationsDone++ + return repeatLoop + } val constvalue = repeatLoop.untilCondition.constValue(namespace, heap) if(constvalue!=null) { return if(constvalue.asBooleanValue){ @@ -209,9 +277,17 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He val bexpr=assignment.value as? BinaryExpression if(bexpr!=null) { val cv = bexpr.right.constValue(namespace, heap)?.asNumericValue?.toDouble() - if(cv!=null) { + if(cv==null) { + if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) { + if (same(bexpr.left, bexpr.right) && same(target, bexpr.left)) { + bexpr.operator = "*" + bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position) + optimizationsDone++ + return assignment + } + } + } else { if (same(target, bexpr.left)) { - // remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc // A = A B val vardeclDt = (target.identifier?.targetStatement(namespace) as? VarDecl)?.type @@ -320,6 +396,21 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He return super.process(assignment) } + + private fun same(left: IExpression, right: IExpression): Boolean { + if(left===right) + return true + when(left) { + is RegisterExpr -> + return (right is RegisterExpr && right.register==left.register) + is IdentifierReference -> + return (right is IdentifierReference && right.nameInSource==left.nameInSource) + is ArrayIndexedExpression -> + return (right is ArrayIndexedExpression && right.identifier==left.identifier && right.arrayspec==left.arrayspec) + } + return false + } + private fun same(target: AssignTarget, value: IExpression): Boolean { return when { target.memoryAddress!=null -> false diff --git a/examples/test.p8 b/examples/test.p8 index 6fcffbcbf..4dfb483c2 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,13 +1,25 @@ %import c64utils +%import c64flt ~ main { sub start() { - c64scr.print("hoi") - c64scr.print("ho") ; @todo 2x CHROUT - c64scr.print("h") ; @todo 1x CHROUT - c64scr.print("h") ; @todo 1x CHROUT - c64scr.print("\n") ; @todo 1x CHROUT + ubyte i=101 + byte b = 40 + uword j=100 + word w = 4000 + float f = 99.9 + + A+=A + Y+=Y + i+=i + j+=j + b+=b + w+=w + f+=f + ; X+=X + } } +