diff --git a/compiler/res/prog8lib/c64utils.p8 b/compiler/res/prog8lib/c64utils.p8 index ebcd9488d..d4c275e99 100644 --- a/compiler/res/prog8lib/c64utils.p8 +++ b/compiler/res/prog8lib/c64utils.p8 @@ -121,7 +121,7 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () { dey ; and repeat for next bit bne - cld ; back to binary - cli ; enable interrupts again + cli ; enable interrupts again @todo don't re-enable if it wasn't enabled before rts }} } diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index f25720203..ad1123124 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -72,6 +72,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap optimizeVariableCopying() optimizeMultipleSequentialLineInstrs() optimizeCallReturnIntoJump() + optimizeConditionalBranches() // todo: add more optimizations to stackvm code optimizeRemoveNops() // must be done as the last step @@ -79,6 +80,57 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap optimizeRemoveNops() // once more } + private fun optimizeConditionalBranches() { + // conditional branches that consume the value on the stack + // sometimes these are just constant values, so we can statically determine the branch + // or, they are preceded by a NOT instruction so we can simply remove that and flip the branch condition + val pushvalue = setOf(Opcode.PUSH_BYTE, Opcode.PUSH_WORD) + val notvalue = setOf(Opcode.NOT_BYTE, Opcode.NOT_WORD) + val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW) + for(blk in blocks) { + val instructionsToReplace = mutableMapOf() + blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach { + if (it[1].value.opcode in branchOpcodes) { + if (it[0].value.opcode in pushvalue) { + val value = it[0].value.arg!!.asBooleanValue + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + val replacement: Instruction = + if (value) { + when (it[1].value.opcode) { + Opcode.JNZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) + Opcode.JNZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) + else -> Instruction(Opcode.NOP) + } + } else { + when (it[1].value.opcode) { + Opcode.JZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) + Opcode.JZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) + else -> Instruction(Opcode.NOP) + } + } + instructionsToReplace[it[1].index] = replacement + } + else if (it[0].value.opcode in notvalue) { + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + val replacement: Instruction = + when (it[1].value.opcode) { + Opcode.JZ -> Instruction(Opcode.JNZ, callLabel = it[1].value.callLabel) + Opcode.JZW -> Instruction(Opcode.JNZW, callLabel = it[1].value.callLabel) + Opcode.JNZ -> Instruction(Opcode.JZ, callLabel = it[1].value.callLabel) + Opcode.JNZW -> Instruction(Opcode.JZW, callLabel = it[1].value.callLabel) + else -> Instruction(Opcode.NOP) + } + instructionsToReplace[it[1].index] = replacement + } + } + } + + for (rins in instructionsToReplace) { + blk.instructions[rins.key] = rins.value + } + } + } + private fun optimizeRemoveNops() { // remove nops (that are not a label) for (blk in blocks) diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index e85667af1..116f5bd52 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -16,6 +16,9 @@ import kotlin.math.abs class AssemblyError(msg: String) : RuntimeException(msg) +// TODO: code generation for POW instruction + + class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues, val zeropage: Zeropage) { private val globalFloatConsts = mutableMapOf() private val assemblyLines = mutableListOf() diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 2e2b01722..114a224d1 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -45,6 +45,7 @@ val BuiltinFunctions = mapOf( "atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::atan) }, "ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::log) }, "log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, ::log2) }, + // TODO: sqrt() should have integer versions too "sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sqrt) }, "rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toRadians) }, "deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) }, diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index acb0eff57..5fd019f05 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -285,10 +285,14 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He val constvalue = repeatLoop.untilCondition.constValue(namespace, heap) if(constvalue!=null) { return if(constvalue.asBooleanValue){ - // always true -> keep only the statement block + // always true -> keep only the statement block (if there are no continue and break statements) printWarning("condition is always true", repeatLoop.position) - optimizationsDone++ - repeatLoop.body + if(hasContinueOrBreak(repeatLoop.body)) + repeatLoop + else { + optimizationsDone++ + repeatLoop.body + } } else { // always false -> print a warning, and optimize into body + jump (if there are no continue and break statements) printWarning("condition is always false", repeatLoop.position) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 488f970a6..cdd291fe9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -40,3 +40,10 @@ Should use the zeropage for variables - Variables should be allocated in the zeropage as long as it has space. - add some sort of ``zp`` modifier keyword on vardecls to force them into zeropage? + +Misc +^^^^ + +- sqrt() should have integer implementation as well, instead of relying on float SQRT for all argument types +- code generation for POW instruction + diff --git a/examples/test.p8 b/examples/test.p8 index 668332c28..db8246269 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,44 +1,48 @@ %import c64utils +%import c64flt +%option enable_floats ~ main { - ubyte xx=99 - word yy=12345 - sub start() { - c64scr.print_ub(xx) - c64.CHROUT('\n') - c64scr.print_w(yy) - c64.CHROUT('\n') - foo.derp() - foo2.derp() + ubyte ub + byte b + word w + uword uw + float f1 + float f2 + float f3 + float f4 + float f5 + float f6 + + f1=sqrt(A) + + f1=A**0.5 + f2=ub**0.5 + f3=b**0.5 + f4=w**0.5 + f5=uw**0.5 + f6=f1**0.5 + +; A=A**5 +; ub=ub**5 +; b=b**5 +; w=w**5 +; uw=uw**5 +; f=f**5 +; +; A=A**Y +; ub=ub**Y +; b=b**Y +; w=w**Y +; uw=uw**Y +; f=f**Y + } ; @todo code for pow() - ; @todo optimize code generation for "if blah ..." and "if not blah ..." - - ; @todo optimize vm - ; push_byte ub:01 - ; jnz _prog8stmt_7_loop -} - - -~ foo { - - ubyte woo=2 - - sub derp() { - A=woo - } -} - -~ foo2 { - - sub derp() { - ubyte woo=3 - A=99 - } }