diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index bf1d1f902..448b3f5d0 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -21,9 +21,9 @@ sub start() { ubyte secretnumber = 0 memory uword freadstr_arg = $22 ; argument for FREADSTR uword testword - ubyte char1 = "@" - ubyte char2 = "\n" - ubyte char3 = "\r" + ubyte char1 = "@" ; @todo don't put this on the heap + ubyte char2 = "\n"; @todo don't put this on the heap + ubyte char3 = "\r"; @todo don't put this on the heap ubyte char1b = '@' ubyte char2b = '\n' ubyte char3b = '\r' @@ -45,10 +45,12 @@ sub start() { wordarray[b1] = wordarray wordarray[mb1] = stringvar wordarray[mb1] = wordarray - ; testword = "stringstring" ; @todo asmgen for this - ; freadstr_arg = "stringstring" ; @todo asmgen for this - ; freadstr_arg = "stringstring2222" ; @todo asmgen for this - ; wordarray[1] = "stringstring" ; @todo asmgen for this + testword = "stringstring" ; @todo asmgen for this + freadstr_arg = "stringstring" ; @todo asmgen for this + freadstr_arg = "stringstring2222" ; @todo asmgen for this + wordarray[1] = "stringstring" ; @todo asmgen for this + wordarray[b1] = "stringstring" ; @todo asmgen for this + wordarray[mb1] = "stringstring" ; @todo asmgen for this diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index e18ed861a..06aeea7a1 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -87,14 +87,14 @@ fun main(args: Array) { // perform initial syntax checks and constant folding val heap = HeapValues() - moduleAst.checkIdentifiers() + moduleAst.checkIdentifiers(heap) moduleAst.constantFold(namespace, heap) StatementReorderer(namespace, heap).process(moduleAst) // reorder statements to please the compiler later moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid // optimize the parse tree println("Optimizing...") - val allScopedSymbolDefinitions = moduleAst.checkIdentifiers() // useful for checking symbol usage later? + val allScopedSymbolDefinitions = moduleAst.checkIdentifiers(heap) // useful for checking symbol usage later? while(true) { // keep optimizing expressions and statements until no more steps remain val optsDone1 = moduleAst.simplifyExpressions(namespace, heap) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 87c9aa59e..7ce816d26 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -47,7 +47,7 @@ class AstChecker(private val namespace: INameScope, private val checkResult: MutableList = mutableListOf() private val heapStringSentinel: Int init { - val stringSentinel = heap.allStrings().firstOrNull {it.value.str==""} + val stringSentinel = heap.allEntries().firstOrNull {it.value.str==""} heapStringSentinel = stringSentinel?.index ?: heap.add(DataType.STR, "") } diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 62ddb278d..c4edcfeb2 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -1,5 +1,6 @@ package prog8.ast +import prog8.compiler.HeapValues import prog8.functions.BuiltinFunctions /** @@ -9,15 +10,29 @@ import prog8.functions.BuiltinFunctions * Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly. */ -fun Module.checkIdentifiers(): MutableMap { - val checker = AstIdentifiersChecker() +fun Module.checkIdentifiers(heap: HeapValues): MutableMap { + val checker = AstIdentifiersChecker(heap) this.process(checker) + + // add any anonymous variables for heap values that are used, and replace literalvalue by identifierref + for (variable in checker.anonymousVariablesFromHeap) { + val scope = variable.first.definingScope() + scope.statements.add(variable.second) + val parent = variable.first.parent + when { + parent is Assignment && parent.value === variable.first -> { + parent.value = IdentifierReference(listOf("auto_heap_value_${variable.first.heapId}"), variable.first.position) + } + else -> TODO("replace literalvalue by identifierref $variable") + } + } + printErrors(checker.result(), name) return checker.symbols } -class AstIdentifiersChecker : IAstProcessor { +class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { private val checkResult: MutableList = mutableListOf() var symbols: MutableMap = mutableMapOf() @@ -86,18 +101,20 @@ class AstIdentifiersChecker : IAstProcessor { nameError(name.key, name.value.position, subroutine) } - // inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines) + // inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines and non-asm parameters) // NOTE: // - numeric types BYTE and WORD and FLOAT are passed by value; // - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter) if(subroutine.asmAddress==null) { - subroutine.parameters - .filter { !definedNames.containsKey(it.name) } - .forEach { - val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) - vardecl.linkParents(subroutine) - subroutine.statements.add(0, vardecl) - } + if(subroutine.asmParameterRegisters.isEmpty()) { + subroutine.parameters + .filter { !definedNames.containsKey(it.name) } + .forEach { + val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) + vardecl.linkParents(subroutine) + subroutine.statements.add(0, vardecl) + } + } } } return super.process(subroutine) @@ -173,4 +190,17 @@ class AstIdentifiersChecker : IAstProcessor { } return super.process(returnStmt) } + + + internal val anonymousVariablesFromHeap = mutableSetOf>() + + override fun process(literalValue: LiteralValue): LiteralValue { + if(literalValue.heapId!=null && literalValue.parent !is VarDecl) { + // a literal value that's not declared as a variable, which refers to something on the heap. + // we need to introduce an auto-generated variable for this to be able to refer to the value! + val variable = VarDecl(VarDeclType.VAR, literalValue.type, null, "auto_heap_value_${literalValue.heapId}", literalValue, literalValue.position) + anonymousVariablesFromHeap.add(Pair(literalValue, variable)) + } + return super.process(literalValue) + } } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index f8ce72528..1e1f8324a 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -99,9 +99,7 @@ class HeapValues { fun get(heapId: Int): HeapValue = heap[heapId] - fun allStrings() = heap.asSequence().withIndex().filter { it.value.str!=null }.toList() - fun allArrays() = heap.asSequence().withIndex().filter { it.value.array!=null }.toList() - fun allDoubleArrays() = heap.asSequence().withIndex().filter { it.value.doubleArray!=null }.toList() + fun allEntries() = heap.withIndex().toList() } diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index 782c72635..fe969d016 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -312,18 +312,20 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap out.println("; stackVM program code for '$name'") out.println("%memory") if(memory.isNotEmpty()) { - TODO("print out initial memory load") + TODO("output initial memory values") } out.println("%end_memory") out.println("%heap") - heap.allStrings().forEach { - out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"") - } - heap.allArrays().forEach { - out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}") - } - heap.allDoubleArrays().forEach { - out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}") + heap.allEntries().forEach { + when { + it.value.str!=null -> + out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"") + it.value.array!=null -> + out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}") + it.value.doubleArray!=null -> + out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}") + else -> throw CompilerException("invalid heap entry $it") + } } out.println("%end_heap") for(blk in blocks) { diff --git a/compiler/src/prog8/compiler/intermediate/Value.kt b/compiler/src/prog8/compiler/intermediate/Value.kt index 0c3d67cbf..c4402dc88 100644 --- a/compiler/src/prog8/compiler/intermediate/Value.kt +++ b/compiler/src/prog8/compiler/intermediate/Value.kt @@ -12,7 +12,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { private var byteval: Short? = null private var wordval: Int? = null private var floatval: Double? = null - var heapId: Int = 0 + var heapId: Int = -1 private set val asBooleanValue: Boolean @@ -47,8 +47,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { asBooleanValue = floatval != 0.0 } else -> { - if(numericvalueOrHeapId !is Int) - throw VmExecutionException("for non-numeric types, the value should be an integer heapId") + if(numericvalueOrHeapId !is Int || numericvalueOrHeapId<0) + throw VmExecutionException("for non-numeric types, the value should be a integer heapId >= 0") heapId = numericvalueOrHeapId asBooleanValue=true } @@ -392,4 +392,4 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { else -> throw VmExecutionException("not can only work on byte/word") } } -} \ No newline at end of file +} diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 8698cf331..1cf37d702 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1325,13 +1325,13 @@ class StackVm(private var traceOutputFile: String?) { Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code") Opcode.PUSH_ADDR_HEAPVAR -> { val heapId = variables[ins.callLabel]!!.heapId - if(heapId<=0) + if(heapId<0) throw VmExecutionException("expected variable on heap") evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string or array variable } Opcode.PUSH_ADDR_STR -> { val heapId = ins.arg!!.heapId - if(heapId<=0) + if(heapId<0) throw VmExecutionException("expected string to be on heap") evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string }