auto converting string literals to variables and asm generation

This commit is contained in:
Irmen de Jong 2018-11-24 03:25:59 +01:00
parent 6c8354aef0
commit 849bfde515
8 changed files with 71 additions and 39 deletions

View File

@ -21,9 +21,9 @@ sub start() {
ubyte secretnumber = 0 ubyte secretnumber = 0
memory uword freadstr_arg = $22 ; argument for FREADSTR memory uword freadstr_arg = $22 ; argument for FREADSTR
uword testword uword testword
ubyte char1 = "@" ubyte char1 = "@" ; @todo don't put this on the heap
ubyte char2 = "\n" ubyte char2 = "\n"; @todo don't put this on the heap
ubyte char3 = "\r" ubyte char3 = "\r"; @todo don't put this on the heap
ubyte char1b = '@' ubyte char1b = '@'
ubyte char2b = '\n' ubyte char2b = '\n'
ubyte char3b = '\r' ubyte char3b = '\r'
@ -45,10 +45,12 @@ sub start() {
wordarray[b1] = wordarray wordarray[b1] = wordarray
wordarray[mb1] = stringvar wordarray[mb1] = stringvar
wordarray[mb1] = wordarray wordarray[mb1] = wordarray
; testword = "stringstring" ; @todo asmgen for this testword = "stringstring" ; @todo asmgen for this
; freadstr_arg = "stringstring" ; @todo asmgen for this freadstr_arg = "stringstring" ; @todo asmgen for this
; freadstr_arg = "stringstring2222" ; @todo asmgen for this freadstr_arg = "stringstring2222" ; @todo asmgen for this
; wordarray[1] = "stringstring" ; @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

View File

@ -87,14 +87,14 @@ fun main(args: Array<String>) {
// perform initial syntax checks and constant folding // perform initial syntax checks and constant folding
val heap = HeapValues() val heap = HeapValues()
moduleAst.checkIdentifiers() moduleAst.checkIdentifiers(heap)
moduleAst.constantFold(namespace, heap) moduleAst.constantFold(namespace, heap)
StatementReorderer(namespace, heap).process(moduleAst) // reorder statements to please the compiler later StatementReorderer(namespace, heap).process(moduleAst) // reorder statements to please the compiler later
moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid
// optimize the parse tree // optimize the parse tree
println("Optimizing...") 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) { while(true) {
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = moduleAst.simplifyExpressions(namespace, heap) val optsDone1 = moduleAst.simplifyExpressions(namespace, heap)

View File

@ -47,7 +47,7 @@ class AstChecker(private val namespace: INameScope,
private val checkResult: MutableList<AstException> = mutableListOf() private val checkResult: MutableList<AstException> = mutableListOf()
private val heapStringSentinel: Int private val heapStringSentinel: Int
init { init {
val stringSentinel = heap.allStrings().firstOrNull {it.value.str==""} val stringSentinel = heap.allEntries().firstOrNull {it.value.str==""}
heapStringSentinel = stringSentinel?.index ?: heap.add(DataType.STR, "") heapStringSentinel = stringSentinel?.index ?: heap.add(DataType.STR, "")
} }

View File

@ -1,5 +1,6 @@
package prog8.ast package prog8.ast
import prog8.compiler.HeapValues
import prog8.functions.BuiltinFunctions 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. * Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
*/ */
fun Module.checkIdentifiers(): MutableMap<String, IStatement> { fun Module.checkIdentifiers(heap: HeapValues): MutableMap<String, IStatement> {
val checker = AstIdentifiersChecker() val checker = AstIdentifiersChecker(heap)
this.process(checker) 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) printErrors(checker.result(), name)
return checker.symbols return checker.symbols
} }
class AstIdentifiersChecker : IAstProcessor { class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
private val checkResult: MutableList<AstException> = mutableListOf() private val checkResult: MutableList<AstException> = mutableListOf()
var symbols: MutableMap<String, IStatement> = mutableMapOf() var symbols: MutableMap<String, IStatement> = mutableMapOf()
@ -86,18 +101,20 @@ class AstIdentifiersChecker : IAstProcessor {
nameError(name.key, name.value.position, subroutine) 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: // NOTE:
// - numeric types BYTE and WORD and FLOAT are passed by value; // - 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) // - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
if(subroutine.asmAddress==null) { if(subroutine.asmAddress==null) {
subroutine.parameters if(subroutine.asmParameterRegisters.isEmpty()) {
.filter { !definedNames.containsKey(it.name) } subroutine.parameters
.forEach { .filter { !definedNames.containsKey(it.name) }
val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) .forEach {
vardecl.linkParents(subroutine) val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position)
subroutine.statements.add(0, vardecl) vardecl.linkParents(subroutine)
} subroutine.statements.add(0, vardecl)
}
}
} }
} }
return super.process(subroutine) return super.process(subroutine)
@ -173,4 +190,17 @@ class AstIdentifiersChecker : IAstProcessor {
} }
return super.process(returnStmt) return super.process(returnStmt)
} }
internal val anonymousVariablesFromHeap = mutableSetOf<Pair<LiteralValue, VarDecl>>()
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)
}
} }

View File

@ -99,9 +99,7 @@ class HeapValues {
fun get(heapId: Int): HeapValue = heap[heapId] fun get(heapId: Int): HeapValue = heap[heapId]
fun allStrings() = heap.asSequence().withIndex().filter { it.value.str!=null }.toList() fun allEntries() = heap.withIndex().toList()
fun allArrays() = heap.asSequence().withIndex().filter { it.value.array!=null }.toList()
fun allDoubleArrays() = heap.asSequence().withIndex().filter { it.value.doubleArray!=null }.toList()
} }

View File

@ -312,18 +312,20 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
out.println("; stackVM program code for '$name'") out.println("; stackVM program code for '$name'")
out.println("%memory") out.println("%memory")
if(memory.isNotEmpty()) { if(memory.isNotEmpty()) {
TODO("print out initial memory load") TODO("output initial memory values")
} }
out.println("%end_memory") out.println("%end_memory")
out.println("%heap") out.println("%heap")
heap.allStrings().forEach { heap.allEntries().forEach {
out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"") when {
} it.value.str!=null ->
heap.allArrays().forEach { out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"")
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}") it.value.array!=null ->
} out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
heap.allDoubleArrays().forEach { it.value.doubleArray!=null ->
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}") out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
else -> throw CompilerException("invalid heap entry $it")
}
} }
out.println("%end_heap") out.println("%end_heap")
for(blk in blocks) { for(blk in blocks) {

View File

@ -12,7 +12,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
private var byteval: Short? = null private var byteval: Short? = null
private var wordval: Int? = null private var wordval: Int? = null
private var floatval: Double? = null private var floatval: Double? = null
var heapId: Int = 0 var heapId: Int = -1
private set private set
val asBooleanValue: Boolean val asBooleanValue: Boolean
@ -47,8 +47,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
asBooleanValue = floatval != 0.0 asBooleanValue = floatval != 0.0
} }
else -> { else -> {
if(numericvalueOrHeapId !is Int) if(numericvalueOrHeapId !is Int || numericvalueOrHeapId<0)
throw VmExecutionException("for non-numeric types, the value should be an integer heapId") throw VmExecutionException("for non-numeric types, the value should be a integer heapId >= 0")
heapId = numericvalueOrHeapId heapId = numericvalueOrHeapId
asBooleanValue=true asBooleanValue=true
} }
@ -392,4 +392,4 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
else -> throw VmExecutionException("not can only work on byte/word") else -> throw VmExecutionException("not can only work on byte/word")
} }
} }
} }

View File

@ -1325,13 +1325,13 @@ class StackVm(private var traceOutputFile: String?) {
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code") Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
Opcode.PUSH_ADDR_HEAPVAR -> { Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables[ins.callLabel]!!.heapId val heapId = variables[ins.callLabel]!!.heapId
if(heapId<=0) if(heapId<0)
throw VmExecutionException("expected variable on heap") throw VmExecutionException("expected variable on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string or array variable evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string or array variable
} }
Opcode.PUSH_ADDR_STR -> { Opcode.PUSH_ADDR_STR -> {
val heapId = ins.arg!!.heapId val heapId = ins.arg!!.heapId
if(heapId<=0) if(heapId<0)
throw VmExecutionException("expected string to be on heap") throw VmExecutionException("expected string to be on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string
} }