optimize if-else handling of asmsub boolean result in status flags

This commit is contained in:
Irmen de Jong 2023-12-13 02:26:56 +01:00
parent 0da9142009
commit a546c2247d
6 changed files with 219 additions and 21 deletions

View File

@ -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 }

View File

@ -120,7 +120,7 @@ enum class BranchCondition {
PL, // PL == POS
POS,
VS,
VC,
VC
}

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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
}}
}
}