mirror of
https://github.com/irmen/prog8.git
synced 2025-07-22 22:24:10 +00:00
IR: get rid of problematic PREPARECALL pseudo instruction
This commit is contained in:
@@ -78,7 +78,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@@ -91,7 +90,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentA = exprGen.translateExpression(call.args[2])
|
||||
@@ -143,7 +141,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
@@ -278,7 +275,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
|
@@ -289,7 +289,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val haystackVar = check.haystackHeapVar!!
|
||||
when {
|
||||
haystackVar.type.isString -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -300,7 +299,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isByteArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -314,7 +312,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isWordArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -328,7 +325,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isFloatArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -618,7 +614,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (callTarget) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -670,7 +665,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
is StExtSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
|
@@ -48,6 +48,7 @@ class IRCodeGen(
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
|
@@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
|
@@ -554,11 +554,8 @@ class TestVmCodeGen: FunSpec({
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
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]
|
||||
irChunks[0].instructions.size shouldBe 1
|
||||
val callInstr = irChunks[0].instructions[0]
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ Future Things and Ideas
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- BUG Key main.start.the_loop is missing in the map when compiling shell into IR. See test.p8 in commit that added this line here
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
|
@@ -1,10 +1,15 @@
|
||||
main {
|
||||
ubyte @shared banknumber
|
||||
extsub @bank 10 $C04B = otherbank() clobbers(A,X,Y)
|
||||
extsub @bank banknumber $C04B = otherbankvar() clobbers(A,X,Y)
|
||||
|
||||
sub start() {
|
||||
otherbank()
|
||||
otherbankvar()
|
||||
nmi_handler()
|
||||
cx16.r0++
|
||||
the_loop:
|
||||
repeat {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
sub nmi_handler() {;forcefully kills the running process and returns to the shell prompt.
|
||||
cx16.r0++
|
||||
goto main.start.the_loop
|
||||
}
|
||||
}
|
||||
|
@@ -71,7 +71,6 @@ CONTROL FLOW
|
||||
------------
|
||||
jump location - continue running at instruction at 'location' (label/memory address)
|
||||
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, does nothing by itself
|
||||
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
|
||||
call label(argument register list) [: resultreg.type]
|
||||
- calls a subroutine with the given arguments and return value (optional).
|
||||
@@ -80,13 +79,13 @@ 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 arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
|
||||
For example: call $ffd2(r5.b@A)
|
||||
Always preceded by parameter setup and preparecall instructions
|
||||
Always preceded by parameter setup
|
||||
callfar bank, address Call a subroutine at the given memory address, in the given RAM/ROM bank (switches both banks at the same time)
|
||||
callfarvb reg1 address Call a subroutine at the given memory address, in the RAM/ROM bank in reg1.b (switches both banks at the same time)
|
||||
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
|
||||
will be POPped off into the given resultregister if any.
|
||||
Always preceded by parameter setup and preparecall instructions.
|
||||
Always preceded by parameter setup
|
||||
All register types (arguments + result register) are ALWAYS WORDS.
|
||||
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
|
||||
@@ -283,7 +282,6 @@ enum class Opcode {
|
||||
|
||||
JUMP,
|
||||
JUMPI,
|
||||
PREPARECALL,
|
||||
CALLI,
|
||||
CALL,
|
||||
CALLFAR,
|
||||
@@ -626,7 +624,6 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.STOREHFACONE to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<a"),
|
||||
Opcode.JUMPI to InstructionFormat.from("N,<r1"),
|
||||
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
|
||||
Opcode.CALLI to InstructionFormat.from("N,<r1"),
|
||||
Opcode.CALL to InstructionFormat.from("N,call"),
|
||||
Opcode.CALLFAR to InstructionFormat.from("N,<i,<a"),
|
||||
|
@@ -217,7 +217,7 @@ class IRProgram(val name: String,
|
||||
if(chunk is IRInlineAsmChunk)
|
||||
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
|
||||
}
|
||||
chunk.instructions.withIndex().forEach { (index, instr) ->
|
||||
chunk.instructions.forEach { instr ->
|
||||
if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) {
|
||||
if(instr.opcode==Opcode.JUMPI) {
|
||||
when(val pointervar = st.lookup(instr.labelSymbol)!!) {
|
||||
@@ -229,26 +229,6 @@ class IRProgram(val name: String,
|
||||
else if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
|
||||
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 && i<chunk.instructions.size-1) {
|
||||
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]
|
||||
}
|
||||
// it could be that the actual call is only in another code chunk, so IF we find one, we can check, otherwise just skip the check...
|
||||
if(chunk.instructions[i].fcallArgs!=null) {
|
||||
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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -216,7 +216,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.STOREHFACONE-> InsSTOREHFACONE(ins)
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPI -> InsJUMPI(ins)
|
||||
Opcode.PREPARECALL -> nextPc()
|
||||
Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes")
|
||||
Opcode.CALL -> InsCALL(ins)
|
||||
Opcode.CALLFAR, Opcode.CALLFARVB -> throw IllegalArgumentException("VM cannot run code from another ram/rombank")
|
||||
|
Reference in New Issue
Block a user