mirror of
https://github.com/irmen/prog8.git
synced 2024-08-11 21:29:03 +00:00
getting rid of old IVariablesAndConsts object
This commit is contained in:
parent
496245c801
commit
cf362c4a61
@ -31,14 +31,14 @@ class AsmGen(internal val program: Program,
|
||||
internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
|
||||
internal val loopEndLabels = ArrayDeque<String>()
|
||||
private val zeropage = options.compTarget.machine.zeropage
|
||||
private val allocator = VariableAllocator(variables, options, errors)
|
||||
private val allocator = VariableAllocator(variables, symbolTable, options, errors)
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val breakpointLabels = mutableListOf<String>()
|
||||
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
|
||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||
private val programGen = ProgramAndVarsGen(program, variables, symbolTable, options, errors, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
|
||||
|
||||
|
@ -23,10 +23,9 @@ import kotlin.math.absoluteValue
|
||||
*/
|
||||
internal class ProgramAndVarsGen(
|
||||
val program: Program,
|
||||
val variables: IVariablesAndConsts,
|
||||
val symboltable: SymbolTable,
|
||||
val options: CompilationOptions,
|
||||
val errors: IErrorReporter,
|
||||
private val symboltable: SymbolTable, // TODO stick this in Program
|
||||
private val functioncallAsmGen: FunctionCallAsmGen,
|
||||
private val asmgen: AsmGen,
|
||||
private val allocator: VariableAllocator,
|
||||
@ -383,15 +382,19 @@ internal class ProgramAndVarsGen(
|
||||
clc""")
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
private fun zeropagevars2asm(block: Block) {
|
||||
//val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup fail") }
|
||||
//require(scope.type==StNodeType.BLOCK)
|
||||
val varnames = variables.blockVars.getOrDefault(block, emptySet()).map { it.scopedname }.toSet()
|
||||
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.BLOCK)
|
||||
val varnames = scope.children.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
private fun zeropagevars2asm(sub: Subroutine) {
|
||||
val varnames = variables.subroutineVars.getOrDefault(sub, emptySet()).map { it.scopedname }.toSet()
|
||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.SUBROUTINE)
|
||||
val varnames = scope.children.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
}
|
||||
|
||||
@ -404,32 +407,44 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
// TODO don't have this as a separate loop, use a partition over the variables in the block; ZP<->NonZP
|
||||
private fun nonZpVariables2asm(block: Block) {
|
||||
val variables = variables.blockVars[block]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
|
||||
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.BLOCK)
|
||||
val variables = scope.children
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
// TODO don't have this as a separate loop, use a partition over the variables in the subroutine; ZP<->NonZP
|
||||
private fun nonZpVariables2asm(sub: Subroutine) {
|
||||
val variables = variables.subroutineVars[sub]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
|
||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.SUBROUTINE)
|
||||
val variables = scope.children
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
private fun nonZpVariables2asm(variables: List<IVariablesAndConsts.StaticVariable>) {
|
||||
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
||||
asmgen.out("")
|
||||
asmgen.out("; non-zeropage variables")
|
||||
val (stringvars, othervars) = variables.partition { it.type==DataType.STR }
|
||||
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
|
||||
stringvars.forEach {
|
||||
val stringvalue = it.initialValue as StringLiteral
|
||||
outputStringvar(it.scopedname.last(), it.type, stringvalue.encoding, stringvalue.value)
|
||||
val stringvalue = it.initialvalue as StringLiteral
|
||||
outputStringvar(it.name, it.dt, stringvalue.encoding, stringvalue.value)
|
||||
}
|
||||
othervars.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun staticVariable2asm(variable: IVariablesAndConsts.StaticVariable) {
|
||||
val name = variable.scopedname.last()
|
||||
val value = variable.initialValue
|
||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||
val name = variable.name
|
||||
val value = variable.initialvalue
|
||||
val staticValue: Number =
|
||||
if(value!=null) {
|
||||
if(value is NumericLiteral) {
|
||||
@ -438,13 +453,13 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
value.number.toInt()
|
||||
} else {
|
||||
if(variable.type in NumericDatatypes)
|
||||
if(variable.dt in NumericDatatypes)
|
||||
throw AssemblyError("can only deal with constant numeric values for global vars")
|
||||
else 0
|
||||
}
|
||||
} else 0
|
||||
|
||||
when (variable.type) {
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}")
|
||||
DataType.BYTE -> asmgen.out("$name\t.char $staticValue")
|
||||
DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}")
|
||||
@ -460,7 +475,7 @@ internal class ProgramAndVarsGen(
|
||||
DataType.STR -> {
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
in ArrayDatatypes -> arrayVariable2asm(name, variable.type, value as? ArrayLiteral, variable.arraysize)
|
||||
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, value as? ArrayLiteral, variable.arraysize)
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
@ -524,30 +539,41 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
private fun memdefsAndConsts2asm(block: Block) {
|
||||
val mvs = variables.blockMemvars[block] ?: emptySet()
|
||||
val consts = variables.blockConsts[block] ?: emptySet()
|
||||
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.BLOCK)
|
||||
val mvs = scope.children
|
||||
.filter { it.value.type==StNodeType.MEMVAR }
|
||||
.map { it.value as StMemVar }
|
||||
val consts = scope.children
|
||||
.filter { it.value.type==StNodeType.CONSTANT }
|
||||
.map { it.value as StConstant }
|
||||
memdefsAndConsts2asm(mvs, consts)
|
||||
}
|
||||
|
||||
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
|
||||
private fun memdefsAndConsts2asm(sub: Subroutine) {
|
||||
val mvs = variables.subroutineMemvars[sub] ?: emptySet()
|
||||
val consts = variables.subroutineConsts[sub] ?: emptySet()
|
||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||
require(scope.type==StNodeType.SUBROUTINE)
|
||||
val mvs = scope.children
|
||||
.filter { it.value.type==StNodeType.MEMVAR }
|
||||
.map { it.value as StMemVar }
|
||||
val consts = scope.children
|
||||
.filter { it.value.type==StNodeType.CONSTANT }
|
||||
.map { it.value as StConstant }
|
||||
memdefsAndConsts2asm(mvs, consts)
|
||||
}
|
||||
|
||||
private fun memdefsAndConsts2asm(
|
||||
memvars: Set<IVariablesAndConsts.MemoryMappedVariable>,
|
||||
consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
|
||||
) {
|
||||
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
|
||||
memvars.forEach {
|
||||
asmgen.out(" ${it.scopedname.last()} = ${it.address.toHex()}")
|
||||
asmgen.out(" ${it.name} = ${it.address.toHex()}")
|
||||
}
|
||||
consts.forEach {
|
||||
if(it.type==DataType.FLOAT)
|
||||
asmgen.out(" ${it.scopedname.last()} = ${it.value}")
|
||||
if(it.dt==DataType.FLOAT)
|
||||
asmgen.out(" ${it.name} = ${it.value}")
|
||||
else
|
||||
asmgen.out(" ${it.scopedname.last()} = ${it.value.toHex()}")
|
||||
asmgen.out(" ${it.name} = ${it.value.toHex()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import prog8.compilerinterface.*
|
||||
|
||||
|
||||
internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
private val symboltable: SymbolTable,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter) {
|
||||
|
||||
@ -35,28 +36,59 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
if(options.zeropage== ZeropageType.DONTUSE)
|
||||
return
|
||||
|
||||
val allVariables = (
|
||||
val allVariablesOld = (
|
||||
vars.blockVars.asSequence().flatMap { it.value } +
|
||||
vars.subroutineVars.asSequence().flatMap { it.value }
|
||||
).toList()
|
||||
|
||||
val numberOfAllocatableVariables = allVariables.size
|
||||
val varsRequiringZp = allVariables.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsDontCare = allVariables.filter { it.zp == ZeropageWish.DONTCARE }
|
||||
val numberOfExplicitNonZpVariables = allVariables.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val allVariables = collectAllVariables(symboltable)
|
||||
require(allVariables.size == allVariablesOld.size)
|
||||
require(allVariables.map{it.scopedName}.toSet()==allVariablesOld.map{it.scopedname}.toSet())
|
||||
|
||||
val numberOfAllocatableVariables = allVariablesOld.size
|
||||
val varsRequiringZp = allVariablesOld.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariablesOld.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsDontCare = allVariablesOld.filter { it.zp == ZeropageWish.DONTCARE }
|
||||
val numberOfExplicitNonZpVariables = allVariablesOld.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
|
||||
|
||||
val numberOfAllocatableVariables2 = allVariables.size
|
||||
val varsRequiringZp2 = allVariables.filter { it.zpw == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp2 = allVariables.filter { it.zpw == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsDontCare2 = allVariables.filter { it.zpw == ZeropageWish.DONTCARE }
|
||||
val numberOfExplicitNonZpVariables2 = allVariables.count { it.zpw == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
require(varsDontCare2.size + varsRequiringZp2.size + varsPreferringZp2.size + numberOfExplicitNonZpVariables2 == numberOfAllocatableVariables2)
|
||||
require(varsDontCare2.size==varsDontCare.size)
|
||||
require(varsRequiringZp2.size==varsRequiringZp.size)
|
||||
require(varsPreferringZp2.size==varsPreferringZp.size)
|
||||
require(numberOfExplicitNonZpVariables2==numberOfExplicitNonZpVariables)
|
||||
require(numberOfAllocatableVariables2==numberOfAllocatableVariables)
|
||||
|
||||
val oldvarsByName = varsDontCare.associateBy { it.scopedname }
|
||||
val newvarsByName = varsDontCare2.associateBy { it.scopedName }
|
||||
require(oldvarsByName.keys==newvarsByName.keys)
|
||||
oldvarsByName.forEach { (name, oldvar) ->
|
||||
val newvar = newvarsByName.getValue(name)
|
||||
require(oldvar.scopedname==newvar.scopedName)
|
||||
require(oldvar.type==newvar.dt)
|
||||
require(oldvar.zp==newvar.zpw)
|
||||
require(oldvar.initialValue==newvar.initialvalue)
|
||||
require(oldvar.arraysize==newvar.arraysize)
|
||||
require(oldvar.position==newvar.position)
|
||||
require(numArrayElements(oldvar) == numArrayElements(newvar))
|
||||
}
|
||||
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
var numberOfNonIntegerVariables = 0
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
varsRequiringZp2.forEach { variable ->
|
||||
val numElements = numArrayElements(variable)
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedname,
|
||||
variable.type,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
numElements,
|
||||
variable.initialValue,
|
||||
variable.initialvalue,
|
||||
variable.position,
|
||||
errors
|
||||
)
|
||||
@ -71,13 +103,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
}
|
||||
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
varsPreferringZp2.forEach { variable ->
|
||||
val numElements = numArrayElements(variable)
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedname,
|
||||
variable.type,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
numElements,
|
||||
variable.initialValue,
|
||||
variable.initialvalue,
|
||||
variable.position,
|
||||
errors
|
||||
)
|
||||
@ -88,17 +120,17 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
// try to allocate any other interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
for (variable in varsDontCare) {
|
||||
if(variable.type in IntegerDatatypes) {
|
||||
for (variable in varsDontCare2.sortedWith(compareBy({it.scopedName.size}, {it.name}))) {
|
||||
if(variable.dt in IntegerDatatypes) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val numElements = numArrayElements(variable)
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedname,
|
||||
variable.type,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
numElements,
|
||||
variable.initialValue,
|
||||
variable.initialvalue,
|
||||
variable.position,
|
||||
errors
|
||||
)
|
||||
@ -115,6 +147,20 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||
}
|
||||
|
||||
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||
val vars = mutableListOf<StStaticVariable>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type==StNodeType.STATICVAR)
|
||||
vars.add(child.value as StStaticVariable)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(st)
|
||||
return vars
|
||||
}
|
||||
|
||||
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
|
||||
|
||||
private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
|
||||
@ -124,6 +170,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun numArrayElements(variable: StStaticVariable) =
|
||||
when(variable.dt) {
|
||||
DataType.STR -> (variable.initialvalue as StringLiteral).value.length
|
||||
in ArrayDatatypes -> variable.arraysize!!
|
||||
else -> null
|
||||
}
|
||||
|
||||
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
|
||||
var extra = subroutineExtras[sub]
|
||||
return if(extra==null) {
|
||||
|
Loading…
Reference in New Issue
Block a user