This commit is contained in:
Irmen de Jong 2019-01-14 21:31:14 +01:00
parent 98e95b5707
commit 870c6ea747
13 changed files with 109 additions and 82 deletions

View File

@ -347,7 +347,7 @@ interface IFunctionCall {
interface INameScope { interface INameScope {
val name: String val name: String
val position: Position val position: Position
var statements: MutableList<IStatement> val statements: MutableList<IStatement>
val parent: Node val parent: Node
fun linkParents(parent: Node) fun linkParents(parent: Node)
@ -376,17 +376,16 @@ interface INameScope {
} }
fun getLabelOrVariable(name: String): IStatement? { fun getLabelOrVariable(name: String): IStatement? {
// TODO this call is relatively slow.... cache it? make statement list non-mutable and update the cache when it is explicitly updated?
for (stmt in statements) { for (stmt in statements) {
if (stmt is Label && stmt.name==name) return stmt
if (stmt is VarDecl && stmt.name==name) return stmt if (stmt is VarDecl && stmt.name==name) return stmt
else if (stmt is Label && stmt.name==name) return stmt
} }
return null return null
} }
fun allLabelsAndVariables(): Map<String, IStatement> { fun allLabelsAndVariables(): Set<String> =
val labelsAndVars = statements.filterIsInstance<Label>() + statements.filterIsInstance<VarDecl>() statements.filterIsInstance<Label>().map { it.name }.toSet() + statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
return labelsAndVars.associate { ((it as? Label)?.name ?: (it as? VarDecl)?.name)!! to it }
}
fun lookup(scopedName: List<String>, statement: Node) : IStatement? { fun lookup(scopedName: List<String>, statement: Node) : IStatement? {
if(scopedName.size>1) { if(scopedName.size>1) {
@ -419,19 +418,6 @@ interface INameScope {
} }
} }
fun debugPrint() {
fun printNames(indent: Int, namespace: INameScope) {
println(" ".repeat(4*indent) + "${namespace.name} -> ${namespace::class.simpleName} at ${namespace.position}")
namespace.allLabelsAndVariables().forEach {
println(" ".repeat(4 * (1 + indent)) + "${it.key} -> ${it.value::class.simpleName} at ${it.value.position}")
}
namespace.statements.filterIsInstance<INameScope>().forEach {
printNames(indent+1, it)
}
}
printNames(0, this)
}
fun isEmpty() = statements.isEmpty() fun isEmpty() = statements.isEmpty()
fun isNotEmpty() = statements.isNotEmpty() fun isNotEmpty() = statements.isNotEmpty()

View File

@ -134,7 +134,7 @@ class AstChecker(private val namespace: INameScope,
printWarning("for loop body is empty", forLoop.position) printWarning("for loop body is empty", forLoop.position)
if(forLoop.iterable is LiteralValue) if(forLoop.iterable is LiteralValue)
checkResult.add(SyntaxError("currently not possible to loop over a literal value directly, use a variable instead", forLoop.position)) // todo loop over literals (by creating a generated variable) checkResult.add(SyntaxError("currently not possible to loop over a literal value directly, define it as a variable instead", forLoop.position)) // todo loop over literals (by creating a generated variable)
if(!forLoop.iterable.isIterable(namespace, heap)) { if(!forLoop.iterable.isIterable(namespace, heap)) {
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position)) checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
@ -330,7 +330,9 @@ class AstChecker(private val namespace: INameScope,
if(subroutine.asmClobbers.intersect(regCounts.keys).isNotEmpty()) if(subroutine.asmClobbers.intersect(regCounts.keys).isNotEmpty())
err("a return register is also in the clobber list") err("a return register is also in the clobber list")
} else { } else {
// TODO: currently, non-asm subroutines can only take numeric arguments // TODO: currently, non-asm subroutines can only take numeric arguments (including floats)
// the way string params are treated is almost okay (their address is passed) but the receiving subroutine treats it as an integer rather than referring back to the original string.
// the way array params are treated is buggy; it thinks the subroutine needs a byte parameter in place of a byte[] ...
if(!subroutine.parameters.all{it.type in NumericDatatypes}) { if(!subroutine.parameters.all{it.type in NumericDatatypes}) {
err("non-asm subroutine can only take numerical parameters (no str/array types) for now") err("non-asm subroutine can only take numerical parameters (no str/array types) for now")
} }

View File

@ -111,12 +111,13 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
} }
// check that there are no local variables that redefine the subroutine's parameters // check that there are no local variables that redefine the subroutine's parameters
val definedNames = subroutine.allLabelsAndVariables() // TODO this call is slow val allDefinedNames = subroutine.allLabelsAndVariables()
val paramNames = subroutine.parameters.map { it.name } val paramNames = subroutine.parameters.map { it.name }.toSet()
val definedNamesCorrespondingToParameters = definedNames.filter { it.key in paramNames } val paramsToCheck = paramNames.intersect(allDefinedNames)
for(name in definedNamesCorrespondingToParameters) { for(name in paramsToCheck) {
if(name.value.position != subroutine.position) val thing = subroutine.getLabelOrVariable(name)!!
nameError(name.key, name.value.position, subroutine) if(thing.position != subroutine.position)
nameError(name, thing.position, subroutine)
} }
// inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines and non-asm parameters) // inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines and non-asm parameters)
@ -126,7 +127,7 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
if(subroutine.asmAddress==null) { if(subroutine.asmAddress==null) {
if(subroutine.asmParameterRegisters.isEmpty()) { if(subroutine.asmParameterRegisters.isEmpty()) {
subroutine.parameters subroutine.parameters
.filter { !definedNames.containsKey(it.name) } .filter { it.name !in allDefinedNames }
.forEach { .forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position) val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position)
vardecl.linkParents(subroutine) vardecl.linkParents(subroutine)

View File

@ -163,7 +163,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
override fun process(block: Block): IStatement { override fun process(block: Block): IStatement {
prog.newBlock(block.scopedname, block.name, block.address, block.options()) prog.newBlock(block.scopedname, block.name, block.address, block.options())
processVariables(block) // @todo optimize initializations with same value: load the value only once (sort on initalization value, datatype ?) processVariables(block)
prog.label("block."+block.scopedname, false) prog.label("block."+block.scopedname, false)
prog.line(block.position) prog.line(block.position)
translate(block.statements) translate(block.statements)
@ -183,7 +183,10 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.START_PROCDEF) prog.instr(Opcode.START_PROCDEF)
prog.line(subroutine.position) prog.line(subroutine.position)
// note: the caller has already written the arguments into the subroutine's parameter variables. // note: the caller has already written the arguments into the subroutine's parameter variables.
translate(subroutine.statements) val (varinits, others) = subroutine.statements.partition { it is VariableInitializationAssignment }
val varInits: List<VariableInitializationAssignment> = varinits as List<VariableInitializationAssignment>
translateVarInits(varInits)
translate(others)
val r= super.process(subroutine) val r= super.process(subroutine)
prog.instr(Opcode.END_PROCDEF) prog.instr(Opcode.END_PROCDEF)
return r return r
@ -197,12 +200,18 @@ private class StatementTranslator(private val prog: IntermediateProgram,
} }
} }
private fun translateVarInits(varinits: List<VariableInitializationAssignment>) {
// sort by datatype and value, so multiple initializations with the same value can be optimized (to load the value just once)
val sortedInits = varinits.sortedWith(compareBy({it.value.resultingDatatype(namespace, heap)}, {it.value.constValue(namespace, heap)?.asNumericValue?.toDouble()}))
for (vi in sortedInits)
translate(vi)
}
private fun translate(statements: List<IStatement>) { private fun translate(statements: List<IStatement>) {
for (stmt: IStatement in statements) { for (stmt: IStatement in statements) {
generatedLabelSequenceNumber++ generatedLabelSequenceNumber++
when (stmt) { when (stmt) {
is Label -> translate(stmt) is Label -> translate(stmt)
is VariableInitializationAssignment -> translate(stmt) // for initializing vars in a scope
is Assignment -> translate(stmt) // normal and augmented assignments is Assignment -> translate(stmt) // normal and augmented assignments
is PostIncrDecr -> translate(stmt) is PostIncrDecr -> translate(stmt)
is Jump -> translate(stmt, null) is Jump -> translate(stmt, null)
@ -1409,13 +1418,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
} }
} }
private fun translate(stmt: VariableInitializationAssignment) {
// this is an assignment to initialize a variable's value in the scope.
// the compiler can perhaps optimize this phase.
// todo: optimize variable init by keeping track of the block of init values so it can be copied as a whole via memcopy instead of all separate load value instructions
translate(stmt as Assignment)
}
private fun translate(stmt: Assignment) { private fun translate(stmt: Assignment) {
prog.line(stmt.position) prog.line(stmt.position)
translate(stmt.value) translate(stmt.value)

View File

@ -44,7 +44,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
optimizeMultipleSequentialLineInstrs() optimizeMultipleSequentialLineInstrs()
optimizeCallReturnIntoJump() optimizeCallReturnIntoJump()
optimizeRestoreXYSaveXYIntoRestoreXY() optimizeRestoreXYSaveXYIntoRestoreXY()
// todo: optimize stackvm code more // todo: add more optimizations to stackvm code
optimizeRemoveNops() // must be done as the last step optimizeRemoveNops() // must be done as the last step
optimizeMultipleSequentialLineInstrs() // once more optimizeMultipleSequentialLineInstrs() // once more
@ -396,9 +396,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) { fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
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("output initial memory values") TODO("add support for writing/reading initial memory values")
}
out.println("%end_memory") out.println("%end_memory")
out.println("%heap") out.println("%heap")
heap.allEntries().forEach { heap.allEntries().forEach {

View File

@ -263,6 +263,7 @@ enum class Opcode {
RRESTORE, // restore all internal registers and status flags RRESTORE, // restore all internal registers and status flags
RRESTOREX, // restore just X (the evaluation stack pointer) RRESTOREX, // restore just X (the evaluation stack pointer)
RRESTOREY, // restore just Y (used in for loops for instance) RRESTOREY, // restore just Y (used in for loops for instance)
NOP, // do nothing NOP, // do nothing
BREAKPOINT, // breakpoint BREAKPOINT, // breakpoint
TERMINATE, // end the program TERMINATE, // end the program

View File

@ -254,7 +254,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
vardecls2asm(block) vardecls2asm(block)
out("") out("")
val instructionPatternWindowSize = 6 val instructionPatternWindowSize = 6 // increase once patterns occur longer than this.
var processed = 0 var processed = 0
if(trace) println("BLOCK: ${block.scopedname} ${block.address ?: ""}") if(trace) println("BLOCK: ${block.scopedname} ${block.address ?: ""}")
@ -3205,7 +3205,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}, },
// 16 bit addition avoiding excessive stack usage // 16 bit addition avoiding excessive stack usage
// @todo optimize this even more with longer asmpatterns (avoid stack use altogether on most common operations) // @todo optimize 8 and 16 bit adds and subs even more with longer asmpatterns (avoid stack use altogether on most common operations)
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_UW), AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_UW),
listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_W)) { segment -> listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_W)) { segment ->
""" """

View File

@ -25,6 +25,10 @@ const val ESTACK_HI = 0xcf00 // $cf00-$cfff inclusive
class C64Zeropage(options: CompilationOptions) : Zeropage(options) { class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// @todo: actually USE the zero page when allocating variables at code generation time
// (ideally, the variables that are used 'most' / inside long loops are allocated in ZP first)
companion object { companion object {
const val SCRATCH_B1 = 0x02 const val SCRATCH_B1 = 0x02
const val SCRATCH_REG = 0x03 // temp storage for a register const val SCRATCH_REG = 0x03 // temp storage for a register

View File

@ -2,7 +2,9 @@
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>

View File

@ -171,5 +171,4 @@ texinfo_documents = [
# -- Options for to do extension ---------------------------------------------- # -- Options for to do extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True todo_include_todos = True

View File

@ -311,7 +311,7 @@ ubyte_assignment_to_ubytearray:
string[mubyte2] = ubarr1[mbyte2] ; via evaluation string[mubyte2] = ubarr1[mbyte2] ; via evaluation
string[mubyte2] = ubarr1[mubyte2] ; via evaluation string[mubyte2] = ubarr1[mubyte2] ; via evaluation
string[mubyte2] = string[mubyte2] ; via evaluation string[mubyte2] = string[mubyte2] ; via evaluation
ubarr1[ubarr2[A]] = ubarr2[ubarr1[Y]] ; via evaluation-- todo check generated asm... ubarr1[ubarr2[A]] = ubarr2[ubarr1[Y]] ; via evaluation
@ -366,7 +366,7 @@ byte_assignment_to_bytearray:
barr2[mubyte2] = barr1[ub] ; via evaluation barr2[mubyte2] = barr1[ub] ; via evaluation
barr2[mubyte2] = barr1[mbyte2] ; via evaluation barr2[mubyte2] = barr1[mbyte2] ; via evaluation
barr2[mubyte2] = barr1[mubyte2] ; via evaluation barr2[mubyte2] = barr1[mubyte2] ; via evaluation
barr1[ubarr2[A]] = barr2[ubarr1[Y]] ; via evaluation-- todo check generated asm... barr1[ubarr2[A]] = barr2[ubarr1[Y]] ; via evaluation
byte_assignment_to_membytearray: byte_assignment_to_membytearray:
@ -420,7 +420,7 @@ byte_assignment_to_membytearray:
mbarr1[mubyte2] = barr1[ub] ; via evaluation mbarr1[mubyte2] = barr1[ub] ; via evaluation
mbarr1[mubyte2] = barr1[mbyte2] ; via evaluation mbarr1[mubyte2] = barr1[mbyte2] ; via evaluation
mbarr1[mubyte2] = barr1[mubyte2] ; via evaluation mbarr1[mubyte2] = barr1[mubyte2] ; via evaluation
mbarr1[ubarr2[A]] = barr2[ubarr1[Y]] ; via evaluation-- todo check generated asm... mbarr1[ubarr2[A]] = barr2[ubarr1[Y]] ; via evaluation

View File

@ -17,7 +17,7 @@
str question = "How are you?\n" str question = "How are you?\n"
; use iteration to write text ; use iteration to write text
for ubyte char in question { ; @todo fix iteration for ubyte char in question {
c64.CHROUT(char) c64.CHROUT(char)
} }

View File

@ -5,40 +5,71 @@
sub start() { sub start() {
c64scr.print_ub(c64utils.str2ubyte("1"))
c64.CHROUT('\n')
c64scr.print_ub(c64utils.str2ubyte("12"))
c64.CHROUT('\n')
c64scr.print_ub(c64utils.str2ubyte("123"))
c64.CHROUT('\n')
c64scr.print_ub(c64utils.str2ubyte("1234"))
c64.CHROUT('\n')
c64scr.print_ub(c64utils.str2ubyte("12xyz"))
c64.CHROUT('\n')
c64.CHROUT('\n')
c64scr.print_ub(c64utils.str2byte("1")) ubyte b1=42
c64.CHROUT('\n') ubyte b2=42
c64scr.print_ub(c64utils.str2byte("12")) ubyte b3=99
c64.CHROUT('\n') ubyte b4=42
c64scr.print_ub(c64utils.str2byte("123")) word w1=42
c64.CHROUT('\n') word w2=42
c64scr.print_ub(c64utils.str2byte("1234")) word w3=99
c64.CHROUT('\n') word w4=42
c64scr.print_ub(c64utils.str2ubyte("12xyz")) uword uw1=42
c64.CHROUT('\n') uword uw2=42
c64.CHROUT('\n') uword uw3=99
uword uw4=42
float f1 = 3.14
float f2 = 3.14
float f3 = 99.0
float f4 = 3.14
str string1="hoi"
str string2="hoi"
str string3="hoi222"
str string4="hoi"
;
; c64scr.print_ub(c64utils.str2ubyte("1"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2ubyte("12"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2ubyte("123"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2ubyte("1234"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2ubyte("12xyz"))
; c64.CHROUT('\n')
; c64.CHROUT('\n')
;
; c64scr.print_ub(c64utils.str2byte("1"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2byte("12"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2byte("123"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2byte("1234"))
; c64.CHROUT('\n')
; c64scr.print_ub(c64utils.str2ubyte("12xyz"))
; c64.CHROUT('\n')
; c64.CHROUT('\n')
;
; c64scr.print_b(c64utils.str2byte("-1"))
; c64.CHROUT('\n')
; c64scr.print_b(c64utils.str2byte("-12"))
; c64.CHROUT('\n')
; c64scr.print_b(c64utils.str2byte("-123"))
; c64.CHROUT('\n')
; c64scr.print_b(c64utils.str2byte("-1111"))
; c64.CHROUT('\n')
; c64scr.print_b(c64utils.str2byte("-12xyz"))
; c64.CHROUT('\n')
}
sub foo(ubyte param1, ubyte param2) {
ubyte local1
}
c64scr.print_b(c64utils.str2byte("-1"))
c64.CHROUT('\n')
c64scr.print_b(c64utils.str2byte("-12"))
c64.CHROUT('\n')
c64scr.print_b(c64utils.str2byte("-123"))
c64.CHROUT('\n')
c64scr.print_b(c64utils.str2byte("-1234"))
c64.CHROUT('\n')
c64scr.print_b(c64utils.str2byte("-12xyz"))
c64.CHROUT('\n')
}
} }