diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 4720b9190..593afd0ee 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -103,7 +103,7 @@ class PtIfElse(position: Position) : PtNode(position) { class PtJump(val identifier: PtIdentifier?, val address: UInt?, - val generatedLabel: String?, + val generatedLabel: String?, // TODO remove this ? always uses identifier... position: Position) : PtNode(position) { init { identifier?.let {it.parent = this } diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 32905f3d1..3a960665b 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -120,7 +120,7 @@ enum class BranchCondition { PL, // PL == POS POS, VS, - VC, + VC } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 4a35aa795..d8aa4b11e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -65,14 +65,18 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend { } is PtJump -> { if(node.identifier!=null) { - val stNode = st.lookup(node.identifier!!.name)!! + val stNode = st.lookup(node.identifier!!.name) + if(stNode==null) + throw AssemblyError("name not found ${node.identifier}") if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) { val index = node.parent.children.indexOf(node) nodesToPrefix += node.parent to index } } else if(node.generatedLabel!=null) { - val stNode = st.lookup(node.generatedLabel!!)!! + val stNode = st.lookup(node.generatedLabel!!) + if(stNode==null) + throw AssemblyError("name not found ${node.generatedLabel}") if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) { val index = node.parent.children.indexOf(node) nodesToPrefix += node.parent to index diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index d0e0c2e2a..460d672c5 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -279,7 +279,98 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr return call } - private fun transform(srcIf: IfElse): PtIfElse { + private fun transform(srcIf: IfElse): PtNode { + + fun codeForStatusflag(fcall: FunctionCallExpression, flag: Statusflag, equalToZero: Boolean): PtNodeGroup? { + // if the condition is a call to something that returns a boolean in a status register (C, Z, V, N), + // a smarter branch is possible using a conditional branch node. + val (branchTrue, branchFalse) = if(equalToZero) { + when (flag) { + Statusflag.Pc -> BranchCondition.CC to BranchCondition.CS + Statusflag.Pz -> BranchCondition.NZ to BranchCondition.Z + Statusflag.Pv -> BranchCondition.VC to BranchCondition.VS + Statusflag.Pn -> BranchCondition.POS to BranchCondition.NEG + } + } else { + when (flag) { + Statusflag.Pc -> BranchCondition.CS to BranchCondition.CC + Statusflag.Pz -> BranchCondition.Z to BranchCondition.NZ + Statusflag.Pv -> BranchCondition.VS to BranchCondition.VC + Statusflag.Pn -> BranchCondition.NEG to BranchCondition.POS + } + } + val jump = srcIf.truepart.statements.firstOrNull() as? Jump + if (jump!=null) { + // only a jump, use a conditional branch to the jump target. + val nodes = PtNodeGroup() + nodes.add(transformExpression(fcall)) + val branch = PtConditionalBranch(branchTrue, srcIf.position) + val ifScope = PtNodeGroup() + ifScope.add(transform(jump)) + val elseScope = PtNodeGroup() + if(srcIf.elsepart.isNotEmpty()) + throw FatalAstException("if-else with only a goto should no longer have statements in the else part") + branch.add(ifScope) + branch.add(elseScope) + nodes.add(branch) + return nodes + } else { + // skip over the true part if the condition is false + val nodes = PtNodeGroup() + nodes.add(transformExpression(fcall)) + val branch = PtConditionalBranch(branchFalse, srcIf.position) + val ifScope = PtNodeGroup() + val elseLabel = program.makeLabel("celse") + val endLabel = program.makeLabel("cend") + val scopedElseLabel = (srcIf.definingScope.scopedName + elseLabel).joinToString(".") + val scopedEndLabel = (srcIf.definingScope.scopedName + endLabel).joinToString(".") + val elseLbl = PtIdentifier(scopedElseLabel, DataType.UNDEFINED, srcIf.position) + val endLbl = PtIdentifier(scopedEndLabel, DataType.UNDEFINED, srcIf.position) + ifScope.add(PtJump(elseLbl, null, null, srcIf.position)) + val elseScope = PtNodeGroup() + branch.add(ifScope) + branch.add(elseScope) + nodes.add(branch) + for (stmt in srcIf.truepart.statements) + nodes.add(transformStatement(stmt)) + if(srcIf.elsepart.isNotEmpty()) + nodes.add(PtJump(endLbl, null, null, srcIf.position)) + nodes.add(PtLabel(elseLabel, srcIf.position)) + if(srcIf.elsepart.isNotEmpty()) { + for (stmt in srcIf.elsepart.statements) + nodes.add(transformStatement(stmt)) + } + if(srcIf.elsepart.isNotEmpty()) + nodes.add(PtLabel(endLabel, srcIf.position)) + return nodes + } + } + + val binexpr = srcIf.condition as? BinaryExpression + if(binexpr!=null && binexpr.right.constValue(program)?.number==0.0) { + if(binexpr.operator=="==" || binexpr.operator=="!=") { + val fcall = binexpr.left as? FunctionCallExpression + if(fcall!=null) { + val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters + if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) { + val translated = codeForStatusflag(fcall, returnRegs[0].statusflag!!, binexpr.operator == "==") + if(translated!=null) + return translated + } + } + } + } else { + val fcall = srcIf.condition as? FunctionCallExpression + if (fcall != null) { + val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters + if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) { + val translated = codeForStatusflag(fcall, returnRegs[0].statusflag!!, false) + if(translated!=null) + return translated + } + } + } + val ifelse = PtIfElse(srcIf.position) ifelse.add(transformExpression(srcIf.condition)) val ifScope = PtNodeGroup() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 90524cfcf..0bf10297e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,7 +2,8 @@ TODO ==== -- optimize if-else expressions whose condition returns the boolean status in a status register to use a branch opcode instead of a comparison against 0 +- optimize: flip if true/else blocks if the else block only contains a jump (invert condition!) +- remove PtJump generatedLabel (always seems to use identifier) - merge branch optimize-st for some optimizations regardign SymbolTable use diff --git a/examples/test.p8 b/examples/test.p8 index 2279f48dd..098808d5e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,39 +4,141 @@ main { sub start() { - bool @shared blerp = test(100) + ; expected output: 0000 + cx16.r0L = test_c_clear() + cx16.r1L = test_z_clear() + cx16.r2L = test_n_clear() + cx16.r3L = test_v_clear() + txt.print_ub(cx16.r0L) + txt.print_ub(cx16.r1L) + txt.print_ub(cx16.r2L) + txt.print_ub(cx16.r3L) + txt.nl() + ; expected output: 1111 + cx16.r0L = test_c_set() + cx16.r1L = test_z_set() + cx16.r2L = test_n_set() + cx16.r3L = test_v_set() + txt.print_ub(cx16.r0L) + txt.print_ub(cx16.r1L) + txt.print_ub(cx16.r2L) + txt.print_ub(cx16.r3L) + txt.nl() - if test(100) - goto skip - txt.print("no0") + ; exptected output: no2, no3, no5, yes6, no7, yes8 + if not test_c_clear() + goto skip1 + else + txt.print("no1\n") -skip: - if test(100) { - txt.print("yes1") +skip1: + if test_c_clear() goto skip2 - } - txt.print("no1") + else + txt.print("no2\n") skip2: - if test(100) - txt.print("yes2") + + if not test_c_set() + goto skip3 else - txt.print("no2") + txt.print("no3\n") + +skip3: + if test_c_set() + goto skip4 + else + txt.print("no4\n") + +skip4: + + if test_c_clear() { + txt.print("yes5\n") + goto skip5 + } + txt.print("no5\n") + +skip5: + if not test_c_clear() { + txt.print("yes6\n") + goto skip6 + } + txt.print("no6\n") + +skip6: + if test_c_clear() + txt.print("yes7\n") + else + txt.print("no7\n") + + if not test_c_clear() + txt.print("yes8\n") + else + txt.print("no8\n") - while test(100) { + while test_c_clear() { cx16.r0++ } do { cx16.r0++ - } until test(100) + } until test_c_set() } - asmsub test(ubyte value @A) -> bool @Pz { + asmsub test_c_clear() -> bool @Pc { + %asm {{ + clc + rts + }} + } + + asmsub test_z_clear() -> bool @Pz { + %asm {{ + lda #1 + rts + }} + } + + asmsub test_n_clear() -> bool @Pn { + %asm {{ + lda #1 + rts + }} + } + + asmsub test_v_clear() -> bool @Pv { + %asm {{ + clv + rts + }} + } + + asmsub test_c_set() -> bool @Pc { + %asm {{ + sec + rts + }} + } + + asmsub test_z_set() -> bool @Pz { %asm {{ lda #0 rts }} } + + asmsub test_n_set() -> bool @Pn { + %asm {{ + lda #-1 + rts + }} + } + + asmsub test_v_set() -> bool @Pv { + %asm {{ + bit + ++ rts + }} + } }