From 880c0a5da86464452670a9e6f647d86ec7444f4c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 17 Sep 2023 18:30:57 +0200 Subject: [PATCH] allow taking address of array element --- codeCore/src/prog8/code/SymbolTableMaker.kt | 6 +- codeCore/src/prog8/code/ast/AstExpressions.kt | 19 +++- .../src/prog8/codegen/cpu6502/AsmGen.kt | 54 ++++++---- .../cpu6502/assignment/AssignmentAsmGen.kt | 101 +++++++++++------- .../codegen/intermediate/ExpressionGen.kt | 29 +++-- .../src/prog8/optimizer/StatementOptimizer.kt | 2 +- compiler/src/prog8/CompilerMain.kt | 2 +- .../astprocessing/BeforeAsmTypecastCleaner.kt | 2 +- .../compiler/astprocessing/CodeDesugarer.kt | 2 + .../astprocessing/IntermediateAstMaker.kt | 2 + .../astprocessing/StatementReorderer.kt | 4 +- .../compiler/astprocessing/TypecastsAdder.kt | 8 +- .../test/codegeneration/TestAsmGenSymbols.kt | 14 +-- .../src/prog8/ast/AstToSourceTextConverter.kt | 1 + .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 11 +- .../prog8/ast/expressions/AstExpressions.kt | 25 +++-- compilerAst/src/prog8/ast/walk/AstWalker.kt | 1 + compilerAst/src/prog8/ast/walk/IAstVisitor.kt | 1 + docs/source/todo.rst | 5 +- examples/test.p8 | 64 ++++++----- parser/antlr/Prog8ANTLR.g4 | 2 +- 21 files changed, 233 insertions(+), 122 deletions(-) diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index 86ed2ae76..43af75e08 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -126,7 +126,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp private fun makeInitialArray(value: PtArray): List { return value.children.map { 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 PtNumber -> StArrayElement(it.number, null) else -> throw AssemblyError("invalid array element $it") diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index 8a46a8227..bb82072e7 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -24,7 +24,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit infix fun isSameAs(other: PtExpression): Boolean { 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 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 @@ -105,7 +115,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { 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 } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 6144066cd..87c9f1e44 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -2417,8 +2417,7 @@ $repeatLabel""") cmp #<$number bne $jumpIfFalseLabel cpy #>$number - bne $jumpIfFalseLabel - """) + bne $jumpIfFalseLabel""") } is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.AY) @@ -2426,18 +2425,21 @@ $repeatLabel""") cmp ${asmVariableName(right)} bne $jumpIfFalseLabel cpy ${asmVariableName(right)}+1 - bne $jumpIfFalseLabel - """) + bne $jumpIfFalseLabel""") } is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) - out(""" - cmp #<$name - bne $jumpIfFalseLabel - cpy #>$name - bne $jumpIfFalseLabel - """) + if(right.isFromArrayElement) { + TODO("address-of array element $name at ${right.position}") + // assignmentAsmGen.assignAddressOf(target, name, right.arrayIndexExpr) + } else { + out(""" + cmp #<$name + bne $jumpIfFalseLabel + cpy #>$name + bne $jumpIfFalseLabel""") + } } else -> { if(left.isSimple()) { @@ -2518,12 +2520,16 @@ $repeatLabel""") is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) - out(""" - cmp #<$name - bne + - cpy #>$name - beq $jumpIfFalseLabel -+""") + if(right.isFromArrayElement) { + TODO("address-of array element $name at ${right.position}") + } else { + out(""" + cmp #<$name + bne + + cpy #>$name + beq $jumpIfFalseLabel + +""") + } } else -> { if(left.isSimple()) { @@ -2849,8 +2855,12 @@ $repeatLabel""") is PtAddressOf -> { assignExpressionToRegister(right, RegisterOrPair.AY) val name = asmSymbolName(left.identifier) - code("#>$name", "#<$name") - return true + if(left.isFromArrayElement) { + TODO("address-of array element $name at ${left.position}") + } else { + code("#>$name", "#<$name") + return true + } } is PtIdentifier -> { assignExpressionToRegister(right, RegisterOrPair.AY) @@ -2898,8 +2908,12 @@ $repeatLabel""") is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY) val name = asmSymbolName(right.identifier) - code("#>$name", "#<$name") - return true + if(right.isFromArrayElement) { + TODO("address-of array element $name at ${right.position}") + } else { + code("#>$name", "#<$name") + return true + } } is PtIdentifier -> { assignExpressionToRegister(left, RegisterOrPair.AY) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 37cd3780c..109a3a171 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -49,7 +49,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype) DataType.UWORD -> { if(assign.source.datatype in PassByReferenceDatatypes) - assignAddressOf(assign.target, variable) + assignAddressOf(assign.target, variable, null, null) else assignVariableWord(assign.target, variable, assign.source.datatype) } @@ -181,7 +181,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, when(val value = assign.source.expression!!) { is PtAddressOf -> { 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 PtIdentifier -> throw AssemblyError("source kind should have been variable") @@ -806,28 +807,32 @@ internal class AssignmentAsmGen(private val program: PtProgram, when (right) { is PtAddressOf -> { - assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) val symbol = asmgen.asmVariableName(right.identifier) - if(expr.operator=="+") - asmgen.out(""" - clc - adc #<$symbol - tax - tya - adc #>$symbol - tay - txa""") - else - asmgen.out(""" - sec - sbc #<$symbol - tax - tya - sbc #>$symbol - tay - txa""") - assignRegisterpairWord(target, RegisterOrPair.AY) - return true + if(right.isFromArrayElement) { + TODO("address-of array element $symbol at ${right.position}") + } else { + assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) + if(expr.operator=="+") + asmgen.out(""" + clc + adc #<$symbol + tax + tya + adc #>$symbol + tay + txa""") + else + asmgen.out(""" + sec + sbc #<$symbol + tax + tya + sbc #>$symbol + tay + txa""") + assignRegisterpairWord(target, RegisterOrPair.AY) + return true + } } is PtIdentifier -> { val symname = asmgen.asmVariableName(right) @@ -1619,7 +1624,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, // use subroutine assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) 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) val stringVal = (variable as PtVariable).value as PtString asmgen.out(" ldy #${stringVal.value.length}") @@ -1632,7 +1637,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, DataType.ARRAY_B, DataType.ARRAY_UB -> { assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) 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.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_bytearray") @@ -1640,7 +1645,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } DataType.ARRAY_W, DataType.ARRAY_UW -> { 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(" jsr prog8_lib.containment_wordarray") 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) { TargetStorageKind.VARIABLE -> { asmgen.out(""" - lda #<$sourceName - ldy #>$sourceName + lda #<$sourceName+$offset + ldy #>$sourceName+$offset sta ${target.asmVarname} sty ${target.asmVarname}+1 """) @@ -2181,20 +2198,20 @@ internal class AssignmentAsmGen(private val program: PtProgram, throw AssemblyError("can't store word into memory byte") } TargetStorageKind.ARRAY -> { - asmgen.out(" lda #<$sourceName | ldy #>$sourceName") + asmgen.out(" lda #<$sourceName+$offset | ldy #>$sourceName+$offset") assignRegisterpairWord(target, RegisterOrPair.AY) } TargetStorageKind.REGISTER -> { when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" ldx #>$sourceName | lda #<$sourceName") - RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName") - RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName") + RegisterOrPair.AX -> asmgen.out(" ldx #>$sourceName+$offset | lda #<$sourceName+$offset") + RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName+$offset | lda #<$sourceName+$offset") + RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName+$offset | ldx #<$sourceName+$offset") in Cx16VirtualRegisters -> { asmgen.out( """ - lda #<$sourceName + lda #<$sourceName+$offset sta cx16.${target.register.toString().lowercase()} - lda #>$sourceName + lda #>$sourceName+$offset sta cx16.${target.register.toString().lowercase()}+1 """) } @@ -3622,7 +3639,11 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.out(" sta ${addressLv.number.toHex()}") } 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 -> { asmgen.storeAIntoPointerVar(addressExpr) @@ -3632,8 +3653,12 @@ internal class AssignmentAsmGen(private val program: PtProgram, val addrOf = addressExpr.left as? PtAddressOf val offset = (addressExpr.right as? PtNumber)?.number?.toInt() if(addrOf!=null && offset!=null) { - asmgen.out(" sta ${asmgen.asmSymbolName(addrOf.identifier)}${addressExpr.operator}${offset}") - return + if(addrOf.isFromArrayElement) { + 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)) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 3848552c0..91cf526ac 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -3,10 +3,7 @@ package prog8.codegen.intermediate import prog8.code.StRomSub import prog8.code.StSub import prog8.code.ast.* -import prog8.code.core.AssemblyError -import prog8.code.core.DataType -import prog8.code.core.PassByValueDatatypes -import prog8.code.core.SignedDatatypes +import prog8.code.core.* import prog8.intermediate.* 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 symbol = expr.identifier.name // note: LOAD gets you the address of the symbol, whereas LOADM would get you the value stored at that location - val code = IRCodeChunk(null, null) + val result = mutableListOf() val resultRegister = codeGen.registers.nextFree() - code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol) - ExpressionCodeResult(code, vmDt, resultRegister, -1) + addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null) + 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 -> { val result = mutableListOf() diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index a8a467386..0af583854 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -30,7 +30,7 @@ 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) { - arg.identifier + if(arg.arrayIndex==null) arg.identifier else null } else { arg as? IdentifierReference } diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index cb41a2b47..67ae32b42 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -42,7 +42,7 @@ private fun compileMain(args: Array): Boolean { 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 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 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") diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt index aa561708d..aa33952cb 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt @@ -56,7 +56,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program, listOf( IAstModification.ReplaceNode( typecast, - AddressOf(identifier, typecast.position), + AddressOf(identifier, null, typecast.position), parent ) ) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 38fc88200..f9604067b 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -219,6 +219,8 @@ _after: // @(&var+1) --> msb(var) 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) return listOf(IAstModification.ReplaceNode(memread, lsb, parent)) diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 5ad6a55b1..66af216fa 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -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) else addr.add(transform(src.identifier)) + if(src.arrayIndex!=null) + addr.add(transformExpression(src.arrayIndex!!.indexExpr)) return addr } diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 0338a926d..6d453c192 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -304,8 +304,8 @@ internal class StatementReorderer( val eltsize = program.memsizer.memorySize(ArrayToElementTypes.getValue(sourceVar.datatype)) val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position), mutableListOf( - AddressOf(sourceIdent, assign.position), - AddressOf(identifier, assign.position), + AddressOf(sourceIdent, null, assign.position), + AddressOf(identifier, null, assign.position), NumericLiteral.optimalInteger(numelements*eltsize, assign.position) ), false, assign.position ) diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index fa94cf85b..706f7798e 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -265,7 +265,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val if(identifier?.isSubroutineParameter(program)==false) { modifications += IAstModification.ReplaceNode( identifier, - AddressOf(identifier, arg.position), + AddressOf(identifier, null, arg.position), call as Node) } } 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) { modifications += IAstModification.ReplaceNode( arg, - AddressOf(arg, arg.position), + AddressOf(arg, null, arg.position), call as Node ) } @@ -312,7 +312,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val if(identifier?.isSubroutineParameter(program)==false) { modifications += IAstModification.ReplaceNode( call.args[index], - AddressOf(identifier, pair.second.position), + AddressOf(identifier, null, pair.second.position), call as Node) 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) { modifications += IAstModification.ReplaceNode( call.args[index], - AddressOf(pair.second as IdentifierReference, pair.second.position), + AddressOf(pair.second as IdentifierReference, null, pair.second.position), call as Node ) } diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index ff7ecd2ba..83f689631 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -52,13 +52,13 @@ class TestAsmGenSymbols: StringSpec({ 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 assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) - val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) - val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), 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 assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), 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 assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), 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), null, 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), null, 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), null, 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 subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY) diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index f17d0d561..bf0fea972 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -429,6 +429,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: override fun visit(addressOf: AddressOf) { output("&") addressOf.identifier.accept(this) + addressOf.arrayIndex?.accept(this) } override fun visit(inlineAssembly: InlineAssembly) { diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 4c3e5529e..1f9f59b66 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -469,8 +469,15 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression { if(directmemory()!=null) return DirectMemoryRead(directmemory().expression().toAst(), toPosition()) - if(addressof()!=null) - return AddressOf(addressof().scoped_identifier().toAst(), toPosition()) + if(addressof()!=null) { + 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) } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 2730cd8e6..8cdfeccdd 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -49,7 +49,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 is AddressOf && other.identifier.nameInSource == identifier.nameInSource && other.arrayIndex==arrayIndex) } is RangeExpression -> { (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 fun linkParents(parent: Node) { this.parent = parent - identifier.parent=this + identifier.linkParents(this) + arrayIndex?.linkParents(this) } override val isSimple = true override fun replaceChildNode(node: Node, replacement: Node) { - require(replacement is IdentifierReference && node===identifier) - identifier = replacement - replacement.parent = this + if(node===identifier) { + require(replacement is IdentifierReference) + 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 referencesIdentifier(nameInSource: List) = identifier.nameInSource==nameInSource + override fun referencesIdentifier(nameInSource: List) = identifier.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index 0226ad958..0d88e5c04 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -458,6 +458,7 @@ abstract class AstWalker { fun visit(addressOf: AddressOf, parent: Node) { track(before(addressOf, parent), addressOf, parent) addressOf.identifier.accept(this, addressOf) + addressOf.arrayIndex?.accept(this) track(after(addressOf, parent), addressOf, parent) } diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index a7712f1d0..4aaa2a189 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -173,6 +173,7 @@ interface IAstVisitor { fun visit(addressOf: AddressOf) { addressOf.identifier.accept(this) + addressOf.arrayIndex?.accept(this) } fun visit(inlineAssembly: InlineAssembly) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 31389d9a2..da18ce56e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,9 +1,6 @@ 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 .... - 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! @@ -40,6 +37,8 @@ Compiler: 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: 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 - [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? diff --git a/examples/test.p8 b/examples/test.p8 index 0e710841b..b49b6a760 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,37 +1,53 @@ %import textio +%import floats + %zeropage basicsafe main { sub start() { - uword zz = $ea45 - txt.print_uwhex(zz, true) + ubyte[] barray = [11,22,33] + 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() - ;@(&zz) = $11 - setlsb(zz, 0) - txt.print_uwhex(zz, true) - txt.nl() - ;@(&zz+1) = $22 - setmsb(zz, 0) - txt.print_uwhex(zz, true) - txt.nl() + txt.print("incr of 2: ") + txt.print_uw(&warray) + txt.spc() + txt.print_uw(&warray[0]) + txt.spc() + txt.print_uw(&warray[1]) + txt.spc() + txt.print_uw(&warray[2]) 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 - ubyte two = 2 - txt.print_uwhex(array[1], true) - txt.nl() - txt.print_uwhex(array[2], true) - txt.nl() - ;@(&array+one*2) = $ff - ;@(&array+two*2+1) = $ff - setlsb(array[one],0) - setmsb(array[two],0) - txt.print_uwhex(array[1], true) - txt.nl() - txt.print_uwhex(array[2], true) + txt.print("incr of 4 or 5: ") + txt.print_uw(&float_array) + txt.spc() + txt.print_uw(&float_array[0]) + txt.spc() + txt.print_uw(&float_array[1]) + txt.spc() + txt.print_uw(&float_array[2]) txt.nl() } } diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index a664e7131..fa9cc22fa 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -197,7 +197,7 @@ typecast : 'as' datatype; directmemory : '@' '(' expression ')'; -addressof : ADDRESS_OF scoped_identifier ; +addressof : ADDRESS_OF (scoped_identifier | arrayindexed) ; functioncall : scoped_identifier '(' expression_list? ')' ;