This commit is contained in:
Irmen de Jong 2022-10-25 22:57:04 +02:00
parent 585009ac5c
commit cfa7258ff4
10 changed files with 55 additions and 47 deletions

View File

@ -78,22 +78,20 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun joinChunks(sub: IRSubroutine) { private fun joinChunks(sub: IRSubroutine) {
/* // Subroutine contains a list of chunks. Some can be joined into one.
Subroutine contains a list of chunks.
Some can be joined into one.
TODO: this has to be changed later...
*/
if(sub.chunks.isEmpty()) if(sub.chunks.isEmpty())
return return
/*
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) { if(previous is IRCodeChunk && chunk is IRCodeChunk) {
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
val lastInstruction = previous.instructions.lastOrNull()
if(lastInstruction!=null)
return lastInstruction.opcode !in OpcodesThatJump
return true return true
// TODO: only if all instructions are non-branching, allow it to join?
// return !chunk.lines.filterIsInstance<IRInstruction>().any {it.opcode in OpcodesThatBranch }
} }
return false return false
} }
@ -107,7 +105,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[ix] chunks += sub.chunks[ix]
} }
sub.chunks.clear() sub.chunks.clear()
sub.chunks += chunks*/ sub.chunks += chunks
} }
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
@ -174,7 +172,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
// remove useless RETURN // remove useless RETURN
if(ins.opcode == Opcode.RETURN && idx>0) { if(ins.opcode == Opcode.RETURN && idx>0) {
val previous = chunk.instructions[idx-1] as? IRInstruction val previous = chunk.instructions[idx-1] as? IRInstruction
if(previous?.opcode in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) { if(previous?.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }

View File

@ -252,16 +252,8 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(inlineAssembly: InlineAssembly) { override fun visit(inlineAssembly: InlineAssembly) {
val assembly = inlineAssembly.assembly if(inlineAssembly.hasReturnOrRts(compilerOptions.compTarget))
if(compilerOptions.compTarget.name!=VMTarget.NAME) { count++
if (" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly
)
count++
} else {
if(" return" in assembly || "\treturn" in assembly || " jump" in assembly || "\tjump" in assembly)
count++
}
} }
} }

View File

@ -12,7 +12,6 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -170,15 +169,8 @@ internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolea
} }
internal fun Subroutine.hasRtsInAsm(compTarget: ICompilationTarget): Boolean { internal fun Subroutine.hasRtsInAsm(compTarget: ICompilationTarget): Boolean {
val instructions =
if(compTarget.name == VMTarget.NAME)
listOf(" return", "\treturn", " jump", "\tjump")
else
listOf(" rti", "\trti", " rts", "\trts", " jmp", "\tjmp", " bra", "\tbra")
return statements return statements
.asSequence() .asSequence()
.filterIsInstance<InlineAssembly>() .filterIsInstance<InlineAssembly>()
.any { .any { it.hasReturnOrRts(compTarget) }
instructions.any { instr->instr in it.assembly }
}
} }

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
interface INamedStatement { interface INamedStatement {
@ -629,6 +630,15 @@ class InlineAssembly(val assembly: String, val isIR: Boolean, override val posit
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
fun hasReturnOrRts(target: ICompilationTarget): Boolean {
return if(target.name!= VMTarget.NAME) {
" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly
} else {
" return" in assembly || "\treturn" in assembly || " jump" in assembly || "\tjump" in assembly || " jumpa" in assembly || "\tjumpa" in assembly
}
}
val names: Set<String> by lazy { val names: Set<String> by lazy {
// A cache of all the words (identifiers) present in this block of assembly code // A cache of all the words (identifiers) present in this block of assembly code

View File

@ -3,14 +3,12 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- ir: fix linkChunks() in IRProgram and addAssemblyToProgram() next pointer tracking
- vm: program is list of chunks, fix dispatcher - vm: program is list of chunks, fix dispatcher
- maybe?: make sure last %ir chunk in a subroutine ends with a jump or a return instruction
- ir: fix unit tests - ir: fix unit tests
- ir: fix joinChunks() in the IR optimizer - there are WAY too many chunks with 1 instruction in them only
- ir: fix removeWeirdBranches in IR optimizer - ir: fix removeWeirdBranches in IR optimizer
- ir: next in IRCodeChunk can also be a Asm Chunk which can have next as well - ir: next in IRCodeChunk can also be a Asm Chunk which can have next as well
- add cx16diskio.vload_raw() to load headerless files into vram - update diagram in technical.rst?
- mention the syntax highlighting files in the readme and the docs, and add note to the IDEA one that it can also be used in Rider
... ...

View File

@ -1,11 +1,16 @@
%import textio ; %import textio
main { main {
sub start() { sub start() {
ubyte aa = 42 ; should get a return after the nop
ubyte bb = 99 %ir {{
aa += bb nop
txt.print_ub(aa) }}
; ubyte aa = 42
; ubyte bb = 99
; aa += bb
; txt.print_ub(aa)
} }
} }

View File

@ -353,6 +353,12 @@ enum class Opcode {
BINARYDATA BINARYDATA
} }
val OpcodesThatJump = setOf(
Opcode.JUMP,
Opcode.JUMPA,
Opcode.RETURN
)
val OpcodesThatBranch = setOf( val OpcodesThatBranch = setOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPA, Opcode.JUMPA,

View File

@ -93,18 +93,22 @@ class IRProgram(val name: String,
} }
blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().forEach { (index, chunk) -> sub.chunks.withIndex().forEach { (index, chunk) ->
fun nextChunk(): IRCodeChunkBase? = if(index<sub.chunks.size-1) sub.chunks[index + 1] else null
when (chunk) { when (chunk) {
is IRCodeChunk -> { is IRCodeChunk -> {
// link sequential chunks // link sequential chunks
val jump = chunk.instructions.lastOrNull()?.opcode val jump = chunk.instructions.lastOrNull()?.opcode
if (jump == null || jump !in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) { if (jump == null || jump !in OpcodesThatJump) {
// no jump at the end, so link to next chunk (if it exists) // no jump at the end, so link to next chunk (if it exists)
if(index<sub.chunks.size-1) { val next = nextChunk()
val nextChunk = sub.chunks[index + 1] if(next!=null) {
if (nextChunk is IRCodeChunk) if (next is IRCodeChunk)
chunk.next = nextChunk chunk.next = next
else else
throw AssemblyError("code chunk flows into following non-code chunk") throw AssemblyError("code chunk flows into following non-code chunk")
} else { } else {
@ -125,7 +129,10 @@ class IRProgram(val name: String,
} }
} }
is IRInlineAsmChunk -> { is IRInlineAsmChunk -> {
// TODO("link next of asm chunk") val next = nextChunk()
if(next!=null) {
// TODO if chunk doesn't end in a jump or return statement, flow continues into the next chunk
}
} }
is IRInlineBinaryChunk -> { is IRInlineBinaryChunk -> {
// TODO("link next of binary chunk") // TODO("link next of binary chunk")

View File

@ -136,7 +136,7 @@ class VirtualMachine(irProgram: IRProgram) {
private fun dispatch(ins: IRInstruction) { private fun dispatch(ins: IRInstruction) {
when(ins.opcode) { when(ins.opcode) {
Opcode.NOP -> { nextPc() } Opcode.NOP -> nextPc()
Opcode.LOAD -> InsLOAD(ins) Opcode.LOAD -> InsLOAD(ins)
Opcode.LOADM -> InsLOADM(ins) Opcode.LOADM -> InsLOADM(ins)
Opcode.LOADX -> InsLOADX(ins) Opcode.LOADX -> InsLOADX(ins)

View File

@ -272,7 +272,7 @@ class VmProgramLoader {
symbolAddresses: MutableMap<String, Int>, symbolAddresses: MutableMap<String, Int>,
): Pair<IRCodeChunkBase, IRCodeChunk> { ): Pair<IRCodeChunkBase, IRCodeChunk> {
if(asmChunk.isIR) { if(asmChunk.isIR) {
val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null) val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null) // TODO keep track of the chunk's next pointer
asmChunk.assembly.lineSequence().forEach { asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders) val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders)
parsed.fold( parsed.fold(