make address-of dereference work

This commit is contained in:
Irmen de Jong
2025-04-26 17:35:04 +02:00
parent 5e2d0d0dfc
commit b920d553a0
28 changed files with 188 additions and 122 deletions

View File

@@ -177,7 +177,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
newValue.add(elt)
else {
val newAddr = PtAddressOf(elt.position)
newAddr.children.add(elt.identifier.prefix(newAddr, st))
newAddr.children.add(elt.identifier!!.prefix(newAddr, st))
if (elt.arrayIndexExpr != null)
newAddr.children.add(elt.arrayIndexExpr!!)
newAddr.parent = arrayValue
@@ -1299,8 +1299,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}")
out(" sta ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true
}
}
@@ -1339,8 +1341,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else {
out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}")
out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true
}
}
@@ -1389,8 +1393,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}")
out(" sta ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true
}
}
@@ -1420,8 +1426,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else {
out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}")
out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true
}
}

View File

@@ -799,8 +799,8 @@ _jump jmp (${target.asmLabel})
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
} else {
var varname = asmgen.asmVariableName(value.identifier)
if(value.identifier.type.isSplitWordArray) {
var varname = asmgen.asmVariableName(value.identifier!!)
if(value.identifier!!.type.isSplitWordArray) {
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
}
asmgen.out(" lda #>$varname")
@@ -1598,10 +1598,10 @@ _jump jmp (${target.asmLabel})
if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt)
} else {
val varname = if(left.identifier.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else {
left.identifier.name
left.identifier!!.name
}
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYNotEquals("#<$varname", "#>$varname")
@@ -1650,10 +1650,10 @@ _jump jmp (${target.asmLabel})
if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt)
} else {
val varname = if(left.identifier.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else {
left.identifier.name
left.identifier!!.name
}
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYEquals("#<$varname", "#>$varname")

View File

@@ -438,15 +438,19 @@ internal class AssignmentAsmGen(
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) {
is PtAddressOf -> {
val arrayDt = value.identifier.type
if(value.identifier!=null) {
val arrayDt = value.identifier!!.type
val sourceName =
if (value.isMsbForSplitArray)
asmgen.asmSymbolName(value.identifier) + "_msb"
asmgen.asmSymbolName(value.identifier!!) + "_msb"
else if (arrayDt.isSplitWordArray)
asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory
asmgen.asmSymbolName(value.identifier!!) + "_lsb" // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(value.identifier)
asmgen.asmSymbolName(value.identifier!!)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
} else {
TODO("read &dereference")
}
}
is PtBool -> throw AssemblyError("source kind should have been literalboolean")
is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
@@ -1384,9 +1388,11 @@ internal class AssignmentAsmGen(
is PtAddressOf -> {
if(right.isFromArrayElement) {
TODO("address-of array element at ${right.position}")
} else if(right.dereference!=null) {
TODO("read &dereference")
} else {
var symbol = asmgen.asmVariableName(right.identifier)
if(right.identifier.type.isSplitWordArray) {
var symbol = asmgen.asmVariableName(right.identifier!!)
if(right.identifier!!.type.isSplitWordArray) {
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
}
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
@@ -4019,8 +4025,10 @@ $endLabel""")
addressOf != null -> {
if(addressOf.isFromArrayElement) {
TODO("address-of array element $addressOf")
} else if(addressOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addressOf.position}")
} else {
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}")
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier!!)}")
}
}
addressExpr is PtIdentifier -> {

View File

@@ -511,7 +511,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else if(targetPointerDeref!=null) {
val pointerTr = expressionEval.translateExpression(targetPointerDeref.start)
result += pointerTr.chunks
result += expressionEval.traverseDerefChain(targetPointerDeref, pointerTr.resultReg)
result += expressionEval.traverseDerefChainToCalculateFinalAddress(targetPointerDeref, pointerTr.resultReg)
val instr = when {
targetPointerDeref.type.isByteOrBool -> {
@@ -977,37 +977,33 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
}
else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
else
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
}
else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress)
else
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
}
}
return result

View File

@@ -101,7 +101,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(deref.start)
result += tr.chunks
result += traverseDerefChain(deref, tr.resultReg)
result += traverseDerefChainToCalculateFinalAddress(deref, tr.resultReg)
when {
deref.type.isByteOrBool -> {
val resultReg = codeGen.registers.next(IRDataType.BYTE)
@@ -199,8 +199,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun loadAddressOfArrayLabel(reg: Int) {
if (expr.isMsbForSplitArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_msb"), null)
} else if (identifier.type.isSplitWordArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
} else if (identifier!!.type.isSplitWordArray) {
// the _lsb split array comes first in memory
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
} else
@@ -213,25 +213,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val indexTr = translateExpression(expr.arrayIndexExpr!!)
addToResult(result, indexTr, indexTr.resultReg, -1)
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
if(identifier.type.isUnsignedWord) {
addInstr(
result,
IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = indexWordReg, reg2 = indexTr.resultReg),
null
)
if (identifier!!.type.isUnsignedWord) {
require(!expr.isMsbForSplitArray)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
}
} else {
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
result += IRCodeChunk(null, null).also {
loadAddressOfArrayLabel(resultRegister)
if(eltSize>1 && !identifier.type.isSplitWordArray) {
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1 = indexWordReg, immediate = eltSize)
}
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
}
}
} else {
} else if(expr.identifier!=null ) {
loadAddressOfArrayLabel(resultRegister)
} else {
require(vmDt==IRDataType.WORD)
val pointerTr = translateExpression(expr.dereference!!.start)
result += pointerTr.chunks
result += traverseDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1 = resultRegister, reg2 = pointerTr.resultReg), null)
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
@@ -1473,7 +1483,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun traverseDerefChain(targetPointerDeref: PtPointerDeref, pointerReg: Int): IRCodeChunks {
internal fun traverseDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
var struct: StStruct? = null
if(targetPointerDeref.start.type.subIdentifier!=null)

View File

@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
// @( &thing ) --> thing (but only if thing is a byte type!)
val addrOf = memread.addressExpression as? AddressOf
if(addrOf!=null) {
if(addrOf.identifier.inferType(program).isBytes)
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
if(addrOf.identifier?.inferType(program)?.isBytes==true)
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
}
return noModifications
}

View File

@@ -29,7 +29,14 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference? = if(arg is AddressOf) {
if(arg.arrayIndex==null) arg.identifier else null
if(arg.arrayIndex==null) {
if(arg.identifier!=null)
arg.identifier
else
null // struct can't have string fields so nothing to look at here
} else {
null
}
} else {
arg as? IdentifierReference
}

View File

@@ -713,7 +713,7 @@ internal class AstChecker(private val program: Program,
override fun visit(addressOf: AddressOf) {
checkLongType(addressOf)
val variable=addressOf.identifier.targetVarDecl()
val variable=addressOf.identifier?.targetVarDecl()
if (variable!=null) {
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
errors.err("invalid pointer-of operand type", addressOf.position)
@@ -1987,7 +1987,13 @@ internal class AstChecker(private val program: Program,
val array = value.value.map {
when (it) {
is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.nameInSource.hashCode() and 0xffff
is AddressOf -> {
if(it.identifier!=null)
it.identifier!!.nameInSource.hashCode() and 0xffff
else if(it.dereference!=null)
it.dereference!!.identifier.nameInSource.hashCode() and 0xffff
else 9999999
}
is IdentifierReference -> it.nameInSource.hashCode() and 0xffff
is TypecastExpression if it.type.isBasic -> {
val constVal = it.expression.constValue(program)

View File

@@ -47,7 +47,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
listOf(
IAstModification.ReplaceNode(
typecast,
AddressOf(identifier, null, false, typecast.position),
AddressOf(identifier, null, null, false, typecast.position),
parent
)
)

View File

@@ -282,8 +282,8 @@ _after:
val addrOf = memread.addressExpression as? AddressOf
if(addrOf?.arrayIndex!=null)
return noModifications
if(addrOf!=null && addrOf.identifier.inferType(program).isWords) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier), memread.position)
if(addrOf!=null && addrOf.identifier?.inferType(program)?.isWords==true) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier!!), memread.position)
return listOf(IAstModification.ReplaceNode(memread, lsb, parent))
}
val expr = memread.addressExpression as? BinaryExpression
@@ -291,10 +291,10 @@ _after:
val addressOf = expr.left as? AddressOf
val offset = (expr.right as? NumericLiteral)?.number?.toInt()
if(addressOf!=null && offset==1) {
val variable = addressOf.identifier.targetVarDecl()
val variable = addressOf.identifier?.targetVarDecl()
if(variable!=null && variable.datatype.isWord) {
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(
addressOf.identifier
addressOf.identifier!!
), memread.position)
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
}

View File

@@ -652,9 +652,12 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
private fun transform(src: AddressOf): PtAddressOf {
val addr = PtAddressOf(src.position, src.msb)
addr.add(transform(src.identifier))
if(src.identifier!=null)
addr.add(transform(src.identifier!!))
if (src.arrayIndex != null)
addr.add(transformExpression(src.arrayIndex!!.indexExpr))
if (src.dereference!=null)
addr.add(transformExpression(src.dereference!!))
return addr
}

View File

@@ -267,7 +267,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(!argDt.isString) {
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, false, it.second.position),
AddressOf(identifier, null, null, false, it.second.position),
call as Node
)
}
@@ -285,7 +285,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
// take the address of the identifier
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, false, it.second.position),
AddressOf(identifier, null, null, false, it.second.position),
call as Node
)
}
@@ -410,7 +410,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val eltType = elt.inferType(program)
val tgt = elt.targetStatement(program)
if(eltType.isIterable || tgt is Subroutine || tgt is Label || tgt is Block) {
val addressof = AddressOf(elt, null, false, elt.position)
val addressof = AddressOf(elt, null, null, false, elt.position)
addressof.linkParents(array)
array.value[index] = addressof
}

View File

@@ -61,7 +61,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
str0.value shouldBe "main.bar"
str0.definingScope.name shouldBe "main"
val id1 = (args[1] as AddressOf).identifier
val id1 = (args[1] as AddressOf).identifier!!
val lbl1 = id1.targetStatement(program) as Label
lbl1.name shouldBe "foo_bar"
lbl1.definingScope.name shouldBe "main"

View File

@@ -325,7 +325,7 @@ main {
val assignAddr = (st[2] as Assignment).value
(assignAddr as NumericLiteral).number shouldBe 8194.0
val assignAddr2 = ((st[9] as Assignment).value as AddressOf)
assignAddr2.identifier.nameInSource shouldBe listOf("buffer")
assignAddr2.identifier!!.nameInSource shouldBe listOf("buffer")
assignAddr2.arrayIndex!!.indexExpr shouldBe instanceOf<BinaryExpression>()
}

View File

@@ -60,7 +60,7 @@ class TestIdentifierRef: FunSpec({
val mstmts = (module.statements.single() as Block).statements
val stmts = mstmts.filterIsInstance<Subroutine>().single().statements
val wwref = (stmts[0] as Assignment).target.identifier!!
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier!!
wwref.nameInSource shouldBe listOf("ww")
wwref.wasStringLiteral() shouldBe false
wwref.targetStatement(program) shouldBe instanceOf<VarDecl>()

View File

@@ -65,13 +65,13 @@ class TestAsmGenSymbols: StringSpec({
position = Position.DUMMY
)
val assign1 = Assignment(tgt, IdentifierReference(listOf("localvar"), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), null, null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
@@ -118,13 +118,13 @@ class TestAsmGenSymbols: StringSpec({
val localvarIdent = sub.children.asSequence().filterIsInstance<PtAssignment>().first { it.value is PtIdentifier }.value as PtIdentifier
asmgen.asmSymbolName(localvarIdent) shouldBe "localvar"
asmgen.asmVariableName(localvarIdent) shouldBe "localvar"
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localvarIdentScoped) shouldBe "localvar"
asmgen.asmVariableName(localvarIdentScoped) shouldBe "localvar"
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedVarIdent) shouldBe "main.var_outside"
asmgen.asmVariableName(scopedVarIdent) shouldBe "main.var_outside"
val scopedVarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
val scopedVarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedVarIdentScoped) shouldBe "main.var_outside"
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
}
@@ -134,17 +134,17 @@ class TestAsmGenSymbols: StringSpec({
val asmgen = createTestAsmGen6502(program)
val sub = asmgen.program.entrypoint()!!
val localLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier
val localLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localLabelIdent) shouldBe "locallabel"
asmgen.asmVariableName(localLabelIdent) shouldBe "locallabel"
val localLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier
val localLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localLabelIdentScoped) shouldBe "locallabel"
asmgen.asmVariableName(localLabelIdentScoped) shouldBe "locallabel"
val scopedLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier
val scopedLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedLabelIdent) shouldBe "main.label_outside"
asmgen.asmVariableName(scopedLabelIdent) shouldBe "main.label_outside"
val scopedLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier
val scopedLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedLabelIdentScoped) shouldBe "main.label_outside"
asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside"
}

View File

@@ -482,12 +482,13 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
output("&")
if(addressOf.msb)
output(">")
addressOf.identifier.accept(this)
addressOf.identifier?.accept(this)
if (addressOf.arrayIndex != null) {
output("[")
addressOf.arrayIndex?.accept(this)
output("]")
}
addressOf.dereference?.accept(this)
}
override fun visit(inlineAssembly: InlineAssembly) {

View File

@@ -632,10 +632,10 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
val msb = addressOf.ADDRESS_OF_MSB()!=null
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
return if (identifier != null)
AddressOf(addressof().scoped_identifier().toAst(),null, msb, toPosition())
AddressOf(addressof().scoped_identifier().toAst(),null, null, msb, toPosition())
else {
val array = addressOf.arrayindexed()
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), msb, toPosition())
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), null, msb, toPosition())
}
}

View File

@@ -51,7 +51,7 @@ sealed class Expression: Node {
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
}
is AddressOf -> {
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource && other.arrayIndex==arrayIndex)
(other is AddressOf && other.identifier?.nameInSource == identifier?.nameInSource && other.arrayIndex==arrayIndex && other.dereference==dereference)
}
is RangeExpression -> {
(other is RangeExpression && other.from==from && other.to==to && other.step==step)
@@ -415,13 +415,14 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
}
}
data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayIndex?, val msb: Boolean, override val position: Position) : Expression() {
data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: ArrayIndex?, var dereference: PtrDereference?, val msb: Boolean, override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
identifier.linkParents(this)
identifier?.linkParents(this)
arrayIndex?.linkParents(this)
dereference?.linkParents(this)
}
override val isSimple = true
@@ -432,8 +433,11 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
if(replacement is IdentifierReference) {
identifier = replacement
arrayIndex = null
dereference = null
} else {
TODO("replacement with $replacement")
dereference = replacement as PtrDereference
identifier = null
arrayIndex = null
}
}
node===arrayIndex -> {
@@ -447,11 +451,11 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
replacement.parent = this
}
override fun copy() = AddressOf(identifier.copy(), arrayIndex?.copy(), msb, position)
override fun copy() = AddressOf(identifier?.copy(), arrayIndex?.copy(), dereference?.copy(), msb, position)
override fun constValue(program: Program): NumericLiteral? {
if(msb)
return null
val target = this.identifier.targetStatement(program)
val target = this.identifier?.targetStatement(program)
val targetVar = target as? VarDecl
if(targetVar!=null) {
if (targetVar.type == VarDeclType.MEMORY || targetVar.type == VarDeclType.CONST) {
@@ -482,7 +486,7 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
}
return null
}
override fun referencesIdentifier(nameInSource: List<String>) = identifier.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true
override fun referencesIdentifier(nameInSource: List<String>) = identifier?.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true || dereference?.referencesIdentifier(nameInSource)==true
override fun inferType(program: Program) = InferredTypes.knownFor(BaseDataType.UWORD)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)

View File

@@ -269,7 +269,7 @@ class VarDecl(val type: VarDeclType,
// parameter variable memory mapped to a R0-R15 virtual register
val regname = param.registerOrPair.asScopedNameVirtualReg(param.type)
decltype = VarDeclType.MEMORY
value = AddressOf(IdentifierReference(regname, param.position), null, false, param.position)
value = AddressOf(IdentifierReference(regname, param.position), null, null, false, param.position)
}
val dt = if(param.type.isArray) DataType.UWORD else param.type
return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, SplitWish.DONTCARE, null, param.name, emptyList(), value,
@@ -667,9 +667,10 @@ data class AssignTarget(
multi != null -> false
pointerDereference !=null -> {
if(value is PtrDereference) {
if(pointerDereference!!.identifier!=value.identifier || pointerDereference!!.field!=value.field)
return false
TODO("compare ptrderef chains")
return if(pointerDereference!!.identifier!=value.identifier || pointerDereference!!.field!=value.field)
false
else
pointerDereference!!.chain == value.chain
}
return false
}

View File

@@ -481,8 +481,9 @@ abstract class AstWalker {
fun visit(addressOf: AddressOf, parent: Node) {
track(before(addressOf, parent), addressOf, parent)
addressOf.identifier.accept(this, addressOf)
addressOf.identifier?.accept(this, addressOf)
addressOf.arrayIndex?.accept(this)
addressOf.dereference?.accept(this, addressOf)
track(after(addressOf, parent), addressOf, parent)
}

View File

@@ -185,8 +185,9 @@ interface IAstVisitor {
}
fun visit(addressOf: AddressOf) {
addressOf.identifier.accept(this)
addressOf.identifier?.accept(this)
addressOf.arrayIndex?.accept(this)
addressOf.dereference?.accept(this)
}
fun visit(inlineAssembly: InlineAssembly) {

View File

@@ -101,7 +101,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(addressOf: AddressOf) {
addressOf.identifier.targetSubroutine()?.let { notCalledButReferenced.add(it) }
addressOf.identifier?.targetSubroutine()?.let { notCalledButReferenced.add(it) }
super.visit(addressOf)
}

View File

@@ -7,13 +7,13 @@ STRUCTS and TYPED POINTERS
'DONE' means working in the 'virtual' compiler target... (no 6502 codegen has been touched yet)
- DONE: add ast type check for assignments to struct fields; node_ptr.nextnode = enemy_ptr should error
- ubyte_ptr^^ ++ doesn't get changed to an inplace-assign whereas x++ does
- add IR LOADPIX/STOREPIX instructions for efficient field access through a pointer var?
- change IR instruction LOADI should allow reg1 and reg2 to be the same, so we can remove the extra 'newPointerReg'.
- DONE: declare struct as a separate entity so you can then declare multiple variables (pointers) of the same struct type. Like usual.
- struct is a 'packed' struct, fields are placed in order of declaration. This guarantees exact size and place of the fields
- structs only supported as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
- need to introduce typed pointer datatype in prog8 to allow this to make any sense. + correct code gen
- initially only a pointer-to-struct should actually work, pointer-to-other-type is possible but that can come later.
- DONE: struct is a 'packed' struct, fields are placed in order of declaration. This guarantees exact size and place of the fields
- DONE: structs only supported as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
- DONE: need to introduce typed pointer datatype in prog8 to allow this to make any sense. + correct code gen
- DONE: initially only a pointer-to-struct should actually work, pointer-to-other-type is possible but that can come later.
- DONE: a struct can contain only numeric type fields (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs.
- DONE: struct might also contain typed pointer fields (because a pointer is just an address word)
- DONE: max 1 page of memory total size to allow regular register indexing
@@ -22,14 +22,16 @@ STRUCTS and TYPED POINTERS
- DONE: dereferencing a pointer to struct could look like Pascal's ptr^.field as well, but the ^ is actually redundant here; compiler already knows it's a pointer type.
Note that actually dereferencing a pointer to a struct as an explicit operation, conflicts with the third axiom on this list (structs only as reference types) so it can only be done for basic types?
So... setting struct fields can simply be ``structvar.field = 42`` and reading them ``a = structvar.field``
- you should be able to get the address of an individual field: ``&structpointer.field``
- arrays of structs? Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
- DONE: you should be able to get the address of an individual field: ``&structpointer.field``
- DONE: need to teach sizeof() how to calculate struct sizes (need unit test + doc)
- arrays of structs? Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
- existing STR and ARRAY remain unchanged (don't become typed pointers) so we can keep doing register-indexed addressing directly on them
- rather than str or uword parameter types for routines with a string argument, use ^str (or ^ubyte maybe? these are more or less identical..?)
- same for arrays? pointer-to-array syntax = TBD
- what about pointers to subroutines? should these be typed as well now?
- asm symbol name prefixing should work for dereferences too.
- pointer arithmetic is a pain, but need to follow C? ptr=ptr+10 adds 10*sizeof() instead of just 10.

View File

@@ -75,19 +75,20 @@ main {
node_ptr.nextnode.nextnode.nextnode.nextnode.nextnode.value = 888
cx16.r0=node_ptr.nextnode.nextnode.nextnode.nextnode.value
; BELOW DOESN'T WORK YET:
; address of fields
txt.print("address of field: ")
txt.print_uw(&enemy_ptr.alive)
txt.spc()
enemy_ptr = 8000
txt.print_uw(&enemy_ptr.alive)
txt.nl()
; ubyte_ptr^^ ++
; BELOW DOESN'T WORK YET:
; pointer arithmetic
; ubyte_ptr^^ ++
; enemy_ptr ++ ; add 1*sizeof
; enemy_ptr += 10 ; add 10*sizeof
; TODO how to statically allocate/initialize a struct? Difficult.. see TODO in docs
}
}

View File

@@ -139,7 +139,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
is PtAddressOf -> {
when {
it.isFromArrayElement -> TODO("address-of array element $it in initial array value")
else -> StArrayElement(null, it.identifier.name, null)
else -> StArrayElement(null, it.identifier!!.name, null)
}
}
is PtNumber -> StArrayElement(it.number, null, null)

View File

@@ -27,7 +27,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
return false
if (other.type!==type)
return false
if(!(other.identifier isSameAs identifier))
if(other.identifier==null && identifier!=null)
return false
if(other.identifier!=null && identifier==null)
return false
if(other.identifier!=null && identifier!=null && !(other.identifier!! isSameAs identifier!!))
return false
if(other.dereference==null && identifier!=dereference)
return false
if(other.dereference!=null && identifier==dereference)
return false
if(other.dereference!=null && dereference!=null && !(other.dereference!! isSameAs dereference!!))
return false
if(other.children.size!=children.size)
return false
@@ -141,8 +151,10 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
class PtAddressOf(position: Position, val isMsbForSplitArray: Boolean=false) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier
get() = children[0] as PtIdentifier
val identifier: PtIdentifier?
get() = children[0] as? PtIdentifier
val dereference: PtPointerDeref?
get() = children[0] as? PtPointerDeref
val arrayIndexExpr: PtExpression?
get() = if(children.size==2) children[1] as PtExpression else null

View File

@@ -32,7 +32,12 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtBool -> it.toString()
is PtNumber -> it.number.toString()
is PtIdentifier -> it.name
is PtAddressOf -> "& ${it.identifier.name}"
is PtAddressOf -> {
if(it.identifier!=null)
"& ${it.identifier!!.name}"
else
"& ${txt(it.dereference!!)}"
}
else -> "invalid array element $it"
}
}