mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
start using vars instead of callgraph (2)
This commit is contained in:
parent
1903990f30
commit
8c2e6971fc
@ -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
|
||||
|
@ -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") }
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
@ -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?,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user