mirror of
https://github.com/irmen/prog8.git
synced 2025-02-25 04:29:36 +00:00
new IR call and return instructions to deal with returnregisters
This commit is contained in:
parent
39132327cc
commit
78a097585d
@ -954,17 +954,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol=fcall.name), null)
|
||||
if(fcall.type==DataType.FLOAT) {
|
||||
if (!fcall.void && resultFpRegister != 0) {
|
||||
// Call convention: result value is in fr0, so put it in the required register instead.
|
||||
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0), null)
|
||||
}
|
||||
} else {
|
||||
if (!fcall.void && resultRegister != 0) {
|
||||
// Call convention: result value is in r0, so put it in the required register instead.
|
||||
addInstr(result, IRInstruction(Opcode.LOADR, codeGen.irType(fcall.type), reg1 = resultRegister, reg2 = 0), null)
|
||||
}
|
||||
if(fcall.void)
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol=fcall.name), null)
|
||||
else {
|
||||
if(fcall.type==DataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.CALLRVAL, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol=fcall.name), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.CALLRVAL, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol=fcall.name), null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -983,6 +979,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr), null)
|
||||
}
|
||||
}
|
||||
// just a regular call without using Vm register call convention: the value is returned in CPU registers!
|
||||
addInstr(result, IRInstruction(Opcode.CALL, value=callTarget.address.toInt()), null)
|
||||
if(!fcall.void) {
|
||||
when(callTarget.returns.size) {
|
||||
|
@ -68,6 +68,7 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
if(options.optimize) {
|
||||
// TODO integrate into peephole optimizer above
|
||||
val opt = IROptimizer(irProg)
|
||||
opt.optimize()
|
||||
}
|
||||
@ -1265,14 +1266,20 @@ class IRCodeGen(
|
||||
private fun translate(ret: PtReturn): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val value = ret.value
|
||||
if(value!=null) {
|
||||
// Call Convention: return value is always returned in r0 (or fr0 if float)
|
||||
result += if(value.type==DataType.FLOAT)
|
||||
expressionEval.translateExpression(value, -1, 0)
|
||||
else
|
||||
expressionEval.translateExpression(value, 0, -1)
|
||||
if(value==null) {
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
} else {
|
||||
if(value.type==DataType.FLOAT) {
|
||||
val reg = registers.nextFreeFloat()
|
||||
result += expressionEval.translateExpression(value, -1, reg)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNREG, IRDataType.FLOAT, fpReg1 = reg), null)
|
||||
}
|
||||
else {
|
||||
val reg = registers.nextFree()
|
||||
result += expressionEval.translateExpression(value, reg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNREG, irType(value.type) , reg1=reg), null)
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
// remove useless RETURN
|
||||
if(ins.opcode == Opcode.RETURN && idx>0) {
|
||||
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNREG)) {
|
||||
val previous = chunk.instructions[idx-1] as? IRInstruction
|
||||
if(previous?.opcode in OpcodesThatJump) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
|
@ -5,6 +5,7 @@ import prog8.intermediate.SyscallRegisterBase
|
||||
|
||||
internal class RegisterPool {
|
||||
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
||||
// TODO set this back to 0 once 'resultRegister' has been removed everywhere?
|
||||
private var firstFree: Int=3
|
||||
private var firstFreeFloat: Int=3
|
||||
|
||||
|
@ -2,6 +2,8 @@ package prog8.iroptimizer
|
||||
|
||||
import prog8.intermediate.*
|
||||
|
||||
// TODO integrate into peephole optimizer
|
||||
|
||||
internal class IROptimizer(val program: IRProgram) {
|
||||
fun optimize() {
|
||||
program.blocks.forEach { block ->
|
||||
@ -36,7 +38,7 @@ internal class IROptimizer(val program: IRProgram) {
|
||||
val i1 = first.value
|
||||
val i2 = second.value
|
||||
// replace call + return --> jump
|
||||
if(i1.opcode==Opcode.CALL && i2.opcode==Opcode.RETURN) {
|
||||
if((i1.opcode==Opcode.CALL || i1.opcode==Opcode.CALLRVAL) && i2.opcode==Opcode.RETURN) {
|
||||
elt.instructions[first.index] = IRInstruction(Opcode.JUMP, value=i1.value, labelSymbol = i1.labelSymbol, branchTarget = i1.branchTarget)
|
||||
elt.instructions[second.index] = IRInstruction(Opcode.NOP)
|
||||
if(second.index==elt.instructions.size-1) {
|
||||
|
@ -198,7 +198,7 @@ sub str2uword(str string) -> uword {
|
||||
%ir {{
|
||||
loadm.w r65500,conv.str2uword.string
|
||||
syscall 11
|
||||
return
|
||||
returnreg.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ sub str2word(str string) -> word {
|
||||
%ir {{
|
||||
loadm.w r65500,conv.str2word.string
|
||||
syscall 12
|
||||
return
|
||||
returnreg.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ sub pow(float value, float power) -> float {
|
||||
loadm.f fr0,floats.pow.value
|
||||
loadm.f fr1,floats.pow.power
|
||||
fpow.f fr0,fr1
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ sub fabs(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.fabs.value
|
||||
fabs.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ sub sin(float angle) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sin.angle
|
||||
fsin.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ sub cos(float angle) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.cos.angle
|
||||
fcos.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ sub tan(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.tan.value
|
||||
ftan.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ sub atan(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.atan.value
|
||||
fatan.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ sub ln(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.ln.value
|
||||
fln.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ sub log2(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.log2.value
|
||||
flog.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ sub sqrt(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sqrt.value
|
||||
sqrt.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ sub round(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.round.value
|
||||
fround.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ sub floor(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.floor.value
|
||||
ffloor.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@ -120,14 +120,14 @@ sub ceil(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.ceil.value
|
||||
fceil.f fr0,fr0
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndf() -> float {
|
||||
%ir {{
|
||||
syscall 35
|
||||
return
|
||||
returnreg.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -162,14 +162,14 @@ math {
|
||||
sub rnd() -> ubyte {
|
||||
%ir {{
|
||||
syscall 33
|
||||
return
|
||||
returnreg.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndw() -> uword {
|
||||
%ir {{
|
||||
syscall 34
|
||||
return
|
||||
returnreg.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ string {
|
||||
loadm.w r65500,string.compare.st1
|
||||
loadm.w r65501,string.compare.st2
|
||||
syscall 29
|
||||
return
|
||||
returnreg.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ sys {
|
||||
loadm.w r65500,sys.gfx_getpixel.xx
|
||||
loadm.w r65501,sys.gfx_getpixel.yy
|
||||
syscall 30
|
||||
return
|
||||
returnreg.b r0
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ sub input_chars (uword buffer) -> ubyte {
|
||||
%ir {{
|
||||
loadm.w r65500,txt.input_chars.buffer
|
||||
syscall 6
|
||||
return
|
||||
returnreg.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,7 @@ TODO
|
||||
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
- IR: don't hardcode r0/fr0 as return registers.
|
||||
instead have RETURN -> returns void, RETURNREG <register> -> return value from given register
|
||||
also CALL -> void call, CALLRVAL -> specify register to put call result in. CALLRVAL r0, functionThatReturnsInt
|
||||
- get rid of ResultRegister in IR codegen? as the calls now encode this into the new opcodes...
|
||||
|
||||
...
|
||||
|
||||
|
@ -47,17 +47,13 @@ storezx reg1, address - store zero at memory address, indexed by
|
||||
|
||||
CONTROL FLOW
|
||||
------------
|
||||
Possible subroutine call convention:
|
||||
Set parameters in Reg 0, 1, 2... before call. Return value set in Reg 0 before return.
|
||||
But you can decide whatever you want because here we just care about jumping and returning the flow of control.
|
||||
Saving/restoring registers is possible with PUSH and POP instructions.
|
||||
|
||||
jump location - continue running at instruction number given by location
|
||||
jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction)
|
||||
call location - save current instruction location+1, continue execution at instruction nr given by location
|
||||
calli reg1 - save current instruction location+1, continue execution at instruction number in reg1
|
||||
call location - save current instruction location+1, continue execution at instruction nr given by location. Expect no return value.
|
||||
callrval reg1, location - like call but expects a return value from a returnreg instruction, and puts that in reg1
|
||||
syscall value - do a systemcall identified by call number
|
||||
return - restore last saved instruction location and continue at that instruction
|
||||
return - restore last saved instruction location and continue at that instruction. No return value.
|
||||
returnreg reg1 - like return, but also returns a value to the caller via reg1
|
||||
|
||||
|
||||
BRANCHING and CONDITIONALS
|
||||
@ -232,8 +228,10 @@ enum class Opcode {
|
||||
JUMP,
|
||||
JUMPA,
|
||||
CALL,
|
||||
CALLRVAL,
|
||||
SYSCALL,
|
||||
RETURN,
|
||||
RETURNREG,
|
||||
|
||||
BSTCC,
|
||||
BSTCS,
|
||||
@ -363,14 +361,17 @@ enum class Opcode {
|
||||
val OpcodesThatJump = setOf(
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.RETURN
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURNREG
|
||||
)
|
||||
|
||||
val OpcodesThatBranch = setOf(
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURNREG,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLRVAL,
|
||||
Opcode.SYSCALL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
@ -412,6 +413,7 @@ val OpcodesWithMemoryAddressAsValue = setOf(
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLRVAL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
Opcode.BSTEQ,
|
||||
@ -551,8 +553,10 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.JUMP to InstructionFormat.from("N,<v"),
|
||||
Opcode.JUMPA to InstructionFormat.from("N,<v"),
|
||||
Opcode.CALL to InstructionFormat.from("N,<v"),
|
||||
Opcode.CALLRVAL to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,<v"),
|
||||
Opcode.RETURN to InstructionFormat.from("N"),
|
||||
Opcode.RETURNREG to InstructionFormat.from("BW,<r1 | F,<fr1"),
|
||||
Opcode.BSTCC to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTCS to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTEQ to InstructionFormat.from("N,<v"),
|
||||
|
@ -134,7 +134,7 @@ class IRProgram(val name: String,
|
||||
|
||||
// link all jump and branching instructions to their target
|
||||
chunk.instructions.forEach {
|
||||
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null)
|
||||
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNREG && it.labelSymbol!=null)
|
||||
it.branchTarget = labeledChunks.getValue(it.labelSymbol)
|
||||
// note: branches with an address value cannot be linked to something...
|
||||
}
|
||||
|
@ -31,10 +31,11 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception
|
||||
|
||||
@Suppress("FunctionName")
|
||||
class VirtualMachine(irProgram: IRProgram) {
|
||||
class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val returnValueReg: Int?, val returnValueFpReg: Int?)
|
||||
val memory = Memory()
|
||||
val program: List<IRCodeChunk>
|
||||
val registers = Registers()
|
||||
val callStack = Stack<Pair<IRCodeChunk, Int>>()
|
||||
val callStack = Stack<CallSiteContext>()
|
||||
val valueStack = Stack<UByte>() // max 128 entries
|
||||
var breakpointHandler: ((pcChunk: IRCodeChunk, pcIndex: Int) -> Unit)? = null // can set custom breakpoint handler
|
||||
var pcChunk = IRCodeChunk(null, null)
|
||||
@ -170,8 +171,10 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)")
|
||||
Opcode.CALL -> InsCALL(ins)
|
||||
Opcode.CALLRVAL -> InsCALLRVAL(ins)
|
||||
Opcode.SYSCALL -> InsSYSCALL(ins)
|
||||
Opcode.RETURN -> InsRETURN()
|
||||
Opcode.RETURNREG -> InsRETURNREG(ins)
|
||||
Opcode.BSTCC -> InsBSTCC(ins)
|
||||
Opcode.BSTCS -> InsBSTCS(ins)
|
||||
Opcode.BSTEQ -> InsBSTEQ(ins)
|
||||
@ -578,7 +581,12 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
}
|
||||
|
||||
private fun InsCALL(i: IRInstruction) {
|
||||
callStack.push(Pair(pcChunk, pcIndex+1))
|
||||
callStack.push(CallSiteContext(pcChunk, pcIndex+1, null, null))
|
||||
branchTo(i)
|
||||
}
|
||||
|
||||
private fun InsCALLRVAL(i: IRInstruction) {
|
||||
callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.reg1, i.fpReg1))
|
||||
branchTo(i)
|
||||
}
|
||||
|
||||
@ -586,9 +594,49 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
if(callStack.isEmpty())
|
||||
exit(0)
|
||||
else {
|
||||
val (chunk, idx) = callStack.pop()
|
||||
pcChunk = chunk
|
||||
pcIndex = idx
|
||||
val context = callStack.pop()
|
||||
pcChunk = context.returnChunk
|
||||
pcIndex = context.returnIndex
|
||||
// ignore any return values.
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsRETURNREG(i: IRInstruction) {
|
||||
if(callStack.isEmpty())
|
||||
exit(0)
|
||||
else {
|
||||
val context = callStack.pop()
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
if(context.returnValueReg!=null)
|
||||
registers.setUB(context.returnValueReg, registers.getUB(i.reg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
throw IllegalArgumentException("missing return value reg")
|
||||
}
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
if(context.returnValueReg!=null)
|
||||
registers.setUW(context.returnValueReg, registers.getUW(i.reg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
throw IllegalArgumentException("missing return value reg")
|
||||
}
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
if(context.returnValueFpReg!=null)
|
||||
registers.setFloat(context.returnValueFpReg, registers.getFloat(i.fpReg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
throw IllegalArgumentException("missing return value reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
pcChunk = context.returnChunk
|
||||
pcIndex = context.returnIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user