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 {
val name: String
val position: Position
var statements: MutableList<IStatement>
val statements: MutableList<IStatement>
val parent: Node
fun linkParents(parent: Node)
@ -376,17 +376,16 @@ interface INameScope {
}
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) {
if (stmt is Label && 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
}
fun allLabelsAndVariables(): Map<String, IStatement> {
val labelsAndVars = statements.filterIsInstance<Label>() + statements.filterIsInstance<VarDecl>()
return labelsAndVars.associate { ((it as? Label)?.name ?: (it as? VarDecl)?.name)!! to it }
}
fun allLabelsAndVariables(): Set<String> =
statements.filterIsInstance<Label>().map { it.name }.toSet() + statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
fun lookup(scopedName: List<String>, statement: Node) : IStatement? {
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 isNotEmpty() = statements.isNotEmpty()

View File

@ -134,7 +134,7 @@ class AstChecker(private val namespace: INameScope,
printWarning("for loop body is empty", forLoop.position)
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)) {
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())
err("a return register is also in the clobber list")
} 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}) {
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
val definedNames = subroutine.allLabelsAndVariables() // TODO this call is slow
val paramNames = subroutine.parameters.map { it.name }
val definedNamesCorrespondingToParameters = definedNames.filter { it.key in paramNames }
for(name in definedNamesCorrespondingToParameters) {
if(name.value.position != subroutine.position)
nameError(name.key, name.value.position, subroutine)
val allDefinedNames = subroutine.allLabelsAndVariables()
val paramNames = subroutine.parameters.map { it.name }.toSet()
val paramsToCheck = paramNames.intersect(allDefinedNames)
for(name in paramsToCheck) {
val thing = subroutine.getLabelOrVariable(name)!!
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)
@ -126,7 +127,7 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
if(subroutine.asmAddress==null) {
if(subroutine.asmParameterRegisters.isEmpty()) {
subroutine.parameters
.filter { !definedNames.containsKey(it.name) }
.filter { it.name !in allDefinedNames }
.forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position)
vardecl.linkParents(subroutine)

View File

@ -163,7 +163,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
override fun process(block: Block): IStatement {
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.line(block.position)
translate(block.statements)
@ -183,7 +183,10 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.START_PROCDEF)
prog.line(subroutine.position)
// 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)
prog.instr(Opcode.END_PROCDEF)
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>) {
for (stmt: IStatement in statements) {
generatedLabelSequenceNumber++
when (stmt) {
is Label -> translate(stmt)
is VariableInitializationAssignment -> translate(stmt) // for initializing vars in a scope
is Assignment -> translate(stmt) // normal and augmented assignments
is PostIncrDecr -> translate(stmt)
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) {
prog.line(stmt.position)
translate(stmt.value)

View File

@ -44,7 +44,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
optimizeMultipleSequentialLineInstrs()
optimizeCallReturnIntoJump()
optimizeRestoreXYSaveXYIntoRestoreXY()
// todo: optimize stackvm code more
// todo: add more optimizations to stackvm code
optimizeRemoveNops() // must be done as the last step
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) {
out.println("; stackVM program code for '$name'")
out.println("%memory")
if(memory.isNotEmpty()) {
TODO("output initial memory values")
}
if(memory.isNotEmpty())
TODO("add support for writing/reading initial memory values")
out.println("%end_memory")
out.println("%heap")
heap.allEntries().forEach {

View File

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

View File

@ -254,7 +254,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
vardecls2asm(block)
out("")
val instructionPatternWindowSize = 6
val instructionPatternWindowSize = 6 // increase once patterns occur longer than this.
var processed = 0
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
// @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),
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) {
// @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 {
const val SCRATCH_B1 = 0x02
const val SCRATCH_REG = 0x03 // temp storage for a register

View File

@ -2,7 +2,9 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<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="sourceFolder" forTests="false" />
</component>

View File

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

View File

@ -311,7 +311,7 @@ ubyte_assignment_to_ubytearray:
string[mubyte2] = ubarr1[mbyte2] ; via evaluation
string[mubyte2] = ubarr1[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[mbyte2] ; 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:
@ -420,7 +420,7 @@ byte_assignment_to_membytearray:
mbarr1[mubyte2] = barr1[ub] ; via evaluation
mbarr1[mubyte2] = barr1[mbyte2] ; 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"
; use iteration to write text
for ubyte char in question { ; @todo fix iteration
for ubyte char in question {
c64.CHROUT(char)
}

View File

@ -5,40 +5,71 @@
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"))
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')
ubyte b1=42
ubyte b2=42
ubyte b3=99
ubyte b4=42
word w1=42
word w2=42
word w3=99
word w4=42
uword uw1=42
uword uw2=42
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')
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')
}
sub foo(ubyte param1, ubyte param2) {
ubyte local1
}
}