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) BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
} }
addInstr(result, branchIns, null) addInstr(result, branchIns, null)
} else if(label!=null) { } else if(label!=null && !isIndirectJump(goto)) {
val branchIns = when(branch.condition) { val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label) BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label) BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@@ -304,7 +304,7 @@ class IRCodeGen(
} }
addInstr(result, branchIns, null) addInstr(result, branchIns, null)
} else { } 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()) if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope) result += translateNode(branch.falseScope)
@@ -1027,12 +1027,12 @@ class IRCodeGen(
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel) it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
} }
} }
val identifier = goto.target as? PtIdentifier // evaluate jump address expression into a register and jump indirectly to it
if(identifier!=null) { val tr = expressionEval.translateExpression(goto.target)
it += IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name) for(i in tr.chunks.flatMap { it.instructions }) {
} else { it += i
TODO("JUMP to expression address ${goto.target}")
} }
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
} else { } else {
// normal jump, directly to target with branch opcode // normal jump, directly to target with branch opcode
when(condition.operator) { when(condition.operator) {
@@ -1054,10 +1054,10 @@ class IRCodeGen(
} }
it += if (goto.target.asConstInteger() != null) it += if (goto.target.asConstInteger() != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger()) 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) IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
else 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 { else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"} require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
val identifier = goto.target as? PtIdentifier val identifier = goto.target as? PtIdentifier
if(identifier!=null) if(identifier!=null && !isIndirectJump(goto))
IRInstruction(branchOpcode, labelSymbol = identifier.name) IRInstruction(branchOpcode, labelSymbol = identifier.name)
else 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 // indirect jump to target so the if has to jump past it instead
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val afterIfLabel = createLabelName() 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) { fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) { if(condition.operator in LogicalOperators) {
@@ -1210,7 +1205,10 @@ class IRCodeGen(
else -> throw AssemblyError("weird if condition ${ifElse.condition}") 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) result += IRCodeChunk(afterIfLabel, null)
return result return result
} }
@@ -1254,10 +1252,10 @@ class IRCodeGen(
} }
if (goto.target.asConstInteger() != null) if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = 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) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
else 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 { } else {
@@ -1313,10 +1311,10 @@ class IRCodeGen(
} else { } else {
if (goto.target.asConstInteger() != null) if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = 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) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
else 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 { private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null) val chunk = IRCodeChunk(null, null)
chunk += if(jump.target.asConstInteger()!=null) { if(jump.target.asConstInteger()!=null) {
IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger()) chunk += 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}")
}
}
result += chunk result += chunk
return result 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 { 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 $ff4a = CLOSE_ALL(ubyte device @A) clobbers(A,X,Y)
extsub $ff59 = LKUPLA(ubyte la @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 $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 $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 $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 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 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. Need library to not call init_system AND init_system_phase2 not either.
Library must not include prog8_program_start stuff 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") - 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) - 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>'? - 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 call() return value handling
- fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported") - 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 - 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) - 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. 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. 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 { main {
sub start() { sub start() {
;str test = '?' * 10 cx16.r0 = $aabb
str name = "foo" cx16.r1 = $1122
cx16.r0 = name+2
goto cx16.r1+256
} }
} }

View File

@@ -65,7 +65,7 @@ storehxy reg1 - store reg1.w into cpu hardware register
CONTROL FLOW 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 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 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) 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] call label(argument register list) [: resultreg.type]
@@ -645,7 +645,7 @@ val instructionFormats = mutableMapOf(
Opcode.STOREHAY to InstructionFormat.from("W,<r1"), Opcode.STOREHAY to InstructionFormat.from("W,<r1"),
Opcode.STOREHXY to InstructionFormat.from("W,<r1"), Opcode.STOREHXY to InstructionFormat.from("W,<r1"),
Opcode.JUMP to InstructionFormat.from("N,<a"), 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.PREPARECALL to InstructionFormat.from("N,<i"),
Opcode.CALLI to InstructionFormat.from("N,<r1"), Opcode.CALLI to InstructionFormat.from("N,<r1"),
Opcode.CALL to InstructionFormat.from("N,call"), Opcode.CALL to InstructionFormat.from("N,call"),

View File

@@ -624,7 +624,9 @@ class VirtualMachine(irProgram: IRProgram) {
} }
private fun InsJUMPI(i: IRInstruction) { 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) pcChunk = artificialLabelAddresses.getValue(artificialAddress)
pcIndex = 0 pcIndex = 0
} }

View File

@@ -123,7 +123,7 @@ class VmProgramLoader {
} }
val label = ins.labelSymbol 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 placeholders[Pair(chunk, index)] = label
} }
} }