mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
some improvements to IR peephole optimizer
This commit is contained in:
parent
ce00e49a89
commit
f465b2e2a0
@ -54,23 +54,8 @@ class IRCodeGen(
|
||||
ensureFirstChunkLabels(irProg)
|
||||
irProg.linkChunks()
|
||||
|
||||
if(options.optimize) {
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
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()
|
||||
}
|
||||
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
return irProg
|
||||
}
|
||||
@ -1470,7 +1455,7 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
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) {
|
||||
when(child) {
|
||||
is PtNop -> { /* nothing */ }
|
||||
|
@ -1,9 +1,28 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
fun optimizeOnlyJoinChunks() {
|
||||
class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
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 ->
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
@ -11,7 +30,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
fun optimize() {
|
||||
private fun peepholeOptimize() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
@ -36,7 +55,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
removeEmptyChunks(sub)
|
||||
}
|
||||
|
||||
irprog.linkChunks() // re-link
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
private fun removeEmptyChunks(sub: IRSubroutine) {
|
||||
|
@ -5,14 +5,27 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
internal class IRUnusedCodeRemover(
|
||||
class IRUnusedCodeRemover(
|
||||
private val irprog: IRProgram,
|
||||
private val symbolTable: IRSymbolTable,
|
||||
private val errors: IErrorReporter
|
||||
) {
|
||||
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 ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||
@ -20,8 +33,6 @@ internal class IRUnusedCodeRemover(
|
||||
}
|
||||
|
||||
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
||||
|
||||
// remove empty subs
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
@ -29,24 +40,82 @@ internal class IRUnusedCodeRemover(
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
symbolTable.removeTree(sub.label)
|
||||
irprog.st.removeTree(sub.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty blocks
|
||||
irprog.blocks.reversed().forEach { block ->
|
||||
if(block.isEmpty()) {
|
||||
irprog.blocks.remove(block)
|
||||
symbolTable.removeTree(block.label)
|
||||
numRemoved++
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
private fun removeUnusedAsmSubroutines(): Int {
|
||||
val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
|
||||
.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
|
||||
}
|
||||
|
||||
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 {
|
||||
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())
|
||||
@ -98,7 +167,7 @@ internal class IRUnusedCodeRemover(
|
||||
}
|
||||
|
||||
private fun removeUnlinkedChunks(
|
||||
linkedChunks: MutableSet<IRCodeChunkBase>
|
||||
linkedChunks: Set<IRCodeChunkBase>
|
||||
): Int {
|
||||
var numRemoved = 0
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
|
@ -8,7 +8,7 @@ import prog8.intermediate.*
|
||||
class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
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)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
@ -46,7 +46,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 3
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().size shouldBe 4
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 4
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label"
|
||||
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 6
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 1
|
||||
instr[0].opcode shouldBe Opcode.CLC
|
||||
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 1
|
||||
instr[0].opcode shouldBe Opcode.LOADR
|
||||
@ -130,7 +130,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 10
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 2
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.INC
|
||||
@ -161,7 +161,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 8
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 4
|
||||
instr[0].opcode shouldBe Opcode.LOAD
|
||||
|
@ -1 +1 @@
|
||||
8.12
|
||||
8.13-dev
|
||||
|
@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
|
||||
<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>
|
||||
</PROGRAM>
|
||||
""")
|
||||
|
@ -8,36 +8,9 @@ For next minor release
|
||||
...
|
||||
|
||||
|
||||
For 9.0 major changes
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
- 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)
|
||||
For 9.0 major changes are being made in the "version_9" branch. Look there.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
|
||||
Need help with
|
||||
|
@ -4,25 +4,16 @@
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
byte v1s = 22
|
||||
byte v2s = -99
|
||||
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
|
||||
}
|
||||
txt.print("hello")
|
||||
; foobar()
|
||||
}
|
||||
|
||||
asmsub foobar() {
|
||||
%asm {{
|
||||
nop
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,6 +336,8 @@ class IRFileReader {
|
||||
val block = IRBlock(
|
||||
attrs.getValue("NAME"),
|
||||
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")),
|
||||
parsePosition(attrs.getValue("POS")))
|
||||
skipText(reader)
|
||||
|
@ -57,6 +57,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
xml.writeStartElement("BLOCK")
|
||||
xml.writeAttribute("NAME", block.label)
|
||||
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("POS", block.position.toString())
|
||||
xml.writeCharacters("\n")
|
||||
|
@ -23,7 +23,6 @@ Currently ther is NO support for 24 or 32 bits 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).
|
||||
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
|
||||
@ -65,29 +64,29 @@ BRANCHING and CONDITIONALS
|
||||
--------------------------
|
||||
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
|
||||
bstcs address - branch to location if Status bit Carry is Set
|
||||
bstcc address - branch to location if Status bit Carry is clear
|
||||
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
|
||||
bstne address - branch to location if Status bit Zero is not set
|
||||
bstneg address - branch to location if Status bit Negative is not set
|
||||
bstpos address - branch to location if Status bit Negative is not set
|
||||
bstvc address - branch to location if Status bit Overflow is not set
|
||||
bstvs address - branch to location if Status bit Overflow is not set
|
||||
bstpos address - branch to location if Status bit Negative is clear
|
||||
bstneg address - branch to location if Status bit Negative is set
|
||||
bstvc address - branch to location if Status bit Overflow is clear
|
||||
bstvs address - branch to location if Status bit Overflow is set
|
||||
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
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
( 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
|
||||
@ -727,18 +726,6 @@ data class IRInstruction(
|
||||
fpReg1direction = format.fpReg1
|
||||
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) {
|
||||
require(immediate!=null) {
|
||||
"syscall needs immediate integer for the syscall number"
|
||||
|
@ -240,6 +240,8 @@ class IRProgram(val name: String,
|
||||
class IRBlock(
|
||||
val label: String,
|
||||
val address: UInt?,
|
||||
val library: Boolean,
|
||||
val forceOutput: Boolean,
|
||||
val alignment: BlockAlignment,
|
||||
val position: Position
|
||||
) {
|
||||
|
@ -75,7 +75,7 @@ load.b r1,42
|
||||
</CODE>
|
||||
</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]">
|
||||
<PARAMS>
|
||||
</PARAMS>
|
||||
@ -85,7 +85,7 @@ return
|
||||
</SUB>
|
||||
</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]">
|
||||
<PARAMS>
|
||||
uword sys.wait.jiffies
|
||||
|
@ -43,7 +43,7 @@ class TestVm: FunSpec( {
|
||||
|
||||
test("vm execution: modify memory") {
|
||||
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 code = IRCodeChunk(startSub.label, null)
|
||||
code += IRInstruction(Opcode.NOP)
|
||||
@ -72,7 +72,7 @@ class TestVm: FunSpec( {
|
||||
|
||||
test("vm asmbinary not supported") {
|
||||
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 code = IRCodeChunk(startSub.label, null)
|
||||
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") {
|
||||
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(
|
||||
"main.asmstart",
|
||||
0x2000u,
|
||||
@ -129,7 +129,7 @@ class TestVm: FunSpec( {
|
||||
<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>
|
||||
</PROGRAM>
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user