improved string constant interning; no longer output duplicate strings in the Ast

This commit is contained in:
Irmen de Jong 2021-02-16 23:43:38 +01:00
parent fa527f8624
commit ab544ee965
6 changed files with 39 additions and 22 deletions

View File

@ -178,7 +178,7 @@ c64 {
; ---- C64 ROM kernal routines ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine

View File

@ -172,7 +172,6 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
errors.handle()
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
val compilerOptions = determineCompilationOptions(programAst)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
@ -188,7 +187,7 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
}
private fun determineCompilationOptions(program: Program): CompilationOptions {
val mainModule = program.modules.first()
val mainModule = program.mainModule
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }

View File

@ -17,13 +17,10 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
// replace the literal string by a identifier reference to a new local vardecl
val vardecl = VarDecl.createAuto(string)
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
return listOf(
IAstModification.ReplaceNode(string, identifier, parent),
IAstModification.InsertFirst(vardecl, string.definingScope())
)
// replace the literal string by a identifier reference to the interned string
val scopedName = program.internString(string)
val identifier = IdentifierReference(scopedName, string.position)
return listOf(IAstModification.ReplaceNode(string, identifier, parent))
}
return noModifications
}

View File

@ -1,10 +1,7 @@
package prog8.ast
import prog8.ast.base.*
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.InferredTypes
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
@ -258,10 +255,26 @@ interface IBuiltinFunctions {
class Program(val name: String, val modules: MutableList<Module>, val builtinFunctions: IBuiltinFunctions): Node {
val namespace = GlobalNamespace(modules, builtinFunctions.names)
val mainModule: Module
get() = modules.first { it.name!=internedStringsModuleName }
val definedLoadAddress: Int
get() = modules.first().loadAddress
get() = mainModule.loadAddress
var actualLoadAddress: Int = 0
private val internedStrings = mutableMapOf<String, List<String>>()
val internedStringsModuleName = "prog8_interned_strings"
init {
// insert a container module for all interned strings later
if(modules.firstOrNull()?.name != internedStringsModuleName) {
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of(""))
modules.add(0, internedStringsModule)
val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY)
internedStringsModule.statements.add(block)
internedStringsModule.linkParents(this)
internedStringsModule.program = this
}
}
fun entrypoint(): Subroutine? {
val mainBlocks = allBlocks().filter { it.name=="main" }
@ -274,6 +287,21 @@ class Program(val name: String, val modules: MutableList<Module>, val builtinFun
}
}
fun internString(string: StringLiteralValue): List<String> {
val existing = internedStrings[string.value]
if(existing!=null)
return existing
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
(internedStringsBlock as Block).statements.add(decl)
decl.linkParents(internedStringsBlock)
val scopedName = listOf(internedStringsModuleName, decl.name)
internedStrings[string.value] = scopedName
return scopedName
}
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
override val position: Position = Position.DUMMY

View File

@ -176,12 +176,6 @@ open class VarDecl(val type: VarDeclType,
companion object {
private var autoHeapValueSequenceNumber = 0
fun createAuto(string: StringLiteralValue): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
return VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
}
fun createAuto(array: ArrayLiteralValue): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val arrayDt =

View File

@ -2,7 +2,6 @@
TODO
====
- auto_heap_value for strings: avoid creating multiple values for identical strings (or fix it at the end)
- add sound to the cx16 tehtriz
- refactor the asmgen into their own submodule?
- refactor the compiler optimizers into their own submodule?