allow taking address of array element

This commit is contained in:
Irmen de Jong 2023-09-17 18:30:57 +02:00
parent ccf6e32bf9
commit 880c0a5da8
21 changed files with 233 additions and 122 deletions

View File

@ -126,7 +126,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
private fun makeInitialArray(value: PtArray): List<StArrayElement> { private fun makeInitialArray(value: PtArray): List<StArrayElement> {
return value.children.map { return value.children.map {
when(it) { when(it) {
is PtAddressOf -> StArrayElement(null, it.identifier.name) is PtAddressOf -> {
if(it.isFromArrayElement)
TODO("address-of array element $it in initial array value")
StArrayElement(null, it.identifier.name)
}
is PtIdentifier -> StArrayElement(null, it.name) is PtIdentifier -> StArrayElement(null, it.name)
is PtNumber -> StArrayElement(it.number, null) is PtNumber -> StArrayElement(it.number, null)
else -> throw AssemblyError("invalid array element $it") else -> throw AssemblyError("invalid array element $it")

View File

@ -24,7 +24,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
infix fun isSameAs(other: PtExpression): Boolean { infix fun isSameAs(other: PtExpression): Boolean {
return when(this) { return when(this) {
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier is PtAddressOf -> {
if(other !is PtAddressOf)
return false
if (other.type!==type || !(other.identifier isSameAs identifier))
return false
if(other.children.size!=children.size)
return false
if(children.size==1)
return true
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
}
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
@ -105,7 +115,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier val identifier: PtIdentifier
get() = children.single() as PtIdentifier get() = children[0] as PtIdentifier
val arrayIndexExpr: PtExpression?
get() = if(children.size==2) children[1] as PtExpression else null
val isFromArrayElement: Boolean
get() = children.size==2
} }

View File

@ -2417,8 +2417,7 @@ $repeatLabel""")
cmp #<$number cmp #<$number
bne $jumpIfFalseLabel bne $jumpIfFalseLabel
cpy #>$number cpy #>$number
bne $jumpIfFalseLabel bne $jumpIfFalseLabel""")
""")
} }
is PtIdentifier -> { is PtIdentifier -> {
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)
@ -2426,18 +2425,21 @@ $repeatLabel""")
cmp ${asmVariableName(right)} cmp ${asmVariableName(right)}
bne $jumpIfFalseLabel bne $jumpIfFalseLabel
cpy ${asmVariableName(right)}+1 cpy ${asmVariableName(right)}+1
bne $jumpIfFalseLabel bne $jumpIfFalseLabel""")
""")
} }
is PtAddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)
val name = asmSymbolName(right.identifier) val name = asmSymbolName(right.identifier)
out(""" if(right.isFromArrayElement) {
cmp #<$name TODO("address-of array element $name at ${right.position}")
bne $jumpIfFalseLabel // assignmentAsmGen.assignAddressOf(target, name, right.arrayIndexExpr)
cpy #>$name } else {
bne $jumpIfFalseLabel out("""
""") cmp #<$name
bne $jumpIfFalseLabel
cpy #>$name
bne $jumpIfFalseLabel""")
}
} }
else -> { else -> {
if(left.isSimple()) { if(left.isSimple()) {
@ -2518,12 +2520,16 @@ $repeatLabel""")
is PtAddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)
val name = asmSymbolName(right.identifier) val name = asmSymbolName(right.identifier)
out(""" if(right.isFromArrayElement) {
cmp #<$name TODO("address-of array element $name at ${right.position}")
bne + } else {
cpy #>$name out("""
beq $jumpIfFalseLabel cmp #<$name
+""") bne +
cpy #>$name
beq $jumpIfFalseLabel
+""")
}
} }
else -> { else -> {
if(left.isSimple()) { if(left.isSimple()) {
@ -2849,8 +2855,12 @@ $repeatLabel""")
is PtAddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(right, RegisterOrPair.AY) assignExpressionToRegister(right, RegisterOrPair.AY)
val name = asmSymbolName(left.identifier) val name = asmSymbolName(left.identifier)
code("#>$name", "#<$name") if(left.isFromArrayElement) {
return true TODO("address-of array element $name at ${left.position}")
} else {
code("#>$name", "#<$name")
return true
}
} }
is PtIdentifier -> { is PtIdentifier -> {
assignExpressionToRegister(right, RegisterOrPair.AY) assignExpressionToRegister(right, RegisterOrPair.AY)
@ -2898,8 +2908,12 @@ $repeatLabel""")
is PtAddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)
val name = asmSymbolName(right.identifier) val name = asmSymbolName(right.identifier)
code("#>$name", "#<$name") if(right.isFromArrayElement) {
return true TODO("address-of array element $name at ${right.position}")
} else {
code("#>$name", "#<$name")
return true
}
} }
is PtIdentifier -> { is PtIdentifier -> {
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)

View File

@ -49,7 +49,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype) DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
DataType.UWORD -> { DataType.UWORD -> {
if(assign.source.datatype in PassByReferenceDatatypes) if(assign.source.datatype in PassByReferenceDatatypes)
assignAddressOf(assign.target, variable) assignAddressOf(assign.target, variable, null, null)
else else
assignVariableWord(assign.target, variable, assign.source.datatype) assignVariableWord(assign.target, variable, assign.source.datatype)
} }
@ -181,7 +181,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(val value = assign.source.expression!!) { when(val value = assign.source.expression!!) {
is PtAddressOf -> { is PtAddressOf -> {
val sourceName = asmgen.asmSymbolName(value.identifier) val sourceName = asmgen.asmSymbolName(value.identifier)
assignAddressOf(assign.target, sourceName) val arrayDt = value.identifier.type
assignAddressOf(assign.target, sourceName, arrayDt, value.arrayIndexExpr)
} }
is PtNumber -> throw AssemblyError("source kind should have been literalnumber") is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
is PtIdentifier -> throw AssemblyError("source kind should have been variable") is PtIdentifier -> throw AssemblyError("source kind should have been variable")
@ -806,28 +807,32 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when (right) { when (right) {
is PtAddressOf -> { is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
val symbol = asmgen.asmVariableName(right.identifier) val symbol = asmgen.asmVariableName(right.identifier)
if(expr.operator=="+") if(right.isFromArrayElement) {
asmgen.out(""" TODO("address-of array element $symbol at ${right.position}")
clc } else {
adc #<$symbol assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
tax if(expr.operator=="+")
tya asmgen.out("""
adc #>$symbol clc
tay adc #<$symbol
txa""") tax
else tya
asmgen.out(""" adc #>$symbol
sec tay
sbc #<$symbol txa""")
tax else
tya asmgen.out("""
sbc #>$symbol sec
tay sbc #<$symbol
txa""") tax
assignRegisterpairWord(target, RegisterOrPair.AY) tya
return true sbc #>$symbol
tay
txa""")
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
}
} }
is PtIdentifier -> { is PtIdentifier -> {
val symname = asmgen.asmVariableName(right) val symname = asmgen.asmVariableName(right)
@ -1619,7 +1624,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
// use subroutine // use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname, null, null)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
val stringVal = (variable as PtVariable).value as PtString val stringVal = (variable as PtVariable).value as PtString
asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" ldy #${stringVal.value.length}")
@ -1632,7 +1637,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname, null, null)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -1640,7 +1645,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt) assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname, null, null)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
return return
@ -2167,12 +2172,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String) { private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
val offset = if(arrayIndexExpr!=null) {
val constIndex = arrayIndexExpr.asConstInteger()
if(constIndex!=null) {
if(arrayDt in SplitWordArrayTypes)
constIndex
else
program.memsizer.memorySize(arrayDt!!, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
} else {
TODO("address-of array element $sourceName with non-const index at ${target.position}")
}
} else 0
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(""" asmgen.out("""
lda #<$sourceName lda #<$sourceName+$offset
ldy #>$sourceName ldy #>$sourceName+$offset
sta ${target.asmVarname} sta ${target.asmVarname}
sty ${target.asmVarname}+1 sty ${target.asmVarname}+1
""") """)
@ -2181,20 +2198,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("can't store word into memory byte") throw AssemblyError("can't store word into memory byte")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.out(" lda #<$sourceName | ldy #>$sourceName") asmgen.out(" lda #<$sourceName+$offset | ldy #>$sourceName+$offset")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx #>$sourceName | lda #<$sourceName") RegisterOrPair.AX -> asmgen.out(" ldx #>$sourceName+$offset | lda #<$sourceName+$offset")
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName") RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName+$offset | lda #<$sourceName+$offset")
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName") RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName+$offset | ldx #<$sourceName+$offset")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
asmgen.out( asmgen.out(
""" """
lda #<$sourceName lda #<$sourceName+$offset
sta cx16.${target.register.toString().lowercase()} sta cx16.${target.register.toString().lowercase()}
lda #>$sourceName lda #>$sourceName+$offset
sta cx16.${target.register.toString().lowercase()}+1 sta cx16.${target.register.toString().lowercase()}+1
""") """)
} }
@ -3622,7 +3639,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${addressLv.number.toHex()}") asmgen.out(" sta ${addressLv.number.toHex()}")
} }
addressOf != null -> { addressOf != null -> {
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}") if(addressOf.isFromArrayElement) {
TODO("address-of array element $addressOf")
} else {
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}")
}
} }
addressExpr is PtIdentifier -> { addressExpr is PtIdentifier -> {
asmgen.storeAIntoPointerVar(addressExpr) asmgen.storeAIntoPointerVar(addressExpr)
@ -3632,8 +3653,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val addrOf = addressExpr.left as? PtAddressOf val addrOf = addressExpr.left as? PtAddressOf
val offset = (addressExpr.right as? PtNumber)?.number?.toInt() val offset = (addressExpr.right as? PtNumber)?.number?.toInt()
if(addrOf!=null && offset!=null) { if(addrOf!=null && offset!=null) {
asmgen.out(" sta ${asmgen.asmSymbolName(addrOf.identifier)}${addressExpr.operator}${offset}") if(addrOf.isFromArrayElement) {
return TODO("address-of array element $addrOf")
} else {
asmgen.out(" sta ${asmgen.asmSymbolName(addrOf.identifier)}${addressExpr.operator}${offset}")
return
}
} }
} }
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true)) if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))

View File

@ -3,10 +3,7 @@ package prog8.codegen.intermediate
import prog8.code.StRomSub import prog8.code.StRomSub
import prog8.code.StSub import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes
import prog8.intermediate.* import prog8.intermediate.*
internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) { internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) {
@ -69,10 +66,28 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val vmDt = irType(expr.type) val vmDt = irType(expr.type)
val symbol = expr.identifier.name val symbol = expr.identifier.name
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location // note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val code = IRCodeChunk(null, null) val result = mutableListOf<IRCodeChunkBase>()
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol) addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
ExpressionCodeResult(code, vmDt, resultRegister, -1) if(expr.isFromArrayElement) {
val indexTr = translateExpression(expr.arrayIndexExpr!!)
addToResult(result, indexTr, indexTr.resultReg, -1)
if(expr.identifier.type in SplitWordArrayTypes) {
result += IRCodeChunk(null, null).also {
// multiply indexTr resultreg by the eltSize and add this to the resultRegister.
it += IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1=resultRegister, reg2=indexTr.resultReg)
}
} else {
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
result += IRCodeChunk(null, null).also {
// multiply indexTr resultreg by the eltSize and add this to the resultRegister.
if(eltSize>1)
it += IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=indexTr.resultReg, immediate = eltSize)
it += IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1=resultRegister, reg2=indexTr.resultReg)
}
}
}
ExpressionCodeResult(result, vmDt, resultRegister, -1)
} }
is PtMemoryByte -> { is PtMemoryByte -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()

View File

@ -30,7 +30,7 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) { if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
val arg = functionCallStatement.args.single() val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference? = if(arg is AddressOf) { val stringVar: IdentifierReference? = if(arg is AddressOf) {
arg.identifier if(arg.arrayIndex==null) arg.identifier else null
} else { } else {
arg as? IdentifierReference arg as? IdentifierReference
} }

View File

@ -42,7 +42,7 @@ private fun compileMain(args: Array<String>): Boolean {
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation") val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen") val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code") val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations") val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".") val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results") val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing") val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")

View File

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

View File

@ -219,6 +219,8 @@ _after:
// @(&var+1) --> msb(var) // @(&var+1) --> msb(var)
val addrOf = memread.addressExpression as? AddressOf val addrOf = memread.addressExpression as? AddressOf
if(addrOf?.arrayIndex!=null)
return noModifications
if(addrOf!=null && addrOf.identifier.inferType(program).isWords) { if(addrOf!=null && addrOf.identifier.inferType(program).isWords) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier), memread.position) val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier), memread.position)
return listOf(IAstModification.ReplaceNode(memread, lsb, parent)) return listOf(IAstModification.ReplaceNode(memread, lsb, parent))

View File

@ -440,6 +440,8 @@ class IntermediateAstMaker(private val program: Program) {
addr.add(PtIdentifier(name+"_lsb", dt, src.identifier.position)) // NOTE: assumes _lsb is first in memory! (immediately followed by _msb) addr.add(PtIdentifier(name+"_lsb", dt, src.identifier.position)) // NOTE: assumes _lsb is first in memory! (immediately followed by _msb)
else else
addr.add(transform(src.identifier)) addr.add(transform(src.identifier))
if(src.arrayIndex!=null)
addr.add(transformExpression(src.arrayIndex!!.indexExpr))
return addr return addr
} }

View File

@ -304,8 +304,8 @@ internal class StatementReorderer(
val eltsize = program.memsizer.memorySize(ArrayToElementTypes.getValue(sourceVar.datatype)) val eltsize = program.memsizer.memorySize(ArrayToElementTypes.getValue(sourceVar.datatype))
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position), val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position),
mutableListOf( mutableListOf(
AddressOf(sourceIdent, assign.position), AddressOf(sourceIdent, null, assign.position),
AddressOf(identifier, assign.position), AddressOf(identifier, null, assign.position),
NumericLiteral.optimalInteger(numelements*eltsize, assign.position) NumericLiteral.optimalInteger(numelements*eltsize, assign.position)
), false, assign.position ), false, assign.position
) )

View File

@ -265,7 +265,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(identifier?.isSubroutineParameter(program)==false) { if(identifier?.isSubroutineParameter(program)==false) {
modifications += IAstModification.ReplaceNode( modifications += IAstModification.ReplaceNode(
identifier, identifier,
AddressOf(identifier, arg.position), AddressOf(identifier, null, arg.position),
call as Node) call as Node)
} }
} else if(arg is NumericLiteral) { } else if(arg is NumericLiteral) {
@ -286,7 +286,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(arg is IdentifierReference && DataType.UWORD == param.type) { if(arg is IdentifierReference && DataType.UWORD == param.type) {
modifications += IAstModification.ReplaceNode( modifications += IAstModification.ReplaceNode(
arg, arg,
AddressOf(arg, arg.position), AddressOf(arg, null, arg.position),
call as Node call as Node
) )
} }
@ -312,7 +312,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(identifier?.isSubroutineParameter(program)==false) { if(identifier?.isSubroutineParameter(program)==false) {
modifications += IAstModification.ReplaceNode( modifications += IAstModification.ReplaceNode(
call.args[index], call.args[index],
AddressOf(identifier, pair.second.position), AddressOf(identifier, null, pair.second.position),
call as Node) call as Node)
break break
} }
@ -325,7 +325,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(pair.second is IdentifierReference && DataType.UWORD in pair.first.possibleDatatypes) { if(pair.second is IdentifierReference && DataType.UWORD in pair.first.possibleDatatypes) {
modifications += IAstModification.ReplaceNode( modifications += IAstModification.ReplaceNode(
call.args[index], call.args[index],
AddressOf(pair.second as IdentifierReference, pair.second.position), AddressOf(pair.second as IdentifierReference, null, pair.second.position),
call as Node call as Node
) )
} }

View File

@ -52,13 +52,13 @@ class TestAsmGenSymbols: StringSpec({
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY) val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY)
val assign1 = Assignment(tgt, IdentifierReference(listOf("localvar"), Position.DUMMY), AssignmentOrigin.USERCODE, 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), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8) val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY) val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)

View File

@ -429,6 +429,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
override fun visit(addressOf: AddressOf) { override fun visit(addressOf: AddressOf) {
output("&") output("&")
addressOf.identifier.accept(this) addressOf.identifier.accept(this)
addressOf.arrayIndex?.accept(this)
} }
override fun visit(inlineAssembly: InlineAssembly) { override fun visit(inlineAssembly: InlineAssembly) {

View File

@ -469,8 +469,15 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
if(directmemory()!=null) if(directmemory()!=null)
return DirectMemoryRead(directmemory().expression().toAst(), toPosition()) return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
if(addressof()!=null) if(addressof()!=null) {
return AddressOf(addressof().scoped_identifier().toAst(), toPosition()) val addressOf = addressof()
val identifier = addressOf.scoped_identifier()
val array = addressOf.arrayindexed()
return if(identifier!=null)
AddressOf(addressof().scoped_identifier().toAst(), null, toPosition())
else
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), toPosition())
}
throw FatalAstException(text) throw FatalAstException(text)
} }

View File

@ -49,7 +49,7 @@ sealed class Expression: Node {
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression) (other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
} }
is AddressOf -> { is AddressOf -> {
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource) (other is AddressOf && other.identifier.nameInSource == identifier.nameInSource && other.arrayIndex==arrayIndex)
} }
is RangeExpression -> { is RangeExpression -> {
(other is RangeExpression && other.from==from && other.to==to && other.step==step) (other is RangeExpression && other.from==from && other.to==to && other.step==step)
@ -369,25 +369,34 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
} }
} }
data class AddressOf(var identifier: IdentifierReference, override val position: Position) : Expression() { data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayIndex?, override val position: Position) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
identifier.parent=this identifier.linkParents(this)
arrayIndex?.linkParents(this)
} }
override val isSimple = true override val isSimple = true
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is IdentifierReference && node===identifier) if(node===identifier) {
identifier = replacement require(replacement is IdentifierReference)
replacement.parent = this identifier = replacement
replacement.parent = this
} else if(node===arrayIndex) {
require(replacement is ArrayIndex)
arrayIndex = replacement
replacement.parent = this
} else {
throw FatalAstException("invalid replace, no child node $node")
}
} }
override fun copy() = AddressOf(identifier.copy(), position) override fun copy() = AddressOf(identifier.copy(), arrayIndex?.copy(), position)
override fun constValue(program: Program): NumericLiteral? = null override fun constValue(program: Program): NumericLiteral? = null
override fun referencesIdentifier(nameInSource: List<String>) = identifier.nameInSource==nameInSource override fun referencesIdentifier(nameInSource: List<String>) = identifier.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD) override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD)
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)

View File

@ -458,6 +458,7 @@ abstract class AstWalker {
fun visit(addressOf: AddressOf, parent: Node) { fun visit(addressOf: AddressOf, parent: Node) {
track(before(addressOf, parent), addressOf, parent) track(before(addressOf, parent), addressOf, parent)
addressOf.identifier.accept(this, addressOf) addressOf.identifier.accept(this, addressOf)
addressOf.arrayIndex?.accept(this)
track(after(addressOf, parent), addressOf, parent) track(after(addressOf, parent), addressOf, parent)
} }

View File

@ -173,6 +173,7 @@ interface IAstVisitor {
fun visit(addressOf: AddressOf) { fun visit(addressOf: AddressOf) {
addressOf.identifier.accept(this) addressOf.identifier.accept(this)
addressOf.arrayIndex?.accept(this)
} }
fun visit(inlineAssembly: InlineAssembly) { fun visit(inlineAssembly: InlineAssembly) {

View File

@ -1,9 +1,6 @@
TODO TODO
==== ====
- allow taking address of array variable (now gives parser error)
- ir: the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!
@ -40,6 +37,8 @@ Compiler:
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.
- ir: add more optimizations in IRPeepholeOptimizer - ir: add more optimizations in IRPeepholeOptimizer
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination) - ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
- ir: the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
maybe another representation is needed once actual codegeneration is done from the IR...?
- PtAst/IR: more complex common subexpression eliminations - PtAst/IR: more complex common subexpression eliminations
- [problematic due to using 64tass:] better support for building library programs, where unused .proc shouldn't be deleted from the assembly? - [problematic due to using 64tass:] better support for building library programs, where unused .proc shouldn't be deleted from the assembly?
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag? Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?

View File

@ -1,37 +1,53 @@
%import textio %import textio
%import floats
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
uword zz = $ea45 ubyte[] barray = [11,22,33]
txt.print_uwhex(zz, true) uword[] warray = [$1234,$5678,$abcd]
uword[] @split split_warray = [$1234,$5678,$abcd]
float[] float_array = [11.11,22.22,33.33]
txt.print("incr of 1: ")
txt.print_uw(&barray)
txt.spc()
txt.print_uw(&barray[0])
txt.spc()
txt.print_uw(&barray[1])
txt.spc()
txt.print_uw(&barray[2])
txt.nl() txt.nl()
;@(&zz) = $11 txt.print("incr of 2: ")
setlsb(zz, 0) txt.print_uw(&warray)
txt.print_uwhex(zz, true) txt.spc()
txt.nl() txt.print_uw(&warray[0])
;@(&zz+1) = $22 txt.spc()
setmsb(zz, 0) txt.print_uw(&warray[1])
txt.print_uwhex(zz, true) txt.spc()
txt.nl() txt.print_uw(&warray[2])
txt.nl() txt.nl()
uword[] @split array = [$1234,$5678,$abcd] ; TODO also with @split txt.print("incr of 1: ")
txt.print_uw(&split_warray)
txt.spc()
txt.print_uw(&split_warray[0])
txt.spc()
txt.print_uw(&split_warray[1])
txt.spc()
txt.print_uw(&split_warray[2])
txt.nl()
ubyte one = 1 txt.print("incr of 4 or 5: ")
ubyte two = 2 txt.print_uw(&float_array)
txt.print_uwhex(array[1], true) txt.spc()
txt.nl() txt.print_uw(&float_array[0])
txt.print_uwhex(array[2], true) txt.spc()
txt.nl() txt.print_uw(&float_array[1])
;@(&array+one*2) = $ff txt.spc()
;@(&array+two*2+1) = $ff txt.print_uw(&float_array[2])
setlsb(array[one],0)
setmsb(array[two],0)
txt.print_uwhex(array[1], true)
txt.nl()
txt.print_uwhex(array[2], true)
txt.nl() txt.nl()
} }
} }

View File

@ -197,7 +197,7 @@ typecast : 'as' datatype;
directmemory : '@' '(' expression ')'; directmemory : '@' '(' expression ')';
addressof : <assoc=right> ADDRESS_OF scoped_identifier ; addressof : <assoc=right> ADDRESS_OF (scoped_identifier | arrayindexed) ;
functioncall : scoped_identifier '(' expression_list? ')' ; functioncall : scoped_identifier '(' expression_list? ')' ;