Skip to content

Commit 7459896

Browse files
committed
finalized zeropage variable allocation
1 parent 0219c69 commit 7459896

File tree

16 files changed

+124
-187
lines changed

16 files changed

+124
-187
lines changed

compiler/src/prog8/ast/AstChecker.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
4141
if(detailInfo==null)
4242
print("\n")
4343
else
44-
println(": $detailInfo")
44+
println(": $detailInfo\n")
4545
print("\u001b[0m") // normal
4646
}
4747

4848
fun printWarning(msg: String) {
4949
print("\u001b[93m") // bright yellow
5050
print("Warning: $msg")
51-
print("\u001b[0m") // normal
51+
print("\u001b[0m\n") // normal
5252
}
5353

5454
private class AstChecker(private val namespace: INameScope,
@@ -784,7 +784,7 @@ private class AstChecker(private val namespace: INameScope,
784784

785785
if(target is BuiltinFunctionStatementPlaceholder) {
786786
// it's a call to a builtin function.
787-
val func = BuiltinFunctions[target.name]!!
787+
val func = BuiltinFunctions.getValue(target.name)
788788
if(args.size!=func.parameters.size)
789789
checkResult.add(SyntaxError("invalid number of arguments", position))
790790
else {

compiler/src/prog8/ast/AstRecursionChecker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private class DirectedGraph<VT> {
3232
println("#vertices: $numVertices")
3333
graph.forEach { from, to ->
3434
println("$from CALLS:")
35-
to.forEach { it -> println(" $it") }
35+
to.forEach { println(" $it") }
3636
}
3737
val cycle = checkForCycle()
3838
if(cycle.isNotEmpty()) {

compiler/src/prog8/ast/StmtReorderer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ private class StatementReorderer(private val namespace: INameScope, private val
4646
module.statements.removeAll(directives)
4747
module.statements.addAll(0, directives)
4848

49+
// TODO make sure user-defined blocks come BEFORE library blocks
50+
4951
sortConstantAssignments(module.statements)
5052
}
5153

compiler/src/prog8/compiler/Zeropage.kt

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,49 +11,40 @@ abstract class Zeropage(private val options: CompilationOptions) {
1111
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
1212
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
1313

14-
fun available() = free.size
14+
val allowedDatatypes = NumericDatatypes
1515

16-
fun allocate(name: String, type: DataType) =
17-
allocate(VarDecl(VarDeclType.VAR, type, true, null, name, null, Position("",0,0,0)))
16+
fun available() = free.size
1817

19-
fun allocate(vardecl: VarDecl) : Int {
20-
assert(vardecl.name.isEmpty() || !allocations.values.any { it.first==vardecl.name } ) {"same name can't be allocated twice"}
21-
assert(vardecl.type== VarDeclType.VAR) {"can only allocate VAR type"}
18+
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
19+
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"same scopedname can't be allocated twice"}
2220

2321
val size =
24-
if(vardecl.arrayspec!=null) {
25-
printWarning("allocating a large value (arrayspec) in zeropage", vardecl.position)
26-
when(vardecl.datatype) {
27-
DataType.UBYTE, DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
28-
DataType.UWORD, DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
29-
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5
30-
else -> throw CompilerException("array can only be of byte, word, float")
31-
}
32-
} else {
33-
when (vardecl.datatype) {
22+
when (datatype) {
3423
DataType.UBYTE, DataType.BYTE -> 1
3524
DataType.UWORD, DataType.WORD -> 2
3625
DataType.FLOAT -> {
3726
if (options.floats) {
38-
printWarning("allocating a large value (float) in zeropage", vardecl.position)
27+
if(position!=null)
28+
printWarning("allocated a large value (float) in zeropage", position)
29+
else
30+
printWarning("$scopedname: allocated a large value (float) in zeropage")
3931
5
4032
} else throw CompilerException("floating point option not enabled")
4133
}
42-
else -> throw CompilerException("cannot put datatype ${vardecl.datatype} in zeropage")
34+
else -> throw CompilerException("cannot put datatype $datatype in zeropage")
4335
}
44-
}
4536

4637
if(free.size > 0) {
4738
if(size==1) {
4839
for(candidate in free.min()!! .. free.max()!!+1) {
4940
if(loneByte(candidate))
50-
return makeAllocation(candidate, 1, vardecl.datatype, vardecl.name)
41+
return makeAllocation(candidate, 1, datatype, scopedname)
5142
}
52-
return makeAllocation(free[0], 1, vardecl.datatype, vardecl.name)
43+
return makeAllocation(free[0], 1, datatype, scopedname)
5344
}
5445
for(candidate in free.min()!! .. free.max()!!+1) {
5546
if (sequentialFree(candidate, size))
56-
return makeAllocation(candidate, size, vardecl.datatype, vardecl.name)
47+
return makeAllocation(candidate, size, datatype, scopedname)
5748
}
5849
}
5950

compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,15 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
4545
if (zpVariables.isNotEmpty()) {
4646
for (variable in zpVariables) {
4747
try {
48-
val address = zeropage.allocate(variable.key, variable.value.type)
48+
val address = zeropage.allocate(variable.key, variable.value.type, null)
4949
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
50-
println("DEBUG: allocated on ZP: $variable @ $address") //TODO
51-
block.variablesMarkedForZeropage.remove(variable.key)//TODO weg
5250
} catch (x: ZeropageDepletedError) {
5351
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
5452
notAllocated++
5553
}
5654
}
5755
}
5856
}
59-
println("DEBUG: ${allocatedZeropageVariables.size} variables allocated in Zeropage") // TODO
6057
if(notAllocated>0)
6158
printWarning("$notAllocated variables marked for Zeropage could not be allocated there")
6259
}

compiler/src/prog8/compiler/target/c64/AsmGen.kt

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
6262
program.blocks.clear()
6363
program.blocks.addAll(newblocks)
6464

65+
val newAllocatedZp = program.allocatedZeropageVariables.map { symname(it.key, null) to it.value}
66+
program.allocatedZeropageVariables.clear()
67+
program.allocatedZeropageVariables.putAll(newAllocatedZp)
68+
6569
// make a list of all const floats that are used
6670
for(block in program.blocks) {
6771
for(ins in block.instructions.filter{it.arg?.type==DataType.FLOAT}) {
@@ -103,20 +107,27 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
103107
}
104108

105109

106-
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock): String {
110+
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock?): String {
107111
if(' ' in scoped)
108112
return scoped
109-
110113
val blockLocal: Boolean
111-
var name = if (scoped.startsWith("${block.shortname}.")) {
112-
blockLocal = true
113-
scoped.substring(block.shortname.length+1)
114-
} else if (scoped.startsWith("block.")) {
115-
blockLocal = false
116-
scoped
117-
} else {
118-
blockLocal = false
119-
scoped
114+
var name = when {
115+
block==null -> {
116+
blockLocal=true
117+
scoped
118+
}
119+
scoped.startsWith("${block.shortname}.") -> {
120+
blockLocal = true
121+
scoped.substring(block.shortname.length+1)
122+
}
123+
scoped.startsWith("block.") -> {
124+
blockLocal = false
125+
scoped
126+
}
127+
else -> {
128+
blockLocal = false
129+
scoped
130+
}
120131
}
121132
name = name.replace("<<<", "prog8_").replace(">>>", "")
122133
if(name=="-")
@@ -203,7 +214,29 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
203214
out("* = ${block.address?.toHex()}")
204215
}
205216

206-
// @TODO allocate variables on the zeropage as long as there is space
217+
// deal with zeropage variables
218+
for(variable in blk.variables) {
219+
val sym = symname(blk.scopedname+"."+variable.key, null)
220+
val zpVar = program.allocatedZeropageVariables[sym]
221+
if(zpVar==null) {
222+
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
223+
if(variable.value.type in zeropage.allowedDatatypes && variable.value.type != DataType.FLOAT) {
224+
try {
225+
val address = zeropage.allocate(sym, variable.value.type, null)
226+
out("${variable.key} = $address\t; zp ${variable.value.type}")
227+
// make sure we add the var to the set of zpvars for this block
228+
blk.variablesMarkedForZeropage.add(variable.key)
229+
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
230+
} catch (x: ZeropageDepletedError) {
231+
// leave it as it is.
232+
}
233+
}
234+
}
235+
else {
236+
// it was already allocated on the zp
237+
out("${variable.key} = ${zpVar.first}\t; zp ${zpVar.second}")
238+
}
239+
}
207240

208241
out("\n; memdefs and kernel subroutines")
209242
memdefs2asm(block)
@@ -235,7 +268,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
235268
}
236269

237270
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
238-
// these are the non-zeropage variables @todo is this correct now?
271+
// these are the non-zeropage variables
239272
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type }
240273
for (v in sortedVars) {
241274
when (v.second.type) {

compiler/src/prog8/compiler/target/c64/Commodore64.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ const val ESTACK_HI = 0xcf00 // $cf00-$cfff inclusive
2525

2626
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
2727

28-
29-
// @todo: actually USE the zero page when allocating variables at code generation time
30-
// (ideally, the variables that are used 'most' / inside long loops are allocated in ZP first)
31-
3228
companion object {
3329
const val SCRATCH_B1 = 0x02
3430
const val SCRATCH_REG = 0x03 // temp storage for a register

compiler/src/prog8/functions/BuiltinFunctions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
139139
throw FatalAstException("function requires one argument which is an arrayspec $function")
140140
}
141141

142-
val func = BuiltinFunctions[function]!!
142+
val func = BuiltinFunctions.getValue(function)
143143
if(func.returntype!=null)
144144
return func.returntype
145145
// function has return values, but the return type depends on the arguments

compiler/src/prog8/parser/ModuleParsing.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import prog8.compiler.LauncherType
66
import prog8.compiler.OutputType
77
import prog8.determineCompilationOptions
88
import java.io.InputStream
9-
import java.net.URL
109
import java.nio.file.Files
1110
import java.nio.file.Path
1211
import java.nio.file.Paths

compiler/test/UnitTests.kt

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -117,36 +117,29 @@ class TestCompiler {
117117
}
118118

119119

120-
private val dummypos = Position("test", 0, 0, 0)
121-
122-
123120
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
124121
class TestZeropage {
125122
@Test
126123
fun testNames() {
127124
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
128125

126+
zp.allocate("", DataType.UBYTE, null)
127+
zp.allocate("", DataType.UBYTE, null)
128+
zp.allocate("varname", DataType.UBYTE, null)
129129
assertFailsWith<AssertionError> {
130-
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.UBYTE, false, null, "", null, dummypos))
131-
}
132-
133-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
134-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
135-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "varname", null, dummypos))
136-
assertFailsWith<AssertionError> {
137-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "varname", null, dummypos))
130+
zp.allocate("varname", DataType.UBYTE, null)
138131
}
139-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "varname2", null, dummypos))
132+
zp.allocate("varname2", DataType.UBYTE, null)
140133
}
141134

142135
@Test
143136
fun testZpFloatEnable() {
144137
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
145138
assertFailsWith<CompilerException> {
146-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
139+
zp.allocate("", DataType.FLOAT, null)
147140
}
148141
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
149-
zp2.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
142+
zp2.allocate("", DataType.FLOAT, null)
150143
}
151144

152145
@Test
@@ -187,72 +180,72 @@ class TestZeropage {
187180
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
188181
assertEquals(19, zp.available())
189182

190-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
183+
zp.allocate("", DataType.FLOAT, null)
191184
assertFailsWith<ZeropageDepletedError> {
192185
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
193-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
186+
zp.allocate("", DataType.FLOAT, null)
194187
}
195188

196189
for (i in 0 until zp.available()) {
197-
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
190+
val loc = zp.allocate("", DataType.UBYTE, null)
198191
assertTrue(loc > 0)
199192
}
200193
assertEquals(0, zp.available())
201194
assertFailsWith<ZeropageDepletedError> {
202-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
195+
zp.allocate("", DataType.UBYTE, null)
203196
}
204197
assertFailsWith<ZeropageDepletedError> {
205-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos))
198+
zp.allocate("", DataType.UWORD, null)
206199
}
207200
}
208201

209202
@Test
210203
fun testFullAllocation() {
211204
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
212205
assertEquals(238, zp.available())
213-
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
206+
val loc = zp.allocate("", DataType.FLOAT, null)
214207
assertTrue(loc > 3)
215208
assertFalse(loc in zp.free)
216209
val num = zp.available() / 5
217210
val rest = zp.available() % 5
218211

219212
for(i in 0..num-4) {
220-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
213+
zp.allocate("", DataType.FLOAT, null)
221214
}
222215
assertEquals(18,zp.available())
223216

224217
assertFailsWith<ZeropageDepletedError> {
225218
// can't allocate because no more sequential bytes, only fragmented
226-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
219+
zp.allocate("", DataType.FLOAT, null)
227220
}
228221

229222
for(i in 0..13) {
230-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
223+
zp.allocate("", DataType.UBYTE, null)
231224
}
232-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos))
225+
zp.allocate("", DataType.UWORD, null)
233226

234227
assertEquals(2, zp.available())
235-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
236-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
228+
zp.allocate("", DataType.UBYTE, null)
229+
zp.allocate("", DataType.UBYTE, null)
237230
assertFailsWith<ZeropageDepletedError> {
238231
// no more space
239-
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
232+
zp.allocate("", DataType.UBYTE, null)
240233
}
241234
}
242235

243236
@Test
244237
fun testEfficientAllocation() {
245238
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
246239
assertEquals(19, zp.available())
247-
assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos)))
248-
assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos)))
249-
assertEquals(0x0d, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
250-
assertEquals(0x94, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
251-
assertEquals(0xa7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
252-
assertEquals(0xa9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
253-
assertEquals(0xb5, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
254-
assertEquals(0xf7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos)))
255-
assertEquals(0xf9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos)))
240+
assertEquals(0x04, zp.allocate("", DataType.FLOAT, null))
241+
assertEquals(0x09, zp.allocate("", DataType.UBYTE, null))
242+
assertEquals(0x0d, zp.allocate("", DataType.UWORD, null))
243+
assertEquals(0x94, zp.allocate("", DataType.UWORD, null))
244+
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null))
245+
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null))
246+
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null))
247+
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null))
248+
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null))
256249
assertEquals(0, zp.available())
257250
}
258251
}

0 commit comments

Comments
 (0)