IR: BIT instruction added

This commit is contained in:
Irmen de Jong 2024-12-24 22:26:20 +01:00
parent e174b31344
commit 827df04b32
7 changed files with 143 additions and 23 deletions

View File

@ -75,9 +75,6 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
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
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"

View File

@ -1454,8 +1454,51 @@ class IRCodeGen(
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
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")
fun ifElseUsingBIT(testBitSet: Boolean, value: PtIdentifier, bitnumber: Int) {
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = value.name), null)
val bitBranchOpcode = when(testBitSet) {
true -> when(bitnumber) {
6 -> Opcode.BSTVC
7 -> Opcode.BSTPOS
else -> throw AssemblyError("need bit 6 or 7")
}
false -> when(bitnumber) {
6 -> Opcode.BSTVS
7 -> Opcode.BSTNEG
else -> throw AssemblyError("need bit 6 or 7")
}
}
if(ifElse.hasElse()) {
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, null)
} else {
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null)
}
}
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_")) {
// use a BIT instruction to test for bit 8 or 7
when(condition.name) {
"prog8_ifelse_bittest_set" -> {
ifElseUsingBIT(true, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
return
}
"prog8_ifelse_bittest_notset" -> {
ifElseUsingBIT(false, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
return
}
else -> throw AssemblyError("weird bittest")
}
}
val tr = expressionEval.translateExpression(condition)
if(addCmpiZero)

View File

@ -566,4 +566,35 @@ main {
ifelse.hasElse() shouldBe true
ifelse.condition shouldBe instanceOf<PtFunctionCall>()
}
test("bit instruction is used for testing bits 6 and 7 of a byte") {
val text = """
main {
sub start() {
if cx16.r0L & ${'$'}80 != 0
return
if cx16.r1L & ${'$'}80 == 0
return
if cx16.r2L & ${'$'}40 != 0
return
if cx16.r3L & ${'$'}40 == 0
return
}
}"""
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
val assembly = assemblyFile.readText()
assembly shouldContain "bit cx16.r0L"
assembly shouldContain "bit cx16.r1L"
assembly shouldContain "bit cx16.r2L"
assembly shouldContain "bit cx16.r3L"
val resultIR = compileText(VMTarget(), true, text, writeAssembly = true)!!
val irFile = resultIR.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
val ir = irFile.readText()
ir shouldContain "bit.b ${'$'}ff02" // r0
ir shouldContain "bit.b ${'$'}ff04" // r1
ir shouldContain "bit.b ${'$'}ff06" // f2
ir shouldContain "bit.b ${'$'}ff08" // r3
}
})

View File

@ -22,7 +22,6 @@ Future Things and Ideas
- Kotlin: can we use inline value classes in certain spots?
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- don't do BIT instruction tests via makeBittestCall() fake builtin function. Just do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
Add a -library $xxxx command line option (and/or some directive) to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
@ -36,6 +35,7 @@ Future Things and Ideas
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
- Allow normal subroutines to return multiple values as well (just as asmsubs already can)
- don't do BIT instruction tests via optimizeBitTest() using a fake builtin function. Do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first)
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
- Improve register load order in subroutine call args assignments:
@ -55,6 +55,8 @@ Future Things and Ideas
IR/VM
-----
- make a liast in the P8IR file of the data type of every used virtual register (it should have 1 unique type assigned to it when it is allocated, and never used for other types)
- getting it in shape for code generation...
- fix TODO("IR rol/ror on split words array")
- fix "<< in array" / ">> in array"
- implement missing operators in AssignmentGen (array shifts etc)
@ -67,9 +69,6 @@ IR/VM
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
maybe another representation is needed once actual codegeneration is done from the IR...?
- split word arrays, both _msb and _lsb arrays are tagged with an alignment. This is not what's intended; only the one put in memory first should be aligned (the other one should follow straight after it)
- getting it in shape for code generation...
- make optimizeBitTest work for IR too to use the BIT instruction?
- make sure that a 6502 codegen based off the IR, still generates BIT instructions when testing bit 7 or 6 of a byte var.
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change

View File

@ -1,22 +1,40 @@
%import floats
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
bool bb
ubyte ub
uword uw
uw, void = thing2()
uw, bb = thing2()
uw, ub = thing2()
}
if cx16.r0L & $80 != 0
return
if cx16.r1L & $80 == 0
return
if cx16.r2L & $40 != 0
return
if cx16.r3L & $40 == 0
return
asmsub thing2() -> ubyte @A, bool @Pc {
%asm {{
lda #$aa
clc
rts
}}
cx16.r0L = 0
test()
txt.nl()
cx16.r0L = 255
test()
txt.nl()
sub test() {
if cx16.r0L & $80 != 0
txt.chrout('1')
if cx16.r0L & $80 == 0
txt.chrout('2')
if cx16.r0L & $40 != 0
txt.chrout('3')
if cx16.r0L & $40 == 0
txt.chrout('4')
if cx16.r0L & $20 != 0
txt.chrout('5')
if cx16.r0L & $20 == 0
txt.chrout('6')
}
}
}

View File

@ -215,6 +215,7 @@ rorm address - rotate memory right by 1 bits, no
roxrm address - rotate memory right by 1 bits, using carry + set Carry to shifted bit
rolm address - rotate memory left by 1 bits, not using carry + set Carry to shifted bit
roxlm address - rotate memory left by 1 bits, using carry, + set Carry to shifted bit
bit address - test bits in byte value at address, this is a special instruction available on other systems to optimize testing and branching on bits 7 and 6
FLOATING POINT CONVERSIONS AND FUNCTIONS
@ -400,6 +401,7 @@ enum class Opcode {
ROLM,
ROXL,
ROXLM,
BIT,
FFROMUB,
FFROMSB,
@ -476,6 +478,7 @@ val OpcodesThatBranch = arrayOf(
)
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
Opcode.BIT,
Opcode.CMP,
Opcode.CMPI
)
@ -764,6 +767,7 @@ val instructionFormats = mutableMapOf(
Opcode.ROLM to InstructionFormat.from("BW,<>a"),
Opcode.ROXL to InstructionFormat.from("BW,<>r1"),
Opcode.ROXLM to InstructionFormat.from("BW,<>a"),
Opcode.BIT to InstructionFormat.from("B,<a"),
Opcode.FFROMUB to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),

View File

@ -56,6 +56,7 @@ class VirtualMachine(irProgram: IRProgram) {
var statusCarry = false
var statusZero = false
var statusNegative = false
var statusOverflow = false
internal var randomGenerator = Random(0xa55a7653)
internal var randomGeneratorFloats = Random(0xc0d3dbad)
internal var mul16LastUpper = 0u
@ -224,7 +225,8 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.BSTNE -> InsBSTNE(ins)
Opcode.BSTNEG -> InsBSTNEG(ins)
Opcode.BSTPOS -> InsBSTPOS(ins)
Opcode.BSTVC, Opcode.BSTVS -> TODO("overflow status flag not yet supported in VM (BSTVC,BSTVS)")
Opcode.BSTVC -> InsBSTVC(ins)
Opcode.BSTVS -> InsBSTVS(ins)
Opcode.BGTR -> InsBGTR(ins)
Opcode.BGTSR -> InsBGTSR(ins)
Opcode.BGER -> InsBGER(ins)
@ -324,6 +326,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.CLC -> { statusCarry = false; nextPc() }
Opcode.SEC -> { statusCarry = true; nextPc() }
Opcode.CLI, Opcode.SEI -> throw IllegalArgumentException("VM doesn't support interrupt status bit")
Opcode.BIT -> InsBIT(ins)
Opcode.FFROMUB -> InsFFROMUB(ins)
Opcode.FFROMSB -> InsFFROMSB(ins)
@ -786,6 +789,20 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsBSTVS(i: IRInstruction) {
if(statusOverflow)
branchTo(i)
else
nextPc()
}
private fun InsBSTVC(i: IRInstruction) {
if(!statusOverflow)
branchTo(i)
else
nextPc()
}
private fun InsBGTR(i: IRInstruction) {
val (left: UInt, right: UInt) = getBranchOperandsU(i)
if(left>right)
@ -1751,6 +1768,17 @@ class VirtualMachine(irProgram: IRProgram) {
else -> throw IllegalArgumentException("operator word $operator")
}
private fun InsBIT(i: IRInstruction) {
if (i.type!! == IRDataType.BYTE) {
val value = memory.getUB(i.address!!)
statusNegative = value.toInt() and 0x80 != 0
statusOverflow = value.toInt() and 0x40 != 0
// NOTE: the 'AND' part of the BIT instruction as it does on the 6502 CPU, is not utilized in prog8 so we don't implement it here
}
else throw IllegalArgumentException("bit needs byte")
nextPc()
}
private fun InsEXT(i: IRInstruction) {
when(i.type!!){
IRDataType.BYTE -> registers.setUW(i.reg1!!, registers.getUB(i.reg2!!).toUShort())