some improvements to IR peephole optimizer

This commit is contained in:
Irmen de Jong 2023-05-01 23:00:51 +02:00
parent ce00e49a89
commit f465b2e2a0
14 changed files with 156 additions and 126 deletions

View File

@ -54,23 +54,8 @@ class IRCodeGen(
ensureFirstChunkLabels(irProg) ensureFirstChunkLabels(irProg)
irProg.linkChunks() irProg.linkChunks()
if(options.optimize) { val optimizer = IRPeepholeOptimizer(irProg)
val optimizer = IRPeepholeOptimizer(irProg) optimizer.optimize(options.optimize, errors)
optimizer.optimize()
val remover = IRUnusedCodeRemover(irProg, irSymbolTable, errors)
do {
val numRemoved = remover.optimize()
} while(numRemoved>0 && errors.noErrors())
errors.report()
irProg.linkChunks() // re-link
} else {
val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimizeOnlyJoinChunks()
}
irProg.validate() irProg.validate()
return irProg return irProg
} }
@ -1470,7 +1455,7 @@ class IRCodeGen(
} }
private fun translate(block: PtBlock): IRBlock { private fun translate(block: PtBlock): IRBlock {
val irBlock = IRBlock(block.name, block.address, translate(block.alignment), block.position) // no use for other attributes yet? val irBlock = IRBlock(block.name, block.address, block.library, block.forceOutput, translate(block.alignment), block.position) // no use for other attributes yet?
for (child in block.children) { for (child in block.children) {
when(child) { when(child) {
is PtNop -> { /* nothing */ } is PtNop -> { /* nothing */ }

View File

@ -1,9 +1,28 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.* import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) { class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimizeOnlyJoinChunks() { fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
if(!optimizationsEnabled)
return optimizeOnlyJoinChunks()
peepholeOptimize()
val remover = IRUnusedCodeRemover(irprog, errors)
var totalRemovals = 0
do {
val numRemoved = remover.optimize()
totalRemovals += numRemoved
} while(numRemoved>0 && errors.noErrors())
errors.report()
if(totalRemovals>0) {
irprog.linkChunks() // re-link again.
}
}
private fun optimizeOnlyJoinChunks() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
@ -11,7 +30,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
fun optimize() { private fun peepholeOptimize() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
@ -36,7 +55,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
removeEmptyChunks(sub) removeEmptyChunks(sub)
} }
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
private fun removeEmptyChunks(sub: IRSubroutine) { private fun removeEmptyChunks(sub: IRSubroutine) {

View File

@ -5,14 +5,27 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.intermediate.* import prog8.intermediate.*
internal class IRUnusedCodeRemover( class IRUnusedCodeRemover(
private val irprog: IRProgram, private val irprog: IRProgram,
private val symbolTable: IRSymbolTable,
private val errors: IErrorReporter private val errors: IErrorReporter
) { ) {
fun optimize(): Int { fun optimize(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>() var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
// remove empty blocks
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
irprog.st.removeTree(block.label)
numRemoved++
}
}
return numRemoved
}
private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk -> sub.chunks.forEach { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk } chunk.label?.let { allLabeledChunks[it] = chunk }
@ -20,8 +33,6 @@ internal class IRUnusedCodeRemover(
} }
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks) var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
// remove empty subs
irprog.blocks.forEach { block -> irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub -> block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) { if(sub.isEmpty()) {
@ -29,24 +40,82 @@ internal class IRUnusedCodeRemover(
errors.warn("unused subroutine ${sub.label}", sub.position) errors.warn("unused subroutine ${sub.label}", sub.position)
} }
block.children.remove(sub) block.children.remove(sub)
symbolTable.removeTree(sub.label) irprog.st.removeTree(sub.label)
numRemoved++ numRemoved++
} }
} }
} }
// remove empty blocks return numRemoved
irprog.blocks.reversed().forEach { block -> }
if(block.isEmpty()) {
irprog.blocks.remove(block) private fun removeUnusedAsmSubroutines(): Int {
symbolTable.removeTree(block.label) val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
numRemoved++ .associateBy { it.label }
var numRemoved = removeSimpleUnlinkedAsmsubs(allLabeledAsmsubs)
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
}
} }
} }
return numRemoved return numRemoved
} }
private fun removeSimpleUnlinkedAsmsubs(allSubs: Map<String, IRAsmSubroutine>): Int {
val linkedAsmSubs = mutableSetOf<IRAsmSubroutine>()
// TODO: asmsubs in library modules are never removed, we can't really tell here if they're actually being called or not...
// check if asmsub is called from another asmsub
irprog.blocks.asSequence().forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
require(sub.asmChunk.next == null) { "asmsubs won't be pointing to their successor, otherwise we should do more work here" }
if (block.forceOutput || block.library)
linkedAsmSubs += sub
if (sub.asmChunk.isNotEmpty()) {
allSubs.forEach { (label, asmsub) ->
if (sub.asmChunk.assembly.contains(label))
linkedAsmSubs += asmsub
}
}
}
}
// check if asmsub is linked or called from another regular subroutine
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.instructions.forEach {
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
}
}
}
return removeUnlinkedAsmsubs(linkedAsmSubs)
}
private fun removeUnlinkedAsmsubs(linkedAsmSubs: Set<IRAsmSubroutine>): Int {
var numRemoved = 0
irprog.blocks.asSequence().forEach { block ->
block.children.withIndex().reversed().forEach { (index, child) ->
if(child is IRAsmSubroutine && child !in linkedAsmSubs) {
block.children.removeAt(index)
numRemoved++
}
}
}
return numRemoved
}
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int { private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" } val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first()) val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
@ -98,7 +167,7 @@ internal class IRUnusedCodeRemover(
} }
private fun removeUnlinkedChunks( private fun removeUnlinkedChunks(
linkedChunks: MutableSet<IRCodeChunkBase> linkedChunks: Set<IRCodeChunkBase>
): Int { ): Int {
var numRemoved = 0 var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->

View File

@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({ class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram { fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start") require(chunks.first().label=="main.start")
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it } chunks.forEach { sub += it }
block += sub block += sub
@ -46,7 +46,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 3 irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 1 irProg.chunks().single().instructions.size shouldBe 1
} }
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().size shouldBe 4 irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5 irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 4 irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start" irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label" irProg.chunks()[1].label shouldBe "label"
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 6 irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 1 instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.CLC instr[0].opcode shouldBe Opcode.CLC
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 1 instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.LOADR instr[0].opcode shouldBe Opcode.LOADR
@ -130,7 +130,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 10 irProg.chunks().single().instructions.size shouldBe 10
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
} }
@ -141,7 +141,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 2 irProg.chunks().single().instructions.size shouldBe 2
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 2 instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.INC instr[0].opcode shouldBe Opcode.INC
@ -161,7 +161,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 8 irProg.chunks().single().instructions.size shouldBe 8
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
} }
@ -174,7 +174,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 4 instr.size shouldBe 4
instr[0].opcode shouldBe Opcode.LOAD instr[0].opcode shouldBe Opcode.LOAD

View File

@ -1 +1 @@
8.12 8.13-dev

View File

@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS> <INITGLOBALS>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]"> <BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK> </BLOCK>
</PROGRAM> </PROGRAM>
""") """)

View File

@ -8,36 +8,9 @@ For next minor release
... ...
For 9.0 major changes For 9.0 major changes are being made in the "version_9" branch. Look there.
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- rename builtin function sqrt16 to just sqrt
- copy (not move) the CBM kernal romsubs to a new 'cbm' block so programs on c128 and cx16 can also
simply refer to cbm.CHROUT rather than c64.CHROUT which looks a bit silly on the non-c64 cbm systems.
we keep the old definitions intact because of backwards compatibility reasons.
- try to reintroduce builtin functions max/maxw/min/minw that take 2 args and return the largest/smallest of them.
This is a major change because it will likely break existing code that is now using min and max as variable names.
Also add optimization that changes the word variant to byte variant if the operands are bytes.
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
It is documented behavior to now loop 'around' $00 but it's too easy to forget about!
Lot of work because of so many special cases in ForLoopsAsmgen.....
(vm codegen already behaves like this!)
- ?? get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8.
the large majority of users will only deal with a single disk drive so why not make it easier for them.
But see complaint on github https://github.com/irmen/prog8/issues/106
- duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation
- get f_seek_w working like in the BASIC program - this needs the changes to diskio.f_open to use suffixes ,p,m
- add special (u)word array type (or modifier such as @fast? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
So maybe only allow the bare essentials? (store, get, bitwise operations?)
- Some more support for (64tass) SEGMENTS ?
- (What, how, isn't current BSS support enough?)
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
- maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area?
- maybe or may not needed: the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
just initialize them yourself in start() if you need a non-zero value .
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables)
Need help with Need help with

View File

@ -4,25 +4,16 @@
main { main {
sub start() { sub start() {
byte v1s = 22 txt.print("hello")
byte v2s = -99 ; foobar()
word ww
txt.print_w(minsb()) ; TODO WRONG RESULT!
txt.spc()
ww = minsb()
txt.print_w(ww)
txt.spc()
txt.print_b(minsb())
txt.spc()
v2s = minsb()
txt.print_w(v2s)
sub minsb() -> byte {
cx16.r0++
return v2s
}
} }
asmsub foobar() {
%asm {{
nop
rts
}}
}
} }

View File

@ -336,6 +336,8 @@ class IRFileReader {
val block = IRBlock( val block = IRBlock(
attrs.getValue("NAME"), attrs.getValue("NAME"),
if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(),
if(attrs.getValue("LIBRARY")=="") false else attrs.getValue("LIBRARY").toBoolean(),
if(attrs.getValue("FORCEOUTPUT")=="") false else attrs.getValue("FORCEOUTPUT").toBoolean(),
IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")), IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")),
parsePosition(attrs.getValue("POS"))) parsePosition(attrs.getValue("POS")))
skipText(reader) skipText(reader)

View File

@ -57,6 +57,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeStartElement("BLOCK") xml.writeStartElement("BLOCK")
xml.writeAttribute("NAME", block.label) xml.writeAttribute("NAME", block.label)
xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "") xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "")
xml.writeAttribute("LIBRARY", block.library.toString())
xml.writeAttribute("FORCEOUTPUT", block.forceOutput.toString())
xml.writeAttribute("ALIGN", block.alignment.toString()) xml.writeAttribute("ALIGN", block.alignment.toString())
xml.writeAttribute("POS", block.position.toString()) xml.writeAttribute("POS", block.position.toString())
xml.writeCharacters("\n") xml.writeCharacters("\n")

View File

@ -23,7 +23,6 @@ Currently ther is NO support for 24 or 32 bits integers.
There is no distinction between signed and unsigned integers. There is no distinction between signed and unsigned integers.
Instead, a different instruction is used if a distinction should be made (for example div and divs). Instead, a different instruction is used if a distinction should be made (for example div and divs).
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions. Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
Instructions taking more than 1 register cannot take the same register multiple times! (to avoid confusing different datatypes)
LOAD/STORE LOAD/STORE
@ -65,29 +64,29 @@ BRANCHING and CONDITIONALS
-------------------------- --------------------------
All have type b or w except the branches that only check status bits. All have type b or w except the branches that only check status bits.
bstcc address - branch to location if Status bit Carry is Clear bstcc address - branch to location if Status bit Carry is clear
bstcs address - branch to location if Status bit Carry is Set bstcs address - branch to location if Status bit Carry is set
bstne address - branch to location if Status bit Zero is clear
bsteq address - branch to location if Status bit Zero is set bsteq address - branch to location if Status bit Zero is set
bstne address - branch to location if Status bit Zero is not set bstpos address - branch to location if Status bit Negative is clear
bstneg address - branch to location if Status bit Negative is not set bstneg address - branch to location if Status bit Negative is set
bstpos address - branch to location if Status bit Negative is not set bstvc address - branch to location if Status bit Overflow is clear
bstvc address - branch to location if Status bit Overflow is not set bstvs address - branch to location if Status bit Overflow is set
bstvs address - branch to location if Status bit Overflow is not set
beqr reg1, reg2, address - jump to location in program given by location, if reg1 == reg2 beqr reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
beq reg1, value, address - jump to location in program given by location, if reg1 == immediate value beq reg1, value, address - jump to location in program given by location, if reg1 == immediate value
bner reg1, reg2, address - jump to location in program given by location, if reg1 != reg2 bner reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
bne reg1, value, address - jump to location in program given by location, if reg1 != immediate value bne reg1, value, address - jump to location in program given by location, if reg1 != immediate value
bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned) bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned)
blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed) bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed)
bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed) blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed)
bger reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned) bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned)
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
bges reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (signed) bges reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (signed)
bger reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed) bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed)
( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.) ( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.)
sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0 sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0
@ -727,18 +726,6 @@ data class IRInstruction(
fpReg1direction = format.fpReg1 fpReg1direction = format.fpReg1
fpReg2direction = format.fpReg2 fpReg2direction = format.fpReg2
if(opcode in setOf(Opcode.BEQR, Opcode.BNER,
Opcode.BGTR, Opcode.BGTSR,
Opcode.BGER, Opcode.BGESR,
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
Opcode.SGE, Opcode.SGES)) {
if(type==IRDataType.FLOAT)
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
else
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
}
if(opcode==Opcode.SYSCALL) { if(opcode==Opcode.SYSCALL) {
require(immediate!=null) { require(immediate!=null) {
"syscall needs immediate integer for the syscall number" "syscall needs immediate integer for the syscall number"

View File

@ -240,6 +240,8 @@ class IRProgram(val name: String,
class IRBlock( class IRBlock(
val label: String, val label: String,
val address: UInt?, val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val alignment: BlockAlignment, val alignment: BlockAlignment,
val position: Position val position: Position
) { ) {

View File

@ -75,7 +75,7 @@ load.b r1,42
</CODE> </CODE>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]"> <BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
<SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]"> <SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
<PARAMS> <PARAMS>
</PARAMS> </PARAMS>
@ -85,7 +85,7 @@ return
</SUB> </SUB>
</BLOCK> </BLOCK>
<BLOCK NAME="sys" ADDRESS="" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]"> <BLOCK NAME="sys" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
<SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]"> <SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
<PARAMS> <PARAMS>
uword sys.wait.jiffies uword sys.wait.jiffies

View File

@ -43,7 +43,7 @@ class TestVm: FunSpec( {
test("vm execution: modify memory") { test("vm execution: modify memory") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null) val code = IRCodeChunk(startSub.label, null)
code += IRInstruction(Opcode.NOP) code += IRInstruction(Opcode.NOP)
@ -72,7 +72,7 @@ class TestVm: FunSpec( {
test("vm asmbinary not supported") { test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null) val code = IRCodeChunk(startSub.label, null)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u)) code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
@ -88,7 +88,7 @@ class TestVm: FunSpec( {
test("asmsub not supported in vm even with IR") { test("asmsub not supported in vm even with IR") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine( val startSub = IRAsmSubroutine(
"main.asmstart", "main.asmstart",
0x2000u, 0x2000u,
@ -129,7 +129,7 @@ class TestVm: FunSpec( {
<INITGLOBALS> <INITGLOBALS>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]"> <BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK> </BLOCK>
</PROGRAM> </PROGRAM>
""" """