prog8/virtualmachine/src/prog8/vm/VmProgramLoader.kt

327 lines
15 KiB
Kotlin
Raw Normal View History

2022-09-26 17:03:54 +00:00
package prog8.vm
import prog8.code.core.ArrayDatatypes
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
2022-09-26 17:03:54 +00:00
import prog8.intermediate.*
class VmProgramLoader {
2022-10-16 16:30:14 +00:00
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
private val subroutines = mutableMapOf<String, IRSubroutine>() // label to subroutine node
2022-09-26 17:03:54 +00:00
2022-10-16 16:30:14 +00:00
fun load(irProgram: IRProgram, memory: Memory): List<IRCodeChunk> {
irProgram.validate()
placeholders.clear()
subroutines.clear()
2022-09-26 17:03:54 +00:00
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val variableAddresses = allocations.allocations.toMutableMap()
2022-10-16 16:30:14 +00:00
val programChunks = mutableListOf<IRCodeChunk>()
2022-09-26 17:03:54 +00:00
varsToMemory(irProgram, allocations, variableAddresses, memory)
if(irProgram.globalInits.isNotEmpty())
programChunks += irProgram.globalInits
2022-09-26 17:03:54 +00:00
// make sure that if there is a "main.start" entrypoint, we jump to it
irProgram.blocks.firstOrNull()?.let {
2022-11-22 01:04:24 +00:00
if(it.children.any { sub -> sub is IRSubroutine && sub.label=="main.start" }) {
2023-03-12 15:09:55 +00:00
val previous = programChunks.lastOrNull()
val chunk = IRCodeChunk(null, previous)
2022-10-16 16:30:14 +00:00
placeholders[Pair(chunk, 0)] = "main.start"
chunk += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
2023-03-12 15:09:55 +00:00
previous?.let { p -> p.next = chunk }
2022-10-16 16:30:14 +00:00
programChunks += chunk
2022-09-26 17:03:54 +00:00
}
}
// load rest of the program into the list
2022-10-23 23:57:37 +00:00
val chunkReplacements = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunk>>()
2022-09-26 17:03:54 +00:00
irProgram.blocks.forEach { block ->
if(block.address!=null)
2023-04-09 21:09:30 +00:00
throw IRParseException("blocks cannot have a load address for vm: ${block.label}")
2022-09-26 17:03:54 +00:00
2022-11-22 01:04:24 +00:00
block.children.forEach { child ->
when(child) {
is IRAsmSubroutine -> throw IRParseException("vm does not support asmsubs (use normal sub): ${child.label}")
2022-11-22 01:04:24 +00:00
is IRCodeChunk -> programChunks += child
is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
2022-11-22 01:04:24 +00:00
is IRSubroutine -> {
subroutines[child.label] = child
2022-11-22 01:04:24 +00:00
child.chunks.forEach { chunk ->
when (chunk) {
is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
2022-11-22 01:04:24 +00:00
is IRCodeChunk -> programChunks += chunk
else -> throw AssemblyError("weird chunk type")
}
}
}
}
}
}
2022-10-23 23:57:37 +00:00
pass2translateSyscalls(programChunks)
pass2replaceLabelsByProgIndex(programChunks, variableAddresses, subroutines)
2022-10-23 23:57:37 +00:00
phase2relinkReplacedChunks(chunkReplacements, programChunks)
programChunks.forEach {
it.instructions.forEach { ins ->
2023-05-14 14:48:48 +00:00
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
2023-04-09 13:06:40 +00:00
require(ins.address != null) { "instruction with labelSymbol for a var should have value set to the memory address" }
2022-10-23 23:57:37 +00:00
}
}
2022-10-16 16:30:14 +00:00
return programChunks
}
2022-10-23 23:57:37 +00:00
private fun phase2relinkReplacedChunks(
replacements: MutableList<Pair<IRCodeChunkBase, IRCodeChunk>>,
programChunks: MutableList<IRCodeChunk>
) {
replacements.forEach { (old, new) ->
programChunks.forEach { chunk ->
if(chunk.next === old) {
chunk.next = new
}
chunk.instructions.forEach { ins ->
if(ins.branchTarget === old) {
ins.branchTarget = new
} else if(ins.branchTarget==null && ins.labelSymbol==new.label) {
ins.branchTarget = new
}
}
}
}
}
private fun pass2translateSyscalls(chunks: MutableList<IRCodeChunk>) {
chunks.forEach { chunk ->
chunk.instructions.withIndex().forEach { (index, ins) ->
if(ins.opcode == Opcode.SYSCALL) {
2022-10-23 23:57:37 +00:00
// convert IR Syscall to VM Syscall
2023-04-09 13:06:40 +00:00
val vmSyscall = when(ins.immediate!!) {
2022-10-23 23:57:37 +00:00
IMSyscall.SORT_UBYTE.number -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.number -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.number -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.number -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.number -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.number -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.number -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.number -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.number -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.number -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.number -> Syscall.REVERSE_FLOATS
IMSyscall.COMPARE_STRINGS.number -> Syscall.COMPARE_STRINGS
IMSyscall.STRING_CONTAINS.number -> Syscall.STRING_CONTAINS
IMSyscall.BYTEARRAY_CONTAINS.number -> Syscall.BYTEARRAY_CONTAINS
IMSyscall.WORDARRAY_CONTAINS.number -> Syscall.WORDARRAY_CONTAINS
2022-10-23 23:57:37 +00:00
else -> null
}
if(vmSyscall!=null)
2023-04-09 13:06:40 +00:00
chunk.instructions[index] = ins.copy(immediate = vmSyscall.ordinal)
2022-10-23 23:57:37 +00:00
}
val label = ins.labelSymbol
if (label != null && ins.opcode !in OpcodesThatBranch) {
placeholders[Pair(chunk, index)] = label
}
}
}
}
2022-10-16 16:30:14 +00:00
private fun pass2replaceLabelsByProgIndex(
chunks: MutableList<IRCodeChunk>,
variableAddresses: MutableMap<String, Int>,
subroutines: MutableMap<String, IRSubroutine>
2022-10-16 16:30:14 +00:00
) {
for((ref, label) in placeholders) {
val (chunk, line) = ref
val replacement = variableAddresses[label]
2022-10-16 16:30:14 +00:00
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = variableAddresses.getValue(symbol) + index
2023-04-09 13:06:40 +00:00
chunk.instructions[line] = chunk.instructions[line].copy(address = address)
2022-10-16 16:30:14 +00:00
} else {
// placeholder is not a variable, so it must be a label of a code chunk instead
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
val opcode = chunk.instructions[line].opcode
2023-05-14 14:48:48 +00:00
if(target==null)
throw IRParseException("placeholder not found in variables nor labels: $label")
else if(opcode in OpcodesThatBranch)
2023-04-09 13:06:40 +00:00
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
2023-05-14 14:48:48 +00:00
else
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO
2022-10-16 16:30:14 +00:00
}
} else {
2023-04-09 13:06:40 +00:00
chunk.instructions[line] = chunk.instructions[line].copy(address = replacement)
2022-10-16 16:30:14 +00:00
}
}
subroutines.forEach {
it.value.chunks.forEach { chunk ->
chunk.instructions.withIndex().forEach { (index, ins) ->
if(ins.opcode==Opcode.CALL) {
val fcallspec = ins.fcallArgs!!
val argsWithAddresses = fcallspec.arguments.map { arg ->
if(arg.address!=null)
arg
else {
val address = variableAddresses.getValue(ins.labelSymbol + "." + arg.name)
FunctionCallArgs.ArgumentSpec(arg.name, address, arg.reg)
}
}
fcallspec.arguments = argsWithAddresses
}
}
}
}
}
private val functionCallOpcodes = setOf(Opcode.CALL, Opcode.SYSCALL, Opcode.JUMP, Opcode.JUMPA)
private fun findCall(it: IRCodeChunk, startIndex: Int): IRInstruction {
var idx = startIndex
while(it.instructions[idx].opcode !in functionCallOpcodes)
idx++
return it.instructions[idx]
2022-10-16 16:30:14 +00:00
}
private fun varsToMemory(
program: IRProgram,
allocations: VmVariableAllocator,
symbolAddresses: MutableMap<String, Int>,
memory: Memory
) {
program.st.allVariables().forEach { variable ->
var addr = allocations.allocations.getValue(variable.name)
2023-02-19 02:07:55 +00:00
// zero out uninitialized variables.
if(variable.uninitialized) {
if(variable.dt in ArrayDatatypes) {
repeat(variable.length!!) {
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
memory.setUB(addr, 0u)
addr++
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
memory.setUW(addr, 0u)
addr += 2
}
DataType.ARRAY_F -> {
memory.setFloat(addr, 0.0f)
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}
else -> throw IRParseException("invalid dt")
}
}
}
}
variable.onetimeInitializationNumericValue?.let {
when(variable.dt) {
DataType.UBYTE -> memory.setUB(addr, it.toInt().toUByte())
DataType.BYTE -> memory.setSB(addr, it.toInt().toByte())
DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort())
DataType.WORD -> memory.setSW(addr, it.toInt().toShort())
DataType.FLOAT -> memory.setFloat(addr, it.toFloat())
2022-09-27 01:32:39 +00:00
else -> throw IRParseException("invalid dt")
}
}
variable.onetimeInitializationArrayValue?.let {
require(variable.length==it.size || it.size==1 || it.size==0)
if(it.isEmpty() || it.size==1) {
val value = if(it.isEmpty()) {
require(variable.uninitialized)
0.0
} else {
require(!variable.uninitialized)
it[0].number!!
}
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB -> {
repeat(variable.length!!) {
memory.setUB(addr, value.toInt().toUByte())
addr++
}
}
DataType.ARRAY_B -> {
repeat(variable.length!!) {
memory.setSB(addr, value.toInt().toByte())
addr++
}
}
DataType.ARRAY_UW -> {
repeat(variable.length!!) {
memory.setUW(addr, value.toInt().toUShort())
addr+=2
}
}
DataType.ARRAY_W -> {
repeat(variable.length!!) {
memory.setSW(addr, value.toInt().toShort())
addr+=2
}
}
DataType.ARRAY_F -> {
repeat(variable.length!!) {
memory.setFloat(addr, value.toFloat())
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}
}
2022-09-27 01:32:39 +00:00
else -> throw IRParseException("invalid dt")
}
} else {
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB -> {
for(elt in it) {
memory.setUB(addr, elt.number!!.toInt().toUByte())
addr++
}
}
DataType.ARRAY_B -> {
for(elt in it) {
memory.setSB(addr, elt.number!!.toInt().toByte())
addr++
}
}
DataType.ARRAY_UW -> {
for(elt in it) {
2022-12-30 17:07:53 +00:00
if(elt.addressOfSymbol!=null) {
val name = elt.addressOfSymbol!!
val symbolAddress = symbolAddresses[name]
2022-12-30 17:07:53 +00:00
?: throw IRParseException("vm cannot yet load a label address as a value: $name") // TODO
memory.setUW(addr, symbolAddress.toUShort())
} else {
memory.setUW(addr, elt.number!!.toInt().toUShort())
}
addr+=2
}
}
DataType.ARRAY_W -> {
for(elt in it) {
memory.setSW(addr, elt.number!!.toInt().toShort())
addr+=2
}
}
DataType.ARRAY_F -> {
for(elt in it) {
2023-04-03 22:06:55 +00:00
memory.setFloat(addr, elt.number!!.toFloat())
addr+=program.options.compTarget.machine.FLOAT_MEM_SIZE
}
}
2022-09-27 01:32:39 +00:00
else -> throw IRParseException("invalid dt")
}
}
}
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." }
}
}
2022-09-26 17:03:54 +00:00
}