IR support for storing incbins and romsubs

This commit is contained in:
Irmen de Jong 2022-09-17 14:07:13 +02:00
parent 0e831d4b92
commit 2f3e7d1c27
11 changed files with 71 additions and 18 deletions

View File

@ -238,7 +238,11 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
}
class StRomSub(name: String, val address: UInt, val parameters: List<StSubroutineParameter>, val returnTypes: List<DataType>, position: Position) :
class StRomSub(name: String,
val address: UInt,
val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>,
position: Position) :
StNode(name, StNodeType.ROMSUB, position) {
override fun printProperties() {
print("$name address=${address.toHex()}")
@ -247,6 +251,7 @@ class StRomSub(name: String, val address: UInt, val parameters: List<StSubroutin
class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOf: List<String>?)
typealias StString = Pair<String, Encoding>

View File

@ -913,7 +913,31 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
return code
}
is StRomSub -> {
throw AssemblyError("virtual machine doesn't yet support calling romsub $fcall")
val code = IRCodeChunk(fcall.position)
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.vmType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
if(codeGen.isZero(arg)) {
code += IRCodeInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
} else {
if (paramDt == VmDataType.FLOAT)
throw AssemblyError("doesn't support float register argument in asm romsub")
val argReg = codeGen.vmRegisters.nextFree()
code += translateExpression(arg, argReg, -1)
code += IRCodeInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
}
}
code += IRCodeInstruction(Opcode.CALL, value=callTarget.address.toInt())
if(!fcall.void) {
if(callTarget.returns.size!=1)
throw AssemblyError("expect precisely 1 return value")
if(fcall.type==DataType.FLOAT)
throw AssemblyError("doesn't support float register result in asm romsub")
val returns = callTarget.returns.single()
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
code += IRCodeInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
}
return code
}
else -> throw AssemblyError("invalid node type")
}

View File

@ -399,8 +399,8 @@ private fun createAssemblyAndAssemble(program: Program,
// to help clean up the code that still depends on them.
// removeAllVardeclsFromAst(program)
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
println("*********** AST RIGHT BEFORE ASM GENERATION *************")
printProgram(program)
val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly()
errors.report()

View File

@ -230,7 +230,8 @@ class IntermediateAstMaker(val program: Program) {
val type = srcCall.inferType(program).getOrElse {
throw FatalAstException("unknown dt $srcCall")
}
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
val isVoid = type==DataType.UNDEFINED
val call = PtFunctionCall(target, isVoid, type, srcCall.position)
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call

View File

@ -36,12 +36,13 @@ internal class SymbolTableMaker: IAstVisitor {
}
override fun visit(subroutine: Subroutine) {
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
if(subroutine.asmAddress!=null) {
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.returntypes, subroutine.position)
val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) }
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmParameterRegisters, subroutine.position)
scopestack.peek().add(node)
// st.origAstLinks[subroutine] = node
} else {
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
val returnType = if(subroutine.returntypes.isEmpty()) null else subroutine.returntypes.first()
val node = StSub(subroutine.name, parameters, returnType, subroutine.position)
scopestack.peek().add(node)

View File

@ -3,8 +3,9 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- IR/VM: add proper memory mapped variables support - replace the symbol by the memory address in the IR code
- IR/VM: add support for incbin (!binary)
- VM Assembler: add support for translating symbols to address search for "TODO do we have to replace variable names by their allocated address" load.w r0,{_}txt.clear_screen.sequence
- IR/VM: add proper memory mapped variables support - replace the symbol by the memory address in the IR code.
- IR/VM: check that the above works ok now with the cx16 virtual registers.
- IR/VM: add proper memory slabs support
...
@ -21,9 +22,6 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- vm: implement remaining sin/cos functions in math.p8 and merge tables
- vm: implement memory mapped variables properly in VariableAllocator
- vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now. Maybe this gets solved by the previous item?
- vm: encode romsub & romsub call in VM IR (can just crash in virtualmachine itself because program is not in the simulated memory) ExpressionGen.kt
- vm: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us)
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
- vm: add more optimizations in VmPeepholeOptimizer

View File

@ -12,6 +12,9 @@ main {
return
}}
romsub $ee33 = myromsub(ubyte arg1 @A) clobbers() -> ubyte @Y
romsub $ee44 = myromsubmulti(ubyte arg1 @A) clobbers() -> ubyte @Y, ubyte @X, ubyte @Pc
asmsub testasmsub(ubyte arg1 @A) clobbers(Y) -> uword @AX {
%asm {{
nop
@ -20,6 +23,8 @@ main {
}
sub start() {
void myromsubmulti(44)
global1 = myromsub(44)
sys.wait(1)
%asm {{

View File

@ -240,7 +240,7 @@ class IRFileReader(outputDir: Path, programName: String) {
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM POS=(.+)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.+) RETURNS=(.+) POS=(.+)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
@ -309,7 +309,7 @@ class IRFileReader(outputDir: Path, programName: String) {
val asm = parseInlineAssembly(line, lines)
while(line!="</ASMSUB>")
line = lines.next()
val clobberRegs = clobbers.split(',').map { CpuRegister.valueOf(it) }
val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) }
val returns = mutableListOf<Pair<DataType, RegisterOrStatusflag>>()
returnSpec.split(',').forEach{ rs ->
val (regstr, dtstr) = rs.split(':')

View File

@ -29,12 +29,15 @@ loadi reg1, reg2 - load reg1 with value at memory indirect,
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
loadr reg1, reg2 - load reg1 with value in register reg2
loadcpu reg1, cpureg - load reg1 with value from cpu register (register/registerpair/statusflag)
storem reg1, address - store reg1 at memory address
storecpu reg1, cpureg - store reg1 in cpu register (register/registerpair/statusflag)
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
storezm address - store zero at memory address
storezcpu cpureg - store zero in cpu register (register/registerpair/statusflag)
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg
@ -196,7 +199,7 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by
concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs)
push [b, w] reg1 - push value in reg1 on the stack
pop [b, w] reg1 - pop value from stack into reg1
binarydata - 'instruction' to hold inlined binary data bytes
*/
enum class Opcode {
@ -207,11 +210,14 @@ enum class Opcode {
LOADX,
LOADIX,
LOADR,
LOADCPU,
STOREM,
STORECPU,
STOREI,
STOREX,
STOREIX,
STOREZM,
STOREZCPU,
STOREZI,
STOREZX,
@ -338,7 +344,8 @@ enum class Opcode {
POP,
MSIG,
CONCAT,
BREAKPOINT
BREAKPOINT,
BINARYDATA
}
val OpcodesWithAddress = setOf(
@ -393,7 +400,8 @@ data class Instruction(
val fpReg2: Int?=null, // 0-$ffff
val value: Int?=null, // 0-$ffff
val fpValue: Float?=null,
val labelSymbol: List<String>?=null // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val labelSymbol: List<String>?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: ByteArray?=null
) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
@ -401,6 +409,9 @@ data class Instruction(
val fpReg1direction: OperandDirection
init {
if(opcode==Opcode.BINARYDATA && binaryData==null || binaryData!=null && opcode!=Opcode.BINARYDATA)
throw IllegalArgumentException("binarydata inconsistency")
val formats = instructionFormats.getValue(opcode)
if(type==null && !formats.containsKey(null))
throw IllegalArgumentException("missing type")
@ -560,11 +571,14 @@ val instructionFormats = mutableMapOf(
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.LOADCPU to InstructionFormat.from("BW,>r1"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
Opcode.STORECPU to InstructionFormat.from("BW,<r1"),
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
Opcode.STOREZM to InstructionFormat.from("BW,<v | F,<v"),
Opcode.STOREZCPU to InstructionFormat.from("BW"),
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,<v | F,<r1,<v"),
Opcode.JUMP to InstructionFormat.from("N,<v"),
@ -688,4 +702,5 @@ val instructionFormats = mutableMapOf(
Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"),
Opcode.BREAKPOINT to InstructionFormat.from("N"),
Opcode.BINARYDATA to InstructionFormat.from("N"),
)

View File

@ -76,7 +76,10 @@ class Assembler {
val binarymatch = binaryPattern.matchEntire(line.trim())
if(binarymatch!=null) {
val hex = binarymatch.groups[1]!!.value
TODO("binary ${hex}")
val binary = hex.windowed(size=2, step=2).map {
it.toString().toByte(16)
}.toByteArray()
program.add(Instruction(Opcode.BINARYDATA, binaryData = binary))
} else {
val labelmatch = labelPattern.matchEntire(line.trim())
if (labelmatch == null)

View File

@ -217,6 +217,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.BREAKPOINT -> InsBREAKPOINT()
Opcode.CLC -> { statusCarry = false; pc++ }
Opcode.SEC -> { statusCarry = true; pc++ }
Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM")
Opcode.FFROMUB -> InsFFROMUB(ins)
Opcode.FFROMSB -> InsFFROMSB(ins)