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) {
/*
Subroutine contains a list of chunks.
Some can be joined into one.
TODO: this has to be changed later...
*/
// Subroutine contains a list of chunks. Some can be joined into one.
if(sub.chunks.isEmpty())
return
/*
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
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
// TODO: only if all instructions are non-branching, allow it to join?
// return !chunk.lines.filterIsInstance<IRInstruction>().any {it.opcode in OpcodesThatBranch }
}
return false
}
@ -107,7 +105,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks*/
sub.chunks += chunks
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
@ -174,7 +172,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
// remove useless RETURN
if(ins.opcode == Opcode.RETURN && idx>0) {
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)
changed = true
}

View File

@ -252,16 +252,8 @@ internal class AstChecker(private val program: Program,
}
override fun visit(inlineAssembly: InlineAssembly) {
val assembly = inlineAssembly.assembly
if(compilerOptions.compTarget.name!=VMTarget.NAME) {
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++
}
if(inlineAssembly.hasReturnOrRts(compilerOptions.compTarget))
count++
}
}

View File

@ -12,7 +12,6 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.VMTarget
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 {
val instructions =
if(compTarget.name == VMTarget.NAME)
listOf(" return", "\treturn", " jump", "\tjump")
else
listOf(" rti", "\trti", " rts", "\trts", " jmp", "\tjmp", " bra", "\tbra")
return statements
.asSequence()
.filterIsInstance<InlineAssembly>()
.any {
instructions.any { instr->instr in it.assembly }
}
.any { it.hasReturnOrRts(compTarget) }
}

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.target.VMTarget
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: 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 {
// A cache of all the words (identifiers) present in this block of assembly code

View File

@ -3,14 +3,12 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ir: fix linkChunks() in IRProgram and addAssemblyToProgram() next pointer tracking
- 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 joinChunks() in the IR optimizer - there are WAY too many chunks with 1 instruction in them only
- ir: fix removeWeirdBranches in IR optimizer
- 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
- 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
- update diagram in technical.rst?
...

View File

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

View File

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

View File

@ -93,18 +93,22 @@ class IRProgram(val name: String,
}
blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().forEach { (index, chunk) ->
fun nextChunk(): IRCodeChunkBase? = if(index<sub.chunks.size-1) sub.chunks[index + 1] else null
when (chunk) {
is IRCodeChunk -> {
// link sequential chunks
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)
if(index<sub.chunks.size-1) {
val nextChunk = sub.chunks[index + 1]
if (nextChunk is IRCodeChunk)
chunk.next = nextChunk
val next = nextChunk()
if(next!=null) {
if (next is IRCodeChunk)
chunk.next = next
else
throw AssemblyError("code chunk flows into following non-code chunk")
} else {
@ -125,7 +129,10 @@ class IRProgram(val name: String,
}
}
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 -> {
// TODO("link next of binary chunk")

View File

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

View File

@ -272,7 +272,7 @@ class VmProgramLoader {
symbolAddresses: MutableMap<String, Int>,
): Pair<IRCodeChunkBase, IRCodeChunk> {
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 {
val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders)
parsed.fold(