got rid of old IVariablesAndConsts object

This commit is contained in:
Irmen de Jong 2022-03-05 14:26:33 +01:00
parent cf362c4a61
commit 067283834a
11 changed files with 30 additions and 294 deletions

View File

@ -24,14 +24,13 @@ internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen(internal val program: Program,
internal val errors: IErrorReporter,
internal val symbolTable: SymbolTable,
internal val variables: IVariablesAndConsts,
internal val options: CompilationOptions): IAssemblyGenerator {
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
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, symbolTable, options, errors)
private val allocator = VariableAllocator(symbolTable, options, errors)
private val assemblyLines = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)

View File

@ -25,7 +25,7 @@ internal class ProgramAndVarsGen(
val program: Program,
val options: CompilationOptions,
val errors: IErrorReporter,
private val symboltable: SymbolTable, // TODO stick this in Program
private val symboltable: SymbolTable,
private val functioncallAsmGen: FunctionCallAsmGen,
private val asmgen: AsmGen,
private val allocator: VariableAllocator,

View File

@ -11,8 +11,7 @@ import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.*
internal class VariableAllocator(private val vars: IVariablesAndConsts,
private val symboltable: SymbolTable,
internal class VariableAllocator(private val symboltable: SymbolTable,
private val options: CompilationOptions,
private val errors: IErrorReporter) {
@ -36,53 +35,19 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
if(options.zeropage== ZeropageType.DONTUSE)
return
val allVariablesOld = (
vars.blockVars.asSequence().flatMap { it.value } +
vars.subroutineVars.asSequence().flatMap { it.value }
).toList()
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 }
val numberOfAllocatableVariables = allVariables.size
val varsRequiringZp = allVariables.filter { it.zpw == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpw == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpw == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zpw == 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
varsRequiringZp2.forEach { variable ->
varsRequiringZp.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedName,
@ -103,7 +68,7 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
}
if(errors.noErrors()) {
varsPreferringZp2.forEach { variable ->
varsPreferringZp.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedName,
@ -120,7 +85,7 @@ 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 varsDontCare2.sortedWith(compareBy({it.scopedName.size}, {it.name}))) {
for (variable in varsDontCare.sortedBy { it.scopedName.size }) {
if(variable.dt in IntegerDatatypes) {
if(zeropage.free.isEmpty()) {
break
@ -163,13 +128,6 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
when(variable.type) {
DataType.STR -> (variable.initialValue as StringLiteral).value.length
in ArrayDatatypes -> variable.arraysize!!
else -> null
}
private fun numArrayElements(variable: StStaticVariable) =
when(variable.dt) {
DataType.STR -> (variable.initialvalue as StringLiteral).value.length

View File

@ -6,7 +6,6 @@ import prog8.compilerinterface.*
class AsmGen(internal val program: Program,
internal val errors: IErrorReporter,
internal val symbolTable: SymbolTable,
internal val variables: IVariablesAndConsts,
internal val options: CompilationOptions): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {

View File

@ -363,7 +363,6 @@ private fun writeAssembly(program: Program,
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report()
val variables = VariableExtractor().extractFrom(program)
val symbolTable = SymbolTableMaker().makeFrom(program)
// TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast,
@ -375,7 +374,7 @@ private fun writeAssembly(program: Program,
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
val assembly = asmGeneratorFor(program, errors, symbolTable, variables, compilerOptions).compileToAssembly()
val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly()
errors.report()
return if(assembly!=null && errors.noErrors()) {
@ -418,15 +417,14 @@ fun printProgram(program: Program) {
internal fun asmGeneratorFor(program: Program,
errors: IErrorReporter,
symbolTable: SymbolTable,
variables: IVariablesAndConsts,
options: CompilationOptions): IAssemblyGenerator
{
if(options.experimentalCodegen) {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
return prog8.codegen.experimental6502.AsmGen(program, errors, symbolTable, variables, options)
return prog8.codegen.experimental6502.AsmGen(program, errors, symbolTable, options)
} else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
return prog8.codegen.cpu6502.AsmGen(program, errors, symbolTable, variables, options)
return prog8.codegen.cpu6502.AsmGen(program, errors, symbolTable, options)
}
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")

View File

@ -1,183 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.IVariablesAndConsts
internal class VariableExtractor: IAstVisitor {
private val allBlockVars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockConsts = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockMemoryvars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allSubroutineVars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
fun extractFrom(program: Program): IVariablesAndConsts {
this.visit(program)
return VariablesAndConsts(
allBlockVars, allBlockConsts, allBlockMemoryvars,
allSubroutineVars, allSubroutineConsts, allSubroutineMemoryvars)
}
override fun visit(decl: VarDecl) {
val scope=decl.definingScope
when (decl.type) {
VarDeclType.VAR -> {
when (scope) {
is Block -> {
val decls = allBlockVars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockVars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineVars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineVars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
VarDeclType.CONST -> {
when(scope) {
is Block -> {
val decls = allBlockConsts[scope] ?: mutableSetOf()
decls.add(decl)
allBlockConsts[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineConsts[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineConsts[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
VarDeclType.MEMORY -> {
when(scope) {
is Block -> {
val decls = allBlockMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockMemoryvars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineMemoryvars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
else -> {
throw FatalAstException("invalid var type")
}
}
super.visit(decl)
}
}
internal class VariablesAndConsts (
astBlockVars: Map<Block, Set<VarDecl>>,
astBlockConsts: Map<Block, Set<VarDecl>>,
astBlockMemvars: Map<Block, Set<VarDecl>>,
astSubroutineVars: Map<Subroutine, Set<VarDecl>>,
astSubroutineConsts: Map<Subroutine, Set<VarDecl>>,
astSubroutineMemvars: Map<Subroutine, Set<VarDecl>>
) : IVariablesAndConsts
{
override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticVariable>>
override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>>
override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticVariable>>
override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>>
private val bv = astBlockVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }
private val bc = astBlockConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
private val bmv = astBlockMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
private val sv = astSubroutineVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }
private val sc = astSubroutineConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
private val smv = astSubroutineMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
init {
astBlockVars.forEach { (block, decls) ->
val vars = bv.getValue(block)
vars.addAll(decls.map { toStatic(it) })
}
astBlockConsts.forEach { (block, decls) ->
bc.getValue(block).addAll(
decls.map {
IVariablesAndConsts.ConstantNumberSymbol(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number,
it.position
)
})
}
astBlockMemvars.forEach { (block, decls) ->
val vars = bmv.getValue(block)
for(decl in decls) {
// make sure the 'stubs' for the scratch variables in zeropage are not included as normal variables
if(!decl.name.startsWith("P8ZP_SCRATCH_")) {
vars.add(
IVariablesAndConsts.MemoryMappedVariable(
decl.datatype,
decl.scopedName,
(decl.value as NumericLiteral).number.toUInt(),
decl.position
)
)
}
}
}
astSubroutineVars.forEach { (sub, decls) ->
val vars = sv.getValue(sub)
vars.addAll(decls.map { toStatic(it) })
}
astSubroutineConsts.forEach { (sub, decls) ->
sc.getValue(sub).addAll(
decls.map {
IVariablesAndConsts.ConstantNumberSymbol(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number,
it.position
)
})
}
astSubroutineMemvars.forEach { (sub, decls) ->
smv.getValue(sub).addAll(
decls.map {
IVariablesAndConsts.MemoryMappedVariable(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number.toUInt(),
it.position
)
})
}
blockVars = bv
blockConsts = bc
blockMemvars = bmv
subroutineVars = sv
subroutineConsts = sc
subroutineMemvars = smv
}
private fun toStatic(decl: VarDecl) =
IVariablesAndConsts.StaticVariable(decl.datatype, decl.scopedName, decl.value, decl.arraysize?.constIndex(), decl.zeropage, decl.position)
}

View File

@ -16,7 +16,6 @@ import prog8.ast.base.Position
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.codegen.target.C64Target
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compiler.astprocessing.processAstBeforeAsmGeneration
import prog8.compiler.printProgram
import prog8.compilerinterface.CompilationOptions

View File

@ -16,7 +16,6 @@ import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.target.C64Target
import prog8.codegen.target.c64.C64Zeropage
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compilerinterface.*
import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions
@ -80,8 +79,7 @@ class TestAsmGenSymbols: StringSpec({
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target())
options.compTarget.machine.zeropage = C64Zeropage(options)
val st = SymbolTableMaker().makeFrom(program)
val allocation = VariableExtractor().extractFrom(program)
return AsmGen(program, errors, st, allocation, options)
return AsmGen(program, errors, st, options)
}
"symbol and variable names from strings" {

View File

@ -9,7 +9,6 @@ import prog8.codegen.target.c64.C64Zeropage
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compiler.compileProgram
import prog8.compilerinterface.*
import java.nio.file.Path
@ -89,8 +88,7 @@ internal fun generateAssembly(
): IAssemblyProgram? {
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir)
coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
val variables = VariableExtractor().extractFrom(program)
val st = SymbolTableMaker().makeFrom(program)
val asmgen = AsmGen(program, ErrorReporterForTests(), st, variables, coptions)
val asmgen = AsmGen(program, ErrorReporterForTests(), st, coptions)
return asmgen.compileToAssembly()
}

View File

@ -1,35 +0,0 @@
package prog8.compilerinterface
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.ast.statements.ZeropageWish
/**
* A more convenient way to pass variable (and constant values) definitions to the code generator,
* so that it doesn't have to scavenge and manipulate the VerDecl nodes in the AST for this information.
*
* note: the string variables are in here as well, they're in blockVars for the block named 'prog8_interned_strings'.
*/
// TODO remove this, and replace with SymbolTable
interface IVariablesAndConsts {
data class ConstantNumberSymbol(val type: DataType, val scopedname: List<String>, val value: Double, val position: Position)
data class MemoryMappedVariable(val type: DataType, val scopedname: List<String>, val address: UInt, val position: Position)
data class StaticVariable(val type: DataType,
val scopedname: List<String>,
val initialValue: Expression?,
val arraysize: Int?,
val zp: ZeropageWish,
val position: Position)
val blockVars: Map<Block, Set<StaticVariable>>
val blockConsts: Map<Block, Set<ConstantNumberSymbol>>
val blockMemvars: Map<Block, Set<MemoryMappedVariable>>
val subroutineVars: Map<Subroutine, Set<StaticVariable>>
val subroutineConsts: Map<Subroutine, Set<ConstantNumberSymbol>>
val subroutineMemvars: Map<Subroutine, Set<MemoryMappedVariable>>
}

View File

@ -8,6 +8,19 @@ import prog8.ast.statements.ZeropageWish
import prog8.ast.toHex
/**
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types) and labels).
*/
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
fun print() = printIndented(0)
override fun printProperties() { }
val origAstLinks = mutableMapOf<Node, StNode>() // links of the original Ast nodes to the symbol table node.
}
enum class StNodeType {
GLOBAL,
// MODULE, // not used with current scoping rules
@ -81,7 +94,7 @@ open class StNode(val name: String,
}
}
fun printIndented(indent: Int) {
protected fun printIndented(indent: Int) {
print(" ".repeat(indent))
when(type) {
StNodeType.GLOBAL -> print("SYMBOL-TABLE:")
@ -108,14 +121,6 @@ open class StNode(val name: String,
}
}
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
fun print() = printIndented(0)
override fun printProperties() { }
val origAstLinks = mutableMapOf<Node, StNode>() // link the original Ast nodes into the table. TODO is this really needed?
}
class StStaticVariable(name: String,
val dt: DataType,
val initialvalue: Expression?,