allow goto to take any expression, not only an integer or an identifier (part 1)

This commit is contained in:
Irmen de Jong 2024-12-14 00:56:11 +01:00
parent 697d54e10a
commit cc59069876
18 changed files with 122 additions and 119 deletions

View File

@ -68,12 +68,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"%asm {{ ...${node.assembly.length} characters... }}"
}
is PtJump -> {
if(node.identifier!=null)
"goto ${node.identifier.name}"
else if(node.address!=null)
"goto ${node.address.toHex()}"
else
"???"
"goto ${txt(node.target)}"
}
is PtAsmSub -> {
val params = node.parameters.joinToString(", ") {

View File

@ -116,11 +116,9 @@ class PtIfElse(position: Position) : PtNode(position) {
}
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
val address: UInt?,
position: Position) : PtNode(position) {
class PtJump(val target: PtExpression, position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
target.parent = this
}
}

View File

@ -74,8 +74,9 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
}
}
is PtJump -> {
if(node.address==null) {
val stNode = st.lookup(node.identifier!!.name) ?: throw AssemblyError("name not found ${node.identifier}")
val identifier = node.target as? PtIdentifier
if(identifier!=null) {
val stNode = st.lookup(identifier.name) ?: throw AssemblyError("name not found $identifier")
if (stNode.astNode.definingBlock()?.options?.noSymbolPrefixing != true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
@ -177,10 +178,14 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val prefixedIdent = identifier!!.prefix(this, st)
val jump = PtJump(prefixedIdent, address, position)
jump.parent = parent
return jump
val identifier = target as? PtIdentifier
if(identifier!=null) {
val prefixedIdent = identifier.prefix(this, st)
val jump = PtJump(prefixedIdent, position)
jump.parent = parent
return jump
}
return this
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
@ -1034,20 +1039,18 @@ $repeatLabel""")
}
internal fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
val ident = jump.identifier
val addr = jump.address
return when {
ident!=null -> {
// can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name)
if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
Pair(asmSymbolName(ident), true) // indirect jump if the jump symbol is a variable
else
Pair(asmSymbolName(ident), false)
}
addr!=null -> Pair(addr.toHex(), false)
else -> Pair("????", false)
val ident = jump.target as? PtIdentifier
if(ident!=null) {
// can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name)
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
Pair(asmSymbolName(ident), true) // indirect jump if the jump symbol is a variable
else
Pair(asmSymbolName(ident), false)
}
val addr = jump.target.asConstInteger()
if(addr!=null) return Pair(addr.toHex(), false)
else TODO("GOTO TARGET ${jump.target}")
}
private fun translate(ret: PtReturn) {

View File

@ -277,7 +277,8 @@ class IRCodeGen(
val goto = branch.trueScope.children.firstOrNull() as? PtJump
if (goto is PtJump) {
// special case the form: if_cc goto <place> (with optional else)
val address = goto.address?.toInt()
val address = goto.target.asConstInteger()
val label = (goto.target as? PtIdentifier)?.name
if(address!=null) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
@ -290,8 +291,7 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
}
addInstr(result, branchIns, null)
} else {
val label = goto.identifier!!.name
} else if(label!=null) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@ -303,6 +303,8 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
}
addInstr(result, branchIns, null)
} else {
TODO("GOTO TARGET ${goto.target}")
}
if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope)
@ -1025,7 +1027,12 @@ class IRCodeGen(
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
}
}
it += IRInstruction(Opcode.JUMPI, labelSymbol = goto.identifier!!.name)
val identifier = goto.target as? PtIdentifier
if(identifier!=null) {
it += IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name)
} else {
TODO("GOTO TARGET ${goto.target}")
}
} else {
// normal jump, directly to target with branch opcode
when(condition.operator) {
@ -1045,10 +1052,12 @@ class IRCodeGen(
">=" -> Opcode.BGES
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
it += if (goto.target.asConstInteger() != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger())
else if(goto.target is PtIdentifier)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
TODO("GOTO TARGET ${goto.target}")
}
}
}
@ -1059,11 +1068,15 @@ class IRCodeGen(
}
private fun branchInstr(goto: PtJump, branchOpcode: Opcode): IRInstruction {
return if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
return if (goto.target.asConstInteger() != null)
IRInstruction(branchOpcode, address = goto.target.asConstInteger())
else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
val identifier = goto.target as? PtIdentifier
if(identifier!=null)
IRInstruction(branchOpcode, labelSymbol = identifier.name)
else
TODO("GOTO TARGET ${goto.target}")
}
}
@ -1071,7 +1084,11 @@ class IRCodeGen(
// indirect jump to target so the if has to jump past it instead
val result = mutableListOf<IRCodeChunkBase>()
val afterIfLabel = createLabelName()
val gotoSymbol = goto.identifier!!.name
val identifier = goto.target as? PtIdentifier
if(identifier==null) {
TODO("GOTO TARGET ${goto.target}")
}
val gotoSymbol = identifier.name
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) {
@ -1235,10 +1252,12 @@ class IRCodeGen(
">=" -> if(signed) Opcode.BGES else Opcode.BGE
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
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)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
TODO("GOTO TARGET ${goto.target}")
}
}
} else {
@ -1292,10 +1311,12 @@ class IRCodeGen(
it += branchInstr(goto, opcode)
}
} else {
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
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)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
TODO("GOTO TARGET ${goto.target}")
}
}
}
@ -1628,26 +1649,31 @@ class IRCodeGen(
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null)
chunk += if(jump.address!=null) {
IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
chunk += if(jump.target.asConstInteger()!=null) {
IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger())
} else {
if (jump.identifier != null) {
val identifier = jump.target as? PtIdentifier
if (identifier != null) {
if(isIndirectJump(jump)) {
IRInstruction(Opcode.JUMPI, labelSymbol = jump.identifier!!.name)
IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name)
} else {
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
}
} else
throw AssemblyError("weird jump")
} else {
TODO("GOTO TARGET ${jump.target}")
}
}
result += chunk
return result
}
private fun isIndirectJump(jump: PtJump): Boolean {
if(jump.identifier==null)
if(jump.target.asConstInteger()!=null)
return false
val symbol = symbolTable.lookup(jump.identifier!!.name)
val identifier = jump.target as? PtIdentifier
if(identifier==null)
return true
val symbol = symbolTable.lookup(identifier.name)
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
}

View File

@ -312,7 +312,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@ -320,7 +320,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@ -505,7 +505,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@ -513,7 +513,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)

View File

@ -259,7 +259,7 @@ internal class AstChecker(private val program: Program,
}
override fun visit(jump: Jump) {
val ident = jump.identifier
val ident = jump.target as? IdentifierReference
if(ident!=null) {
val targetStatement = ident.checkFunctionOrLabelExists(program, jump, errors)
if(targetStatement!=null) {
@ -269,11 +269,15 @@ internal class AstChecker(private val program: Program,
if(targetStatement is Subroutine && targetStatement.parameters.any()) {
errors.err("can't jump to a subroutine that takes parameters", jump.position)
}
}
} else {
val addr = jump.target.constValue(program)?.number
if (addr!=null && (addr<0 || addr > 65535))
errors.err("goto address must be uword", jump.position)
val addr = jump.address
if(addr!=null && addr > 65535u)
errors.err("jump address must be valid integer 0..\$ffff", jump.position)
val addressDt = jump.target.inferType(program).getOrUndef()
if(!(addressDt.isUnsignedByte || addressDt.isUnsignedWord))
errors.err("goto address must be uword", jump.position)
}
super.visit(jump)
}
@ -340,7 +344,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = jump.identifier?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
@ -1177,7 +1181,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = jump.identifier?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine

View File

@ -377,7 +377,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
val scopedEndLabel = (srcIf.definingScope.scopedName + endLabel).joinToString(".")
val elseLbl = PtIdentifier(scopedElseLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position)
val endLbl = PtIdentifier(scopedEndLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position)
ifScope.add(PtJump(elseLbl, null, srcIf.position))
ifScope.add(PtJump(elseLbl, srcIf.position))
val elseScope = PtNodeGroup()
branch.add(ifScope)
branch.add(elseScope)
@ -385,7 +385,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
for (stmt in srcIf.truepart.statements)
nodes.add(transformStatement(stmt))
if(srcIf.elsepart.isNotEmpty())
nodes.add(PtJump(endLbl, null, srcIf.position))
nodes.add(PtJump(endLbl, srcIf.position))
nodes.add(PtLabel(elseLabel, srcIf.position))
if(srcIf.elsepart.isNotEmpty()) {
for (stmt in srcIf.elsepart.statements)
@ -449,8 +449,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
}
private fun transform(srcJump: Jump): PtJump {
val identifier = if(srcJump.identifier!=null) transform(srcJump.identifier!!) else null
return PtJump(identifier, srcJump.address, srcJump.position)
val target = transformExpression(srcJump.target)
return PtJump(target, srcJump.position)
}
private fun transform(label: Label): PtLabel =

View File

@ -91,8 +91,9 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
jumpsAndCallsToAugment.add(node)
}
is PtJump -> {
if (node.identifier != null) {
val stNode = st.lookup(node.identifier!!.name)!!
val identifier = node.target as? PtIdentifier
if (identifier != null) {
val stNode = st.lookup(identifier.name)!!
val targetSub = stNode.astNode.definingSub()
if (targetSub != node.definingSub())
jumpsAndCallsToAugment.add(node)
@ -188,7 +189,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
val skiplabel = "prog8_defer_skip_${idx+1}"
val branchcc = PtConditionalBranch(BranchCondition.CC, Position.DUMMY)
branchcc.add(PtNodeGroup().also {
it.add(PtJump(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY), null, Position.DUMMY))
it.add(PtJump(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY), Position.DUMMY))
})
branchcc.add(PtNodeGroup())
defersRoutine.add(branchcc)

View File

@ -261,10 +261,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
override fun visit(jump: Jump) {
output("goto ")
when {
jump.address!=null -> output(jump.address!!.toHex())
jump.identifier!=null -> jump.identifier.accept(this)
}
jump.target.accept(this)
}
override fun visit(ifElse: IfElse) {

View File

@ -159,7 +159,6 @@ class Program(val name: String,
}
fun jumpLabel(label: Label): Jump {
val ident = IdentifierReference(listOf(label.name), label.position)
return Jump(null, ident, label.position)
return Jump(IdentifierReference(listOf(label.name), label.position), label.position)
}
}

View File

@ -301,9 +301,7 @@ private fun ReturnstmtContext.toAst() : Return {
}
private fun UnconditionaljumpContext.toAst(): Jump {
val address = integerliteral()?.toAst()?.number?.toUInt()
val identifier = scoped_identifier()?.toAst()
return Jump(address, identifier, toPosition())
return Jump(expression().toAst(), toPosition())
}
private fun LabeldefContext.toAst(): Statement =

View File

@ -679,30 +679,28 @@ data class AssignTarget(var identifier: IdentifierReference?,
}
class Jump(var address: UInt?,
val identifier: IdentifierReference?,
override val position: Position) : Statement() {
class Jump(var target: Expression, override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
identifier?.linkParents(this)
target.linkParents(this)
}
override fun replaceChildNode(node: Node, replacement: Node) {
if(node===identifier && replacement is NumericLiteral) {
address = replacement.number.toUInt()
if(node===target && replacement is Expression) {
target = replacement
}
else
throw FatalAstException("can't replace $node")
}
override fun copy() = Jump(address, identifier?.copy(), position)
override fun copy() = Jump(target.copy(), position)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun referencesIdentifier(nameInSource: List<String>): Boolean = identifier?.referencesIdentifier(nameInSource)==true
override fun referencesIdentifier(nameInSource: List<String>): Boolean = target.referencesIdentifier(nameInSource)
override fun toString() =
"Jump(addr: $address, identifier: $identifier, pos=$position)"
"Jump($target, pos=$position)"
}
class FunctionCallStatement(override var target: IdentifierReference,

View File

@ -299,7 +299,7 @@ abstract class AstWalker {
fun visit(jump: Jump, parent: Node) {
track(before(jump, parent), jump, parent)
jump.identifier?.accept(this, jump)
jump.target.accept(this, jump)
track(after(jump, parent), jump, parent)
}

View File

@ -63,7 +63,7 @@ interface IAstVisitor {
}
fun visit(jump: Jump) {
jump.identifier?.accept(this)
jump.target.accept(this)
}
fun visit(ifElse: IfElse) {

View File

@ -95,7 +95,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(jump: Jump) {
val otherSub = jump.identifier?.targetSubroutine(program)
val otherSub = (jump.target as? IdentifierReference)?.targetSubroutine(program)
if (otherSub != null) {
jump.definingSubroutine?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub) + otherSub

View File

@ -1,12 +1,16 @@
TODO
====
goto can now accept any expression (instead of just a constant address or an identifier).
document this. + FIX code gen for the case where the target is a non-constant expression! See TODOs with "GOTO TARGET"
...
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- make word arrays split by default (remove @split tag) and use new @notsplit or @linear tag to make an array use the old storage format? Also remove -splitarrays command line option. Document this (also that word arrays can then have length 256 by default as well, and that @linear will reduce it to half.)
- 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>'?
- Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.

View File

@ -1,29 +1,9 @@
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
regparams(true, 42, 9999)
}
goto $3000
goto labeltje
goto cx16.r0+cx16.r1
sub regparams(bool arg1 @R0, byte arg2 @R1, uword arg3 @R2) {
txt.print_bool(arg1)
txt.nl()
txt.print_b(arg2)
txt.nl()
txt.print_uw(arg3)
txt.nl()
cx16.r0=0
cx16.r1=$ffff
cx16.r2=11222
txt.print_bool(arg1)
txt.nl()
txt.print_b(arg2)
txt.nl()
txt.print_uw(arg3)
txt.nl()
labeltje:
}
}

View File

@ -147,7 +147,7 @@ defer: 'defer' (statement | statement_block) ;
labeldef : identifier ':' ;
unconditionaljump : 'goto' (integerliteral | scoped_identifier) ;
unconditionaljump : 'goto' expression ;
directive :
directivename=('%output' | '%launcher' | '%zeropage' | '%zpreserved' | '%zpallowed' | '%address' | '%memtop' | '%import' |