fixed silly if-goto expression code in IR codegen where it used too many branching instructions

This commit is contained in:
Irmen de Jong 2022-12-12 22:47:15 +01:00
parent 27568c2bef
commit def7e87151
5 changed files with 94 additions and 3 deletions

View File

@ -320,6 +320,41 @@ class IRCodeGen(
private fun translate(branch: PtConditionalBranch): IRCodeChunks { private fun translate(branch: PtConditionalBranch): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val goto = branch.trueScope.children.firstOrNull() as? PtJump
if(goto is PtJump && branch.falseScope.children.isEmpty()) {
// special case the form: if_cc <condition> goto <place>
val address = goto.address?.toInt()
if(address!=null) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, value = address)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, value = address)
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, value = address)
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, value = address)
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, value = address)
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, value = address)
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, value = address)
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, value = address)
}
addInstr(result, branchIns, null)
} else {
val label = if(goto.generatedLabel!=null) goto.generatedLabel else goto.identifier!!.targetName.joinToString(".")
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
}
addInstr(result, branchIns, null)
}
return result
}
val elseLabel = createLabelName() val elseLabel = createLabelName()
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part // note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
val branchIns = when(branch.condition) { val branchIns = when(branch.condition) {
@ -329,8 +364,8 @@ class IRCodeGen(
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel) BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel) BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel) BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = elseLabel) BranchCondition.VC -> IRInstruction(Opcode.BSTVS, labelSymbol = elseLabel)
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = elseLabel) BranchCondition.VS -> IRInstruction(Opcode.BSTVC, labelSymbol = elseLabel)
} }
addInstr(result, branchIns, null) addInstr(result, branchIns, null)
result += translateNode(branch.trueScope) result += translateNode(branch.trueScope)
@ -868,6 +903,32 @@ class IRCodeGen(
val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val irDt = irType(ifElse.condition.left.type) val irDt = irType(ifElse.condition.left.type)
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
if(goto!=null && ifElse.elseScope.children.isEmpty()) {
// special case the form: if <condition> goto <place>
val result = mutableListOf<IRCodeChunkBase>()
val leftReg = registers.nextFree()
val rightReg = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
result += expressionEval.translateExpression(ifElse.condition.right, rightReg, -1)
val opcode = when(ifElse.condition.operator) {
"==" -> Opcode.BEQ
"!=" -> Opcode.BNE
"<" -> Opcode.BLT
">" -> Opcode.BGT
"<=" -> Opcode.BLE
">=" -> Opcode.BGE
else -> throw AssemblyError("invalid comparison operator")
}
if(goto.address!=null)
addInstr(result, IRInstruction(opcode, irDt, reg1=leftReg, reg2=rightReg, value = goto.address?.toInt()), null)
else if(goto.generatedLabel!=null)
addInstr(result, IRInstruction(opcode, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = goto.identifier!!.targetName.joinToString(".")), null)
return result
}
fun translateNonZeroComparison(): IRCodeChunks { fun translateNonZeroComparison(): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val elseBranch = when(ifElse.condition.operator) { val elseBranch = when(ifElse.condition.operator) {

View File

@ -40,6 +40,7 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
} }

View File

@ -23,5 +23,6 @@
<orderEntry type="module" module-name="codeGenExperimental" /> <orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenIntermediate" /> <orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" /> <orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="module" module-name="intermediate" scope="TEST" />
</component> </component>
</module> </module>

View File

@ -10,6 +10,9 @@ import prog8.ast.statements.Assignment
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode
import prog8.vm.VmRunner import prog8.vm.VmRunner
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
import kotlin.io.path.readText import kotlin.io.path.readText
@ -298,4 +301,30 @@ main {
val target = VMTarget() val target = VMTarget()
compileText(target, false, src, writeAssembly = true) shouldNotBe null compileText(target, false, src, writeAssembly = true) shouldNotBe null
} }
test("compile virtual: short code for if-goto") {
val src = """
main {
sub start() {
if_cc
goto ending
if_cs
goto ending
if cx16.r0 goto ending
if cx16.r0==0 goto ending
if cx16.r0!=0 goto ending
if cx16.r0s>0 goto ending
if cx16.r0s<0 goto ending
ending:
}
}"""
val result = compileText(VMTarget(), true, src, writeAssembly = true)!!
result.program.entrypoint.statements.size shouldBe 9
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
val irProgram = IRFileReader().read(virtfile)
val start = irProgram.blocks[0].children[0] as IRSubroutine
val instructions = start.chunks.flatMap { c->c.instructions }
instructions.size shouldBe 18
instructions.last().opcode shouldBe Opcode.RETURN
}
}) })

View File

@ -3,7 +3,6 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- if epxr goto somewhere creates inefficient code on virt
- regression test the various projects before release - regression test the various projects before release
... ...