ir: added preparecall 'meta' instruction for functioncalls

This commit is contained in:
Irmen de Jong 2023-09-18 23:22:03 +02:00
parent 958b5c0780
commit c319233ddc
7 changed files with 55 additions and 50 deletions

View File

@ -71,6 +71,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0]) val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1]) val addressTr = exprGen.translateExpression(call.args[1])
val argumentwordTr = exprGen.translateExpression(call.args[2]) val argumentwordTr = exprGen.translateExpression(call.args[2])
@ -112,6 +113,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val left = exprGen.translateExpression(call.args[0]) val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1]) val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1) addToResult(result, left, left.resultReg, -1)
@ -146,6 +148,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
@ -167,6 +170,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
@ -310,6 +314,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
@ -333,6 +338,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
@ -356,6 +362,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val type = irType(call.type) val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0]) val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1]) val minimumTr = exprGen.translateExpression(call.args[1])

View File

@ -122,6 +122,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
when(check.iterable.type) { when(check.iterable.type) {
DataType.STR -> { DataType.STR -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val elementTr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
@ -130,6 +131,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
@ -141,6 +143,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
@ -381,6 +384,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) { when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
is StSub -> { is StSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments // assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>() val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
@ -401,8 +405,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null) FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
} }
// create the call // create the call
val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)) addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)), null)
addInstr(result, call, null)
return if(fcall.void) return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT) else if(fcall.type==DataType.FLOAT)
@ -412,6 +415,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
is StRomSub -> { is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments // assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>() val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {

View File

@ -466,7 +466,11 @@ class TestVmCodeGen: FunSpec({
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBe 1 irChunks.size shouldBe 1
val callInstr = irChunks.single().instructions.single() irChunks[0].instructions.size shouldBe 2
val preparecallInstr = irChunks[0].instructions[0]
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
preparecallInstr.immediate shouldBe 0
val callInstr = irChunks[0].instructions[1]
callInstr.opcode shouldBe Opcode.CALL callInstr.opcode shouldBe Opcode.CALL
callInstr.address shouldBe 0x5000 callInstr.address shouldBe 0x5000
} }

View File

@ -1,53 +1,20 @@
%import textio %import textio
%import floats
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
ubyte[] barray = [11,22,33] ubyte arg3 = 200
uword[] warray = [$1234,$5678,$abcd] uword result = calc(101, 202, arg3)
uword[] @split split_warray = [$1234,$5678,$abcd] txt.print_uw(result)
float[] float_array = [11.11,22.22,33.33]
txt.print("incr of 1: ") str name = "irmen"
txt.print_uw(&barray) ubyte[] array = [1,2,3,4]
txt.spc() bool xx = 44 in array
txt.print_uw(&barray[0]) bool yy = 'a' in name
txt.spc() }
txt.print_uw(&barray[1])
txt.spc()
txt.print_uw(&barray[2])
txt.nl()
txt.print("incr of 2: ") sub calc(ubyte a1, ubyte a2, ubyte a3) -> uword {
txt.print_uw(&warray) return a1 as uword + a2 + a3
txt.spc()
txt.print_uw(&warray[0])
txt.spc()
txt.print_uw(&warray[1])
txt.spc()
txt.print_uw(&warray[2])
txt.nl()
txt.print("incr of 1: ")
txt.print_uw(&split_warray)
txt.spc()
txt.print_uw(&split_warray[0])
txt.spc()
txt.print_uw(&split_warray[1])
txt.spc()
txt.print_uw(&split_warray[2])
txt.nl()
txt.print("incr of 4 or 5: ")
txt.print_uw(&float_array)
txt.spc()
txt.print_uw(&float_array[0])
txt.spc()
txt.print_uw(&float_array[1])
txt.spc()
txt.print_uw(&float_array[2])
txt.nl()
} }
} }

View File

@ -53,6 +53,7 @@ CONTROL FLOW
------------ ------------
jump location - continue running at instruction at 'location' (label/memory address) jump location - continue running at instruction at 'location' (label/memory address)
jumpi reg1 - continue running at memory address in reg1 (indirect jump) jumpi reg1 - continue running at memory address in reg1 (indirect jump)
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters
call label(argument register list) [: resultreg.type] call label(argument register list) [: resultreg.type]
- calls a subroutine with the given arguments and return value (optional). - calls a subroutine with the given arguments and return value (optional).
save current instruction location+1, continue execution at instruction nr of the label. save current instruction location+1, continue execution at instruction nr of the label.
@ -60,9 +61,11 @@ call label(argument register list) [: resultreg.type]
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2 If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix. If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
For example: call $ffd2(r5.b@A) For example: call $ffd2(r5.b@A)
Always preceded by parameter setup and preparecall instructions
syscall number (argument register list) [: resultreg.type] syscall number (argument register list) [: resultreg.type]
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so - do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
will be POPped off into the given resultregister if any. will be POPped off into the given resultregister if any.
Always preceded by parameter setup and preparecall instructions
return - restore last saved instruction location and continue at that instruction. No return value. return - restore last saved instruction location and continue at that instruction. No return value.
returnr reg1 - like return, but also returns the value in reg1 to the caller returnr reg1 - like return, but also returns the value in reg1 to the caller
@ -244,6 +247,7 @@ enum class Opcode {
JUMP, JUMP,
JUMPI, JUMPI,
PREPARECALL,
CALL, CALL,
SYSCALL, SYSCALL,
RETURN, RETURN,
@ -541,6 +545,7 @@ val instructionFormats = mutableMapOf(
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"), Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.JUMP to InstructionFormat.from("N,<a"), Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPI to InstructionFormat.from("N,<r1"), Opcode.JUMPI to InstructionFormat.from("N,<r1"),
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
Opcode.CALL to InstructionFormat.from("N,call"), Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.SYSCALL to InstructionFormat.from("N,syscall"), Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.RETURN to InstructionFormat.from("N"), Opcode.RETURN to InstructionFormat.from("N"),

View File

@ -213,10 +213,27 @@ class IRProgram(val name: String,
if(chunk is IRInlineAsmChunk) if(chunk is IRInlineAsmChunk)
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"} require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
} }
chunk.instructions.forEach { chunk.instructions.withIndex().forEach { (index, instr) ->
if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch) { if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) {
if(!it.labelSymbol.startsWith('$') && !it.labelSymbol.first().isDigit()) if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" } require(instr.branchTarget != null) { "branching instruction to label should have branchTarget set" }
}
if(instr.opcode==Opcode.PREPARECALL) {
var i = index+1
var instr2 = chunk.instructions[i]
val registers = mutableSetOf<Int>()
while(instr2.opcode!=Opcode.SYSCALL && instr2.opcode!=Opcode.CALL) {
if(instr2.reg1direction==OperandDirection.WRITE || instr2.reg1direction==OperandDirection.READWRITE) registers.add(instr2.reg1!!)
if(instr2.reg2direction==OperandDirection.WRITE || instr2.reg2direction==OperandDirection.READWRITE) registers.add(instr2.reg2!!)
if(instr2.reg3direction==OperandDirection.WRITE || instr2.reg3direction==OperandDirection.READWRITE) registers.add(instr2.reg3!!)
if(instr2.fpReg1direction==OperandDirection.WRITE || instr2.fpReg1direction==OperandDirection.READWRITE) registers.add(instr2.fpReg1!!)
if(instr2.fpReg2direction==OperandDirection.WRITE || instr2.fpReg2direction==OperandDirection.READWRITE) registers.add(instr2.fpReg2!!)
i++
instr2 = chunk.instructions[i]
}
val expectedRegisterLoads = chunk.instructions[i].fcallArgs!!.arguments.map { it.reg.registerNum }
require(registers.containsAll(expectedRegisterLoads)) { "not all argument registers are given a value in the preparecall-call sequence" }
} }
} }
} }

View File

@ -179,6 +179,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.STOREZX -> InsSTOREZX(ins) Opcode.STOREZX -> InsSTOREZX(ins)
Opcode.STOREZI -> InsSTOREZI(ins) Opcode.STOREZI -> InsSTOREZI(ins)
Opcode.JUMP, Opcode.JUMPI -> InsJUMP(ins) Opcode.JUMP, Opcode.JUMPI -> InsJUMP(ins)
Opcode.PREPARECALL -> nextPc()
Opcode.CALL -> InsCALL(ins) Opcode.CALL -> InsCALL(ins)
Opcode.SYSCALL -> InsSYSCALL(ins) Opcode.SYSCALL -> InsSYSCALL(ins)
Opcode.RETURN -> InsRETURN() Opcode.RETURN -> InsRETURN()