mirror of
https://github.com/irmen/prog8.git
synced 2025-02-17 13:31:01 +00:00
Codegen: use BIT instruction for memory location bit 7 and 6 tests (use N and V flags)
This commit is contained in:
parent
0ec719e429
commit
c67f877857
@ -82,6 +82,8 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||||||
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
|
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
|
||||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||||
|
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
|
||||||
|
"prog8_ifelse_bittest_notset" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
|
||||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
||||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||||
|
@ -11,6 +11,7 @@ fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st:
|
|||||||
return
|
return
|
||||||
while (errors.noErrors() &&
|
while (errors.noErrors() &&
|
||||||
(optimizeCommonSubExpressions(program, errors)
|
(optimizeCommonSubExpressions(program, errors)
|
||||||
|
+ optimizeBitTest(program, options)
|
||||||
+ optimizeAssignTargets(program, st, errors)) > 0
|
+ optimizeAssignTargets(program, st, errors)) > 0
|
||||||
) {
|
) {
|
||||||
// keep rolling
|
// keep rolling
|
||||||
@ -164,6 +165,51 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: I
|
|||||||
return changes
|
return changes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): Int {
|
||||||
|
if(options.compTarget.machine.cpu == CpuType.VIRTUAL)
|
||||||
|
return 0 // the special bittest optimization is not yet valid for the IR
|
||||||
|
|
||||||
|
var changes = 0
|
||||||
|
var recurse = true
|
||||||
|
walkAst(program) { node: PtNode, depth: Int ->
|
||||||
|
if(node is PtIfElse) {
|
||||||
|
val condition = node.condition as? PtBinaryExpression
|
||||||
|
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
|
||||||
|
if(condition.right.asConstInteger()==0) {
|
||||||
|
val and = condition.left as? PtBinaryExpression
|
||||||
|
if(and != null && and.operator=="&" && and.type == DataType.UBYTE) {
|
||||||
|
val variable = and.left as? PtIdentifier
|
||||||
|
val bitmask = and.right.asConstInteger()
|
||||||
|
if(variable!=null && variable.type in ByteDatatypes && (bitmask==128 || bitmask==64)) {
|
||||||
|
val setOrNot = if(condition.operator=="!=") "set" else "notset"
|
||||||
|
val index = node.parent.children.indexOf(node)
|
||||||
|
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.BOOL, node.condition.position)
|
||||||
|
bittestCall.add(variable)
|
||||||
|
if(bitmask==128)
|
||||||
|
bittestCall.add(PtNumber(DataType.UBYTE, 7.0, and.right.position))
|
||||||
|
else
|
||||||
|
bittestCall.add(PtNumber(DataType.UBYTE, 6.0, and.right.position))
|
||||||
|
val ifElse = PtIfElse(node.position)
|
||||||
|
ifElse.add(bittestCall)
|
||||||
|
ifElse.add(node.ifScope)
|
||||||
|
if(node.hasElse())
|
||||||
|
ifElse.add(node.elseScope)
|
||||||
|
node.parent.children[index] = ifElse
|
||||||
|
ifElse.parent = node.parent
|
||||||
|
changes++
|
||||||
|
recurse = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recurse
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
|
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
|
||||||
if(returnedRegister in Cx16VirtualRegisters) {
|
if(returnedRegister in Cx16VirtualRegisters) {
|
||||||
val regname = returnedRegister.name.lowercase()
|
val regname = returnedRegister.name.lowercase()
|
||||||
|
@ -626,6 +626,16 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
|||||||
mods.add(Modification(lines[0].index, true, null))
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
}
|
}
|
||||||
|
else if((" bvc " in first || "\tbvc " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bvs")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
|
else if((" bvs " in first || "\tbvs " in first) && sameLabel(first, second, third)) {
|
||||||
|
val branch = second.replace("jmp", "bvc")
|
||||||
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
|
mods.add(Modification(lines[1].index, false, branch))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
|
@ -65,6 +65,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||||
"call" -> funcCall(fcall)
|
"call" -> funcCall(fcall)
|
||||||
|
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
|
||||||
|
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
|
||||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
|
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
|
||||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
|
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
|
||||||
|
@ -83,9 +83,80 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
|
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
|
||||||
|
val bittest = ifElse.condition as? PtBuiltinFunctionCall
|
||||||
|
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
|
||||||
|
|
||||||
|
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
|
||||||
|
val variable = bittest.args[0] as PtIdentifier
|
||||||
|
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
|
||||||
|
val testForBitSet = bittest.name.endsWith("_set")
|
||||||
|
when (bitnumber) {
|
||||||
|
7 -> {
|
||||||
|
// test via bit + N flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val (asmLabel, indirect) = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
if(indirect)
|
||||||
|
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||||
|
if(ifElse.hasElse())
|
||||||
|
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||||
|
else
|
||||||
|
asmgen.out(" bmi $asmLabel")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bpl", ifElse)
|
||||||
|
} else {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val (asmLabel, indirect) = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
if(indirect)
|
||||||
|
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||||
|
if(ifElse.hasElse())
|
||||||
|
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||||
|
else
|
||||||
|
asmgen.out(" bpl $asmLabel")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bmi", ifElse)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
6 -> {
|
||||||
|
// test via bit + V flag
|
||||||
|
asmgen.out(" bit ${variable.name}")
|
||||||
|
if(testForBitSet) {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val (asmLabel, indirect) = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
if(indirect)
|
||||||
|
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||||
|
if(ifElse.hasElse())
|
||||||
|
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||||
|
else
|
||||||
|
asmgen.out(" bvs $asmLabel")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bvc", ifElse)
|
||||||
|
} else {
|
||||||
|
if(jumpAfterIf!=null) {
|
||||||
|
val (asmLabel, indirect) = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
if(indirect)
|
||||||
|
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||||
|
if(ifElse.hasElse())
|
||||||
|
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||||
|
else
|
||||||
|
asmgen.out(" bvc $asmLabel")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translateIfElseBodies("bvs", ifElse)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||||
assignConditionValueToRegisterAndTest(ifElse.condition)
|
assignConditionValueToRegisterAndTest(ifElse.condition)
|
||||||
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
|
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, ifElse.elseScope)
|
translateJumpElseBodies("bne", "beq", jumpAfterIf, ifElse.elseScope)
|
||||||
else
|
else
|
||||||
|
@ -38,6 +38,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
"ror" -> funcRolRor(call)
|
"ror" -> funcRolRor(call)
|
||||||
"rol2" -> funcRolRor(call)
|
"rol2" -> funcRolRor(call)
|
||||||
"ror2" -> funcRolRor(call)
|
"ror2" -> funcRolRor(call)
|
||||||
|
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
|
||||||
|
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
|
||||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||||
|
@ -1318,6 +1318,10 @@ class IRCodeGen(
|
|||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
|
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
|
||||||
|
|
||||||
|
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
|
||||||
|
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
|
||||||
|
|
||||||
val tr = expressionEval.translateExpression(condition)
|
val tr = expressionEval.translateExpression(condition)
|
||||||
result += tr.chunks
|
result += tr.chunks
|
||||||
if(ifElse.hasElse()) {
|
if(ifElse.hasElse()) {
|
||||||
|
@ -3,16 +3,6 @@ TODO
|
|||||||
|
|
||||||
See open issues on github.
|
See open issues on github.
|
||||||
|
|
||||||
Asm peephole optimizer: while cx16.VERA_AUDIO_CTRL & %01000000 == 0 { } compiles into the following. Replace the bne+bra into a beq. Similar for !=0 I guess?
|
|
||||||
p8l_label_5_whileloop
|
|
||||||
lda cx16.VERA_AUDIO_CTRL
|
|
||||||
and #$40
|
|
||||||
bne p8l_label_6_afterwhile
|
|
||||||
bra p8l_label_5_whileloop
|
|
||||||
p8l_label_6_afterwhile
|
|
||||||
|
|
||||||
Codegen: use BIT instruction for memory location bit 7 and 6 tests (use N and V flags)
|
|
||||||
|
|
||||||
Re-generate the skeletons doc files.
|
Re-generate the skeletons doc files.
|
||||||
|
|
||||||
Improve register load order in subroutine call args assignments:
|
Improve register load order in subroutine call args assignments:
|
||||||
|
172
examples/test.p8
172
examples/test.p8
@ -1,4 +1,3 @@
|
|||||||
%import buffers
|
|
||||||
%import textio
|
%import textio
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
@ -6,75 +5,114 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
signed()
|
ubyte @shared variable
|
||||||
unsigned()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub signed() {
|
variable = 0
|
||||||
txt.print("signed\n")
|
while variable & %10000000 == 0 {
|
||||||
byte @shared bvalue = -88
|
cx16.r0L++
|
||||||
word @shared wvalue = -8888
|
variable = 128
|
||||||
|
}
|
||||||
|
txt.chrout('1')
|
||||||
|
while variable & %10000000 != 0 {
|
||||||
|
cx16.r0L++
|
||||||
|
variable = 0
|
||||||
|
}
|
||||||
|
txt.chrout('2')
|
||||||
|
while variable & %01000000 == 0 {
|
||||||
|
cx16.r0L++
|
||||||
|
variable = 64
|
||||||
|
}
|
||||||
|
txt.chrout('3')
|
||||||
|
while variable & %01000000 != 0 {
|
||||||
|
cx16.r0L++
|
||||||
|
variable=0
|
||||||
|
}
|
||||||
|
txt.chrout('4')
|
||||||
|
variable = 255
|
||||||
|
while variable & %10000000 == 0 {
|
||||||
|
}
|
||||||
|
while variable & %01000000 == 0 {
|
||||||
|
}
|
||||||
|
txt.chrout('5')
|
||||||
|
variable = 0
|
||||||
|
while variable & %10000000 != 0 {
|
||||||
|
}
|
||||||
|
while variable & %01000000 != 0 {
|
||||||
|
}
|
||||||
|
txt.chrout('6')
|
||||||
|
txt.chrout('\n')
|
||||||
|
|
||||||
txt.print_b(bvalue/2)
|
variable = 0
|
||||||
txt.spc()
|
cx16.r0L++
|
||||||
txt.print_b(bvalue/4)
|
if variable & %10000000 == 0 {
|
||||||
txt.spc()
|
txt.print("bit 7 not set\n")
|
||||||
txt.print_b(bvalue/8)
|
}
|
||||||
txt.nl()
|
if variable & %10000000 != 0 {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
}
|
||||||
|
if variable & %10000000 == 0 {
|
||||||
|
txt.print("bit 7 not set\n")
|
||||||
|
} else {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
}
|
||||||
|
if variable & %10000000 != 0 {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
} else {
|
||||||
|
txt.print("bit 7 not set\n")
|
||||||
|
}
|
||||||
|
|
||||||
bvalue /= 2
|
variable = 128
|
||||||
txt.print_b(bvalue)
|
cx16.r0L++
|
||||||
txt.spc()
|
if variable & %10000000 == 0 {
|
||||||
bvalue /= 8
|
txt.print("bit 7 not set\n")
|
||||||
txt.print_b(bvalue)
|
}
|
||||||
txt.nl()
|
if variable & %10000000 != 0 {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
}
|
||||||
|
if variable & %10000000 == 0 {
|
||||||
|
txt.print("bit 7 not set\n")
|
||||||
|
} else {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
}
|
||||||
|
if variable & %10000000 != 0 {
|
||||||
|
txt.print("bit 7 set\n")
|
||||||
|
} else {
|
||||||
|
txt.print("bit 7 not set\n")
|
||||||
|
}
|
||||||
|
|
||||||
txt.print_w(wvalue/2)
|
if variable & %01000000 == 0 {
|
||||||
txt.spc()
|
txt.print("bit 6 not set\n")
|
||||||
txt.print_w(wvalue/4)
|
}
|
||||||
txt.spc()
|
if variable & %01000000 != 0 {
|
||||||
txt.print_w(wvalue/8)
|
txt.print("bit 6 set\n")
|
||||||
txt.nl()
|
}
|
||||||
|
if variable & %01000000 == 0 {
|
||||||
wvalue /= 2
|
txt.print("bit 6 not set\n")
|
||||||
txt.print_w(wvalue)
|
} else {
|
||||||
txt.spc()
|
txt.print("bit 6 set\n")
|
||||||
wvalue /= 8
|
}
|
||||||
txt.print_w(wvalue)
|
if variable & %01000000 != 0 {
|
||||||
txt.nl()
|
txt.print("bit 6 set\n")
|
||||||
}
|
} else {
|
||||||
|
txt.print("bit 6 not set\n")
|
||||||
sub unsigned() {
|
}
|
||||||
txt.print("\nunsigned\n")
|
variable = %01000000
|
||||||
ubyte @shared bvalue = 88
|
cx16.r0L++
|
||||||
uword @shared wvalue = 8888
|
if variable & %01000000 == 0 {
|
||||||
|
txt.print("bit 6 not set\n")
|
||||||
txt.print_ub(bvalue/2)
|
}
|
||||||
txt.spc()
|
if variable & %01000000 != 0 {
|
||||||
txt.print_ub(bvalue/4)
|
txt.print("bit 6 set\n")
|
||||||
txt.spc()
|
}
|
||||||
txt.print_ub(bvalue/8)
|
if variable & %01000000 == 0 {
|
||||||
txt.nl()
|
txt.print("bit 6 not set\n")
|
||||||
|
} else {
|
||||||
bvalue /= 2
|
txt.print("bit 6 set\n")
|
||||||
txt.print_ub(bvalue)
|
}
|
||||||
txt.spc()
|
if variable & %01000000 != 0 {
|
||||||
bvalue /= 8
|
txt.print("bit 6 set\n")
|
||||||
txt.print_ub(bvalue)
|
} else {
|
||||||
txt.nl()
|
txt.print("bit 6 not set\n")
|
||||||
|
}
|
||||||
txt.print_uw(wvalue/2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(wvalue/4)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(wvalue/8)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
wvalue /= 2
|
|
||||||
txt.print_uw(wvalue)
|
|
||||||
txt.spc()
|
|
||||||
wvalue /= 8
|
|
||||||
txt.print_uw(wvalue)
|
|
||||||
txt.nl()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user