changed IR JUMPI instruction to support more indirect jump cases

This commit is contained in:
Irmen de Jong 2024-12-19 03:30:42 +01:00
parent 73baaeff1f
commit f93b7e3303
7 changed files with 50 additions and 43 deletions

View File

@ -291,7 +291,7 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
}
addInstr(result, branchIns, null)
} else if(label!=null) {
} else if(label!=null && !isIndirectJump(goto)) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@ -304,7 +304,7 @@ class IRCodeGen(
}
addInstr(result, branchIns, null)
} else {
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope)
@ -1027,12 +1027,12 @@ class IRCodeGen(
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
}
}
val identifier = goto.target as? PtIdentifier
if(identifier!=null) {
it += IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name)
} else {
TODO("JUMP to expression address ${goto.target}")
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(goto.target)
for(i in tr.chunks.flatMap { it.instructions }) {
it += i
}
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
} else {
// normal jump, directly to target with branch opcode
when(condition.operator) {
@ -1054,10 +1054,10 @@ class IRCodeGen(
}
it += if (goto.target.asConstInteger() != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger())
else if(goto.target is PtIdentifier)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
else
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
@ -1073,10 +1073,10 @@ class IRCodeGen(
else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
val identifier = goto.target as? PtIdentifier
if(identifier!=null)
if(identifier!=null && !isIndirectJump(goto))
IRInstruction(branchOpcode, labelSymbol = identifier.name)
else
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
@ -1084,11 +1084,6 @@ class IRCodeGen(
// indirect jump to target so the if has to jump past it instead
val result = mutableListOf<IRCodeChunkBase>()
val afterIfLabel = createLabelName()
val identifier = goto.target as? PtIdentifier
if(identifier==null) {
TODO("JUMP to expression address ${goto.target}")
}
val gotoSymbol = identifier.name
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) {
@ -1210,7 +1205,10 @@ class IRCodeGen(
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
}
addInstr(result, IRInstruction(Opcode.JUMPI, labelSymbol = gotoSymbol), null)
// indirect jump to some computed address
val tr = expressionEval.translateExpression(goto.target)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg), null)
result += IRCodeChunk(afterIfLabel, null)
return result
}
@ -1254,10 +1252,10 @@ class IRCodeGen(
}
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
else
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
} else {
@ -1313,10 +1311,10 @@ class IRCodeGen(
} else {
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
else
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
@ -1649,23 +1647,26 @@ class IRCodeGen(
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null)
chunk += if(jump.target.asConstInteger()!=null) {
IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger())
} else {
val identifier = jump.target as? PtIdentifier
if (identifier != null) {
if(isIndirectJump(jump)) {
IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name)
} else {
IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
}
} else {
// val tr = expressionEval.translateExpression(jump.target)
TODO("JUMP to expression address ${jump.target}")
}
}
if(jump.target.asConstInteger()!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger())
result += chunk
return result
} else {
val identifier = jump.target as? PtIdentifier
if (identifier != null && !isIndirectJump(jump)) {
// jump to label
chunk += IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
result += chunk
return result
}
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(jump.target)
result += tr.chunks
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg)
}
return result
}
}
private fun isIndirectJump(jump: PtJump): Boolean {

View File

@ -397,7 +397,7 @@ cx16 {
extsub $ff4a = CLOSE_ALL(ubyte device @A) clobbers(A,X,Y)
extsub $ff59 = LKUPLA(ubyte la @A) clobbers(A,X,Y)
extsub $ff5c = LKUPSA(ubyte sa @Y) clobbers(A,X,Y)
extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get_screen_mode()
extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get/set_screen_mode()
extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y)
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A

View File

@ -24,6 +24,9 @@ Future Things and Ideas
Need to add some way to generate a stable jump table at a given address.
Need library to not call init_system AND init_system_phase2 not either.
Library must not include prog8_program_start stuff either.
- Add a LZSA decompressor to the compression library to be able to decompress lzsa when you don't have it in ROM or when the ROM is banked out or unavailable
Problem is: on the X16, it should replicate the Kernal's behavior with decompressing to Vram / not incrementing the output address
- Add TSCrunch or ZX0 decruncher to compression lib. Same requirement on X16 again to be able to decompress into vram.
- Fix missing cases where regular & has to return the start of the split array in memory whatever byte comes first. Search TODO("address of split word array")
- Add a syntax to access specific bits in a variable, to avoid manually shifts&ands, something like variable[4:8] ? (or something else this may be too similar to regular array indexing)
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
@ -56,7 +59,7 @@ IR/VM
- fix call() return value handling
- fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported")
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address" . This needs a change in the JUMPI instruction: it has to take a register instead (like CALLI)
- make it possible to jump and branch to a computed address (expression) in all cases, see TODO("JUMP to expression address"
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.

View File

@ -1,7 +1,8 @@
main {
sub start() {
;str test = '?' * 10
str name = "foo"
cx16.r0 = name+2
cx16.r0 = $aabb
cx16.r1 = $1122
goto cx16.r1+256
}
}

View File

@ -65,7 +65,7 @@ storehxy reg1 - store reg1.w into cpu hardware register
CONTROL FLOW
------------
jump location - continue running at instruction at 'location' (label/memory address)
jumpi pointervar - continue running at memory address contained in the pointer variable (indirect jump)
jumpi rqg1 - 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
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]
@ -645,7 +645,7 @@ val instructionFormats = mutableMapOf(
Opcode.STOREHAY to InstructionFormat.from("W,<r1"),
Opcode.STOREHXY to InstructionFormat.from("W,<r1"),
Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPI 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"),

View File

@ -624,7 +624,9 @@ class VirtualMachine(irProgram: IRProgram) {
}
private fun InsJUMPI(i: IRInstruction) {
val artificialAddress = memory.getUW(i.address!!).toInt()
val artificialAddress = registers.getUW(i.reg1!!).toInt()
if(!artificialLabelAddresses.contains(artificialAddress))
throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${artificialAddress.toHex()})")
pcChunk = artificialLabelAddresses.getValue(artificialAddress)
pcIndex = 0
}

View File

@ -123,7 +123,7 @@ class VmProgramLoader {
}
val label = ins.labelSymbol
if (label != null && (ins.opcode !in OpcodesThatBranch || ins.opcode==Opcode.JUMPI)) {
if (label != null && (ins.opcode !in OpcodesThatBranch)) {
placeholders[Pair(chunk, index)] = label
}
}