start using vars instead of callgraph (2)

This commit is contained in:
Irmen de Jong 2022-02-08 19:53:47 +01:00
parent 1903990f30
commit 8c2e6971fc
13 changed files with 194 additions and 190 deletions

View File

@ -777,7 +777,7 @@ $repeatLabel lda $counterVar
val counterVar = makeLabel("counter")
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors)
val result = zeropage.allocate(listOf(counterVar), dt, null, null, stmt.position, errors)
result.fold(
success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally

View File

@ -291,7 +291,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
@ -332,7 +332,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }

View File

@ -32,8 +32,6 @@ internal class ProgramGen(
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
internal fun generate() {
// variables.dump(program.memsizer) // TODO
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
@ -304,30 +302,31 @@ internal class ProgramGen(
}
// string and array variables in zeropage that have initializer value, should be initialized
val stringVarsInZp = allocation.varsInZeropage.filter { it.datatype==DataType.STR && it.value!=null }
val arrayVarsInZp = allocation.varsInZeropage.filter { it.datatype in ArrayDatatypes && it.value!=null }
if(stringVarsInZp.isNotEmpty() || arrayVarsInZp.isNotEmpty()) {
val stringVarsWithInitInZp = asmgen.zeropage.variables.filter { it.value.dt==DataType.STR && it.value.initialStringValue!=null }
val arrayVarsWithInitInZp = asmgen.zeropage.variables.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null }
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
asmgen.out("; zp str and array initializations")
stringVarsInZp.forEach {
stringVarsWithInitInZp.forEach {
val name = asmgen.asmVariableName(it.key)
asmgen.out("""
lda #<${it.name}
ldy #>${it.name}
lda #<${name}
ldy #>${name}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${it.name}_init_value
ldy #>${it.name}_init_value
lda #<${name}_init_value
ldy #>${name}_init_value
jsr prog8_lib.strcpy""")
}
arrayVarsInZp.forEach {
val numelements = (it.value as ArrayLiteralValue).value.size
val size = numelements * program.memsizer.memorySize(ArrayToElementTypes.getValue(it.datatype))
arrayVarsWithInitInZp.forEach {
val size = it.value.size
val name = asmgen.asmVariableName(it.key)
asmgen.out("""
lda #<${it.name}_init_value
ldy #>${it.name}_init_value
lda #<${name}_init_value
ldy #>${name}_init_value
sta cx16.r0L
sty cx16.r0H
lda #<${it.name}
ldy #>${it.name}
lda #<${name}
ldy #>${name}
sta cx16.r1L
sty cx16.r1H
lda #<$size
@ -337,11 +336,16 @@ internal class ProgramGen(
asmgen.out(" jmp +")
}
stringVarsInZp.forEach {
outputStringvar(it, it.name+"_init_value")
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
val sv = it.value.initialStringValue!!
outputStringvar(varname, it.value.dt, sv.encoding, sv.value)
}
arrayVarsInZp.forEach {
vardecl2asm(it, it.name+"_init_value")
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
val av = it.value.initialArrayValue!!
arrayVardecl2asm(varname, it.value.dt, av, null)
}
asmgen.out("""+ tsx
@ -365,7 +369,7 @@ internal class ProgramGen(
if(variable.zeropage != ZeropageWish.NOT_IN_ZEROPAGE &&
variable.datatype in IntegerDatatypes
&& options.zeropage != ZeropageType.DONTUSE) {
val result = asmgen.zeropage.allocate(scopedName, variable.datatype, null, null, errors)
val result = asmgen.zeropage.allocate(scopedName, variable.datatype, null, null, null, errors)
errors.report()
result.fold(
success = { (address, _) -> asmgen.out("${variable.name} = $address\t; zp ${variable.datatype}") },
@ -374,13 +378,13 @@ internal class ProgramGen(
}
} else {
// Var has been placed in ZP, just output the address
val lenspec = when(zpAlloc.second.first) {
val lenspec = when(zpAlloc.dt) {
DataType.FLOAT,
DataType.STR,
in ArrayDatatypes -> " ${zpAlloc.second.second} bytes"
in ArrayDatatypes -> " ${zpAlloc.size} bytes"
else -> ""
}
asmgen.out("${variable.name} = ${zpAlloc.first}\t; zp ${variable.datatype} $lenspec")
asmgen.out("${variable.name} = ${zpAlloc.address}\t; zp ${variable.datatype} $lenspec")
}
}
}
@ -418,66 +422,67 @@ internal class ProgramGen(
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVardecl2asm(name, decl.datatype, decl.value as? ArrayLiteralValue, decl.arraysize?.constIndex())
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun arrayVardecl2asm(varname: String, dt: DataType, value: ArrayLiteralValue?, orNumberOfZeros: Int?) {
when(dt) {
DataType.ARRAY_UB -> {
val data = makeArrayFillDataUnsigned(decl)
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$name\t.byte ${data.joinToString()}")
asmgen.out("$varname\t.byte ${data.joinToString()}")
else {
asmgen.out(name)
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
val data = makeArrayFillDataSigned(decl)
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$name\t.char ${data.joinToString()}")
asmgen.out("$varname\t.char ${data.joinToString()}")
else {
asmgen.out(name)
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
val data = makeArrayFillDataUnsigned(decl)
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$name\t.word ${data.joinToString()}")
asmgen.out("$varname\t.word ${data.joinToString()}")
else {
asmgen.out(name)
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
val data = makeArrayFillDataSigned(decl)
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$name\t.sint ${data.joinToString()}")
asmgen.out("$varname\t.sint ${data.joinToString()}")
else {
asmgen.out(name)
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_F -> {
val array =
if(decl.value!=null)
(decl.value as ArrayLiteralValue).value
else {
// no init value, use zeros
val zero = decl.zeroElementValue()
Array(decl.arraysize!!.constIndex()!!) { zero }
}
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
val floatFills = array.map {
val number = (it as NumericLiteralValue).number
compTarget.machine.getFloat(number).makeFloatFillAsm()
}
asmgen.out(name)
asmgen.out(varname)
for (f in array.zip(floatFills))
asmgen.out(" .byte ${f.second} ; float ${f.first}")
}
else -> {
throw AssemblyError("weird dt")
}
else -> throw AssemblyError("require array dt")
}
}
@ -543,23 +548,21 @@ internal class ProgramGen(
private fun outputStringvar(strdecl: VarDecl, nameOverride: String?=null) {
val varname = nameOverride ?: strdecl.name
val sv = strdecl.value as StringLiteralValue
asmgen.out("$varname\t; ${strdecl.datatype} ${sv.encoding}:\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
val bytes = compTarget.encodeString(sv.value, sv.encoding).plus(0.toUByte())
outputStringvar(varname, strdecl.datatype, sv.encoding, sv.value)
}
private fun outputStringvar(varname: String, dt: DataType, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $dt $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"")
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16))
asmgen.out(" .byte " + chunk.joinToString())
}
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
val array =
if(decl.value!=null)
(decl.value as ArrayLiteralValue).value
else {
// no array init value specified, use a list of zeros
val zero = decl.zeroElementValue()
Array(decl.arraysize!!.constIndex()!!) { zero }
}
return when (decl.datatype) {
private fun makeArrayFillDataUnsigned(dt: DataType, value: ArrayLiteralValue?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
@ -580,23 +583,14 @@ internal class ProgramGen(
else -> throw AssemblyError("weird array elt dt")
}
}
else -> throw AssemblyError("invalid arraysize type")
else -> throw AssemblyError("invalid dt")
}
}
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
val array =
if(decl.value!=null) {
if(decl.value !is ArrayLiteralValue)
throw AssemblyError("can only use array literal values as array initializer value")
(decl.value as ArrayLiteralValue).value
}
else {
// no array init value specified, use a list of zeros
val zero = decl.zeroElementValue()
Array(decl.arraysize!!.constIndex()!!) { zero }
}
return when (decl.datatype) {
private fun makeArrayFillDataSigned(dt: DataType, value: ArrayLiteralValue?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
@ -625,7 +619,7 @@ internal class ProgramGen(
else
"-$$hexnum"
}
else -> throw AssemblyError("invalid arraysize type ${decl.datatype}")
else -> throw AssemblyError("invalid dt")
}
}

View File

@ -1,11 +1,9 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import com.github.michaelbull.result.onSuccess
import com.github.michaelbull.result.onFailure
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IErrorReporter
@ -14,7 +12,6 @@ import prog8.compilerinterface.ZeropageType
internal class VariableAllocation(val vars: IVariablesAndConsts, val errors: IErrorReporter) {
val varsInZeropage = mutableSetOf<VarDecl>()
fun allocateAllZeropageVariables(options: CompilationOptions) {
if(options.zeropage== ZeropageType.DONTUSE)
@ -75,11 +72,8 @@ internal class VariableAllocation(val vars: IVariablesAndConsts, val errors: IEr
}
else -> null
}
val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.position, errors)
result.fold(
success = { varsInZeropage.add(vardecl) },
failure = { errors.err(it.message!!, vardecl.position) }
)
val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors)
result.onFailure { errors.err(it.message!!, vardecl.position) }
}
if(errors.noErrors()) {
varsPreferringZp.forEach { (vardecl, scopedname) ->
@ -92,8 +86,7 @@ internal class VariableAllocation(val vars: IVariablesAndConsts, val errors: IEr
}
else -> null
}
val result = zeropage.allocate(scopedname, vardecl.datatype, arraySize, vardecl.position, errors)
result.onSuccess { varsInZeropage.add(vardecl) }
zeropage.allocate(scopedname, vardecl.datatype, arraySize, vardecl.value, vardecl.position, errors)
// no need to check for error, if there is one, just allocate in normal system ram later.
}
}

View File

@ -41,12 +41,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
removeReservedFromFreePool()
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = (2+reg*2).toUInt() to (DataType.UWORD to 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = (2+reg*2).toUInt() to (DataType.WORD to 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = (2+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = (3+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = (2+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = (3+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sH .. cx16.r15sH
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2, null, null) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2, null, null) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1, null, null) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1, null, null) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1, null, null) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1, null, null) // cx16.r0sH .. cx16.r15sH
}
}
}

View File

@ -3,6 +3,7 @@ package prog8.optimizer
import prog8.ast.*
import prog8.ast.base.DataType
import prog8.ast.base.VarDeclType
import prog8.ast.base.defaultZero
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@ -222,7 +223,7 @@ class UnusedCodeRemover(private val program: Program,
val cvalue1 = assign1.value.constValue(program)
if(cvalue1!=null && cvalue1.number==0.0 && assign2.target.isSameAs(assign1.target, program) && assign2.isAugmentable) {
val value2 = assign2.value
val zero = VarDecl.defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
val zero = defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
when(value2) {
is BinaryExpression -> substituteZeroInBinexpr(value2, zero, assign1, assign2)
is PrefixExpression -> substituteZeroInPrefixexpr(value2, zero, assign1, assign2)

View File

@ -7,7 +7,7 @@ conv {
; ----- number conversions to decimal strings ----
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
str string_out = "????????????????" ; result buffer for the string conversion routines
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)

View File

@ -64,26 +64,26 @@ class TestC64Zeropage: FunSpec({
test("testNames") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target))
var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
result.onFailure { fail(it.toString()) }
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
result.onFailure { fail(it.toString()) }
result = zp.allocate(listOf("varname"), DataType.UBYTE, null, null, errors)
result = zp.allocate(listOf("varname"), DataType.UBYTE, null, null, null, errors)
result.onFailure { fail(it.toString()) }
shouldThrow<IllegalArgumentException> { zp.allocate(listOf("varname"), DataType.UBYTE, null, null, errors) }
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, errors)
shouldThrow<IllegalArgumentException> { zp.allocate(listOf("varname"), DataType.UBYTE, null, null, null, errors) }
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, null, errors)
result.onFailure { fail(it.toString()) }
}
test("testZpFloatEnable") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
result.expectError { "should be allocation error due to disabled floats" }
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target))
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, errors)
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
result.expectError { "should be allocation error due to disabled ZP use" }
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target))
zp3.allocate(emptyList(), DataType.FLOAT, null, null, errors)
zp3.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
}
test("testZpModesWithFloats") {
@ -105,7 +105,7 @@ class TestC64Zeropage: FunSpec({
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target))
println(zp.free)
zp.availableBytes() shouldBe 0
val result = zp.allocate(emptyList(), DataType.BYTE, null, null, errors)
val result = zp.allocate(emptyList(), DataType.BYTE, null, null, null, errors)
result.expectError { "expected error due to disabled ZP use" }
}
@ -118,9 +118,9 @@ class TestC64Zeropage: FunSpec({
zp3.availableBytes() shouldBe 125
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
zp4.availableBytes() shouldBe 239
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, null, errors)
zp4.availableBytes() shouldBe 238
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors)
zp4.availableBytes() shouldBe 237
}
@ -151,19 +151,19 @@ class TestC64Zeropage: FunSpec({
zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
for (i in 0 until zp.availableBytes()) {
val alloc = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
val alloc = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
alloc.getOrElse { throw it }
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
result.expectError { "expected allocation error" }
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors)
result.expectError { "expected allocation error" }
}
@ -172,47 +172,47 @@ class TestC64Zeropage: FunSpec({
zp.availableBytes() shouldBe 239
zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true
var result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
var result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors)
val loc = result.getOrElse { throw it } .first
loc shouldBeGreaterThan 3u
loc shouldNotBeIn zp.free
val num = zp.availableBytes() / 2
for(i in 0..num-3) {
zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors)
}
zp.availableBytes() shouldBe 5
// can't allocate because no more sequential bytes, only fragmented
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors)
result.expectError { "should give allocation error" }
for(i in 0..4) {
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
result.expectError { "should give allocation error" }
}
test("testEfficientAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
zp.availableBytes() shouldBe 18
zp.allocate(emptyList(), DataType.WORD, null, null, errors ).getOrElse{throw it}.first shouldBe 0x04u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.allocate(emptyList(), DataType.WORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u
zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u
zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.availableBytes() shouldBe 0
}
@ -243,9 +243,9 @@ class TestCx16Zeropage: FunSpec({
zp2.availableBytes() shouldBe 175
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target))
zp3.availableBytes() shouldBe 216
zp3.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
zp3.allocate(listOf("test"), DataType.UBYTE, null, null, null, errors)
zp3.availableBytes() shouldBe 215
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors)
zp3.availableBytes() shouldBe 214
}

View File

@ -1,6 +1,7 @@
package prog8.ast.base
import prog8.ast.Node
import prog8.ast.expressions.NumericLiteralValue
import kotlin.io.path.Path
import kotlin.io.path.absolute
@ -196,3 +197,12 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
val DUMMY = Position("<dummy>", 0, 0, 0)
}
}
fun defaultZero(dt: DataType, position: Position) = when(dt) {
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0.0, position)
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0.0, position)
DataType.UWORD, DataType.STR -> NumericLiteralValue(DataType.UWORD, 0.0, position)
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0.0, position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
else -> throw FatalAstException("can only determine default zero value for a numeric type")
}

View File

@ -215,15 +215,6 @@ class VarDecl(val type: VarDeclType,
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array,
isArray = true, sharedWithAsm = false, subroutineParameter = null, position = array.position)
}
fun defaultZero(dt: DataType, position: Position) = when(dt) {
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0.0, position)
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0.0, position)
DataType.UWORD, DataType.STR -> NumericLiteralValue(DataType.UWORD, 0.0, position)
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0.0, position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
else -> throw FatalAstException("can only determine default zero value for a numeric type")
}
}
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
@ -405,12 +396,6 @@ class Assignment(var target: AssignTarget, var value: Expression, var origin: As
return false
}
fun initializerFor(program: Program) =
if(origin==AssignmentOrigin.VARINIT)
target.identifier!!.targetVarDecl(program)
else
null
}
data class AssignTarget(var identifier: IdentifierReference?,

View File

@ -4,6 +4,9 @@ import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import prog8.ast.base.*
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.Expression
import prog8.ast.expressions.StringLiteralValue
class ZeropageAllocationError(message: String) : Exception(message)
@ -16,12 +19,17 @@ abstract class Zeropage(protected val options: CompilationOptions) {
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
data class ZpAllocation(val address: UInt,
val dt: DataType,
val size: Int,
val initialStringValue: StringLiteralValue?,
val initialArrayValue: ArrayLiteralValue?)
// the variables allocated into Zeropage.
// name (scoped) ==> pair of address to (Datatype + bytesize)
protected val allocatedVariables = mutableMapOf<List<String>, Pair<UInt, Pair<DataType, Int>>>()
protected val allocatedVariables = mutableMapOf<List<String>, ZpAllocation>()
private val allocations = mutableMapOf<UInt, Pair<List<String>, DataType>>()
public val variables: Map<List<String>, Pair<UInt, Pair<DataType, Int>>> = allocatedVariables
val variables: Map<List<String>, ZpAllocation> = allocatedVariables
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
@ -43,7 +51,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
return free.windowed(2).any { it[0] == it[1] - 1u }
}
fun allocate(name: List<String>, datatype: DataType, numElements: Int?, position: Position?, errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> {
fun allocate(name: List<String>,
datatype: DataType,
numElements: Int?,
initValue: Expression?,
position: Position?,
errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> {
require(name.isEmpty() || !allocations.values.any { it.first==name } ) {"name can't be allocated twice"}
if(options.zeropage== ZeropageType.DONTUSE)
@ -78,13 +92,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate))
return Ok(Pair(makeAllocation(candidate, 1, datatype, name), 1))
return Ok(Pair(makeAllocation(candidate, 1, datatype, name, initValue), 1))
}
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 1))
return Ok(Pair(makeAllocation(free[0], 1, datatype, name, initValue), 1))
}
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if (sequentialFree(candidate, size))
return Ok(Pair(makeAllocation(candidate, size, datatype, name), size))
return Ok(Pair(makeAllocation(candidate, size, datatype, name, initValue), size))
}
}
}
@ -94,12 +108,18 @@ abstract class Zeropage(protected val options: CompilationOptions) {
private fun reserve(range: UIntRange) = free.removeAll(range)
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>): UInt {
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>, initValue: Expression?): UInt {
require(size>=0)
free.removeAll(address until address+size.toUInt())
allocations[address] = name to datatype
if(name.isNotEmpty())
allocatedVariables[name] = address to (datatype to size)
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
in NumericDatatypes -> ZpAllocation(address, datatype, size, null, null) // numerical variables in zeropage never have an initial value here TODO why not?
DataType.STR -> ZpAllocation(address, datatype, size, initValue as? StringLiteralValue, null)
in ArrayDatatypes -> ZpAllocation(address, datatype, size, null, initValue as? ArrayLiteralValue)
else -> throw AssemblyError("invalid dt")
}
}
return address
}

View File

@ -4,6 +4,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- (newvaralloc) UnusedCodeRemover after(decl: VarDecl): fix that vars defined in a library can also safely be removed if unused. Currently this breaks programs such as textelite (due to diskio.save().end_address ?)
- remove hacks in VariableAllocation
Need help with

View File

@ -4,41 +4,41 @@
main {
ubyte @zp mainglobal1=10
uword [2] nullwords
ubyte [2] nullbytes
uword [2] valuewords = [1111,22222]
ubyte [2] valuebytes = [111,222]
str name = "irmen"
uword [2] @requirezp zpnullwords
ubyte [2] @requirezp zpnullbytes
uword [2] @requirezp zpvaluewords = [11111,22222]
ubyte [2] @requirezp zpvaluebytes = [111,222]
str @requirezp zpname = "irmenzp"
sub start() {
c64.SETNAM(1, "$")
txt.print("1")
txt.print("12")
txt.print("test.")
txt.print("test.")
; foobar(2,1,1,1)
; foobar2(2,1,1,1)
}
sub unusedsubroutine() {
c64.SETNAM(1, "$") ; TODO fix don't remove this interned string because referenced in start()
txt.print("this string should be removed from the pool")
txt.print("this string should be removed from the pool")
txt.print("this string should be removed from the pool")
}
; asmsub foobar2(ubyte argument @A, uword a2 @R0, uword a3 @R1, uword a4 @R2) -> ubyte @A {
; %asm {{
; lda #0
; rts
; }}
; }
; sub foobar(ubyte argument, uword a2, uword a3, uword a4) -> ubyte {
; argument++
; return lsb(a2)
; }
}
foobar {
str name = "don't remove this one"
sub unusedinfoobar() {
name = "should be removed" ; TODO remove from interned strings because subroutine got removed
txt.print_uw(nullwords[1])
txt.nl()
txt.print_ub(nullbytes[1])
txt.nl()
txt.print_uw(valuewords[1])
txt.nl()
txt.print_ub(valuebytes[1])
txt.nl()
txt.print(name)
txt.print("should also be removed2") ; TODO remove from interned strings because subroutine got removed
txt.nl()
txt.nl()
txt.print_uw(zpnullwords[1])
txt.nl()
txt.print_ub(zpnullbytes[1])
txt.nl()
txt.print_uw(zpvaluewords[1])
txt.nl()
txt.print_ub(zpvaluebytes[1])
txt.nl()
txt.print(zpname)
txt.nl()
}
}