From ca61248861b1910f2258c009455242de7b714804 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 16 May 2021 15:00:40 +0200 Subject: [PATCH] printing 2-letter strings is now only optimized into direct CHROUT if it's a const string literal --- compiler/src/prog8/optimizer/StatementOptimizer.kt | 6 ++---- compiler/src/prog8/optimizer/UnusedCodeRemover.kt | 7 ++----- compilerAst/src/prog8/ast/AstToplevel.kt | 3 ++- .../src/prog8/ast/expressions/AstExpressions.kt | 9 +++++++++ docs/source/todo.rst | 3 ++- examples/test.p8 | 14 ++++++++------ 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index cfa5fbd36..2a8372d83 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -48,6 +48,7 @@ internal class StatementOptimizer(private val program: Program, } // printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters + // only do this optimization if the arg is a known-constant string literal instead of a user defined variable. if(functionCallStatement.target.nameInSource==listOf("txt", "print")) { val arg = functionCallStatement.args.single() val stringVar: IdentifierReference? = if(arg is AddressOf) { @@ -55,10 +56,7 @@ internal class StatementOptimizer(private val program: Program, } else { arg as? IdentifierReference } - if(stringVar!=null) { - - // TODO: only do this optimization if the arg is a known-constant string literal instead of a user defined variable. We can't see the difference here yet. - + if(stringVar!=null && stringVar.wasStringLiteral(program)) { val string = stringVar.targetVarDecl(program)?.value as? StringLiteralValue if(string!=null) { val pos = functionCallStatement.position diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index bee3d85bc..94700f61d 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -1,9 +1,6 @@ package prog8.optimizer -import prog8.ast.INameScope -import prog8.ast.Module -import prog8.ast.Node -import prog8.ast.Program +import prog8.ast.* import prog8.ast.base.VarDeclType import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.FunctionCall @@ -65,7 +62,7 @@ internal class UnusedCodeRemover(private val program: Program, override fun after(block: Block, parent: Node): Iterable { if("force_output" !in block.options()) { if (block.containsNoCodeNorVars()) { - if(block.name != program.internedStringsModuleName) + if(block.name != internedStringsModuleName) errors.warn("removing unused block '${block.name}'", block.position) return listOf(IAstModification.Remove(block, parent as INameScope)) } diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 40a5c940d..64db46607 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -8,6 +8,8 @@ import prog8.ast.walk.IAstVisitor import java.nio.file.Path import kotlin.math.abs +const val internedStringsModuleName = "prog8_interned_strings" + interface IStringEncoding { fun encodeString(str: String, altEncoding: Boolean): List fun decodeString(bytes: List, altEncoding: Boolean): String @@ -257,7 +259,6 @@ class Program(val name: String, var actualLoadAddress: Int = 0 private val internedStringsUnique = mutableMapOf, List>() - val internedStringsModuleName = "prog8_interned_strings" init { // insert a container module for all interned strings later diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 4e6558a01..2116fe5f6 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -788,6 +788,15 @@ data class IdentifierReference(val nameInSource: List, override val posi else -> InferredTypes.InferredType.unknown() } } + + fun wasStringLiteral(program: Program): Boolean { + val decl = targetVarDecl(program) + if(decl == null || !decl.autogeneratedDontRemove) + return false + + val scope=decl.definingModule() + return scope.name==internedStringsModuleName + } } class FunctionCall(override var target: IdentifierReference, diff --git a/docs/source/todo.rst b/docs/source/todo.rst index b613d14f0..85971404d 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,8 +4,9 @@ TODO - possible idea: option to mark vardecls 'shared' to indicate they should not be optimized away because they're shared with assembly code? However: who even needs variables declared in prog8 code that are only used by assembly??? + - github issue about strings and their immutability: - StatementOptimizer line 60: only optimize when it's a known constant literal + Can we make deduplication the default again? (only string literals are considered...) remove cli option for it again? IMPROVE DOCUMENTATION ABOUT STRINGS AND DEDUP and (NON)IMMUTABILITY. - test all examples (including imgviewer, assembler and petaxian) before release of the new version diff --git a/examples/test.p8 b/examples/test.p8 index 179697814..f8de245d8 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,24 +1,26 @@ %import textio ; txt.* %zeropage basicsafe main { + sub start() { - str string1 = "stringvalue" - str string2 = "stringvalue" - str string3 = "stringvalue" + str string1 = "stringvalue\n" + str string2 = "stringvalue\n" + str string3 = "stringvalue\n" + str string4 = "a" + str string5 = "bb" txt.print("a") txt.print("a") + txt.print(string4) txt.print("bb") txt.print("bb") + txt.print(string5) txt.print("\n") txt.print("\n\n") txt.print(string1) - txt.nl() txt.print(string2) - txt.nl() txt.print(string3) - txt.nl() txt.print("hello\n") txt.print("hello\n") txt.print("hello\n")