improved var -> const replacement, now done in constfolding already (fixes some obscure problems later on)

Also fixed some directive parenting errors
This commit is contained in:
Irmen de Jong 2023-12-29 19:48:40 +01:00
parent 932bbd0381
commit d03ff1e4d0
9 changed files with 209 additions and 156 deletions

View File

@ -1,5 +1,7 @@
package prog8.optimizer
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
@ -9,10 +11,17 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.compiler.CallGraph
// Fix up the literal value's type to match that of the vardecl
// (also check range literal operands types before they get expanded into arrays for instance)
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
class VarConstantValueTypeAdjuster(
private val program: Program,
private val options: CompilationOptions,
private val errors: IErrorReporter
) : AstWalker() {
private val callGraph by lazy { CallGraph(program) }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
@ -38,6 +47,73 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
errors.err(x.message, x.position)
}
// replace variables by constants, if possible
if(options.optimize) {
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
return noModifications
if (decl.value != null && decl.value!!.constValue(program) == null)
return noModifications
val usages = callGraph.usages(decl)
val (writes, reads) = usages
.partition {
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|| it.parent is AssignTarget
|| it.parent is ForLoop
|| it.parent is AddressOf
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
}
val singleAssignment =
writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
if (singleAssignment == null) {
if (writes.isEmpty()) {
if(reads.isEmpty()) {
// variable is never used AT ALL so we just remove it altogether
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
val declValue = decl.value?.constValue(program)
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
)
}
}
} else {
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
if(reads.isEmpty()) {
// variable is never used AT ALL so we just remove it altogether, including the single assignment
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(
IAstModification.Remove(decl, parent as IStatementContainer),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
}
/*
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
if(writes.size==2) {
val firstAssignment = writes[0].parent as? Assignment
val secondAssignment = writes[1].parent as? Assignment
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
}
}
*/
}
return noModifications
}

View File

@ -7,13 +7,13 @@ import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
val valuetypefixer = VarConstantValueTypeAdjuster(this, options, errors)
valuetypefixer.visit(this)
if(errors.noErrors()) {
valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
val replacer = ConstantIdentifierReplacer(this, errors, options.compTarget)
replacer.visit(this)
if (errors.noErrors()) {
replacer.applyModifications()

View File

@ -1,11 +1,17 @@
package prog8.optimizer
import prog8.ast.*
import prog8.ast.expressions.*
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.internedStringsModuleName
import prog8.compiler.CallGraph
@ -118,36 +124,6 @@ class UnusedCodeRemover(private val program: Program,
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
else {
val (writes, reads) = usages
.partition{
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|| it.parent is AssignTarget
|| it.parent is ForLoop
|| it.parent is AddressOf
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
}
val singleAssignment = writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
if (singleAssignment!=null && reads.isNotEmpty()) {
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
}
/*
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
if(writes.size==2) {
val firstAssignment = writes[0].parent as? Assignment
val secondAssignment = writes[1].parent as? Assignment
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
}
}
*/
if(usages.size==1) {
val singleUse = usages[0].parent
if(singleUse is AssignTarget) {

View File

@ -358,7 +358,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report()
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)
errors.report()
program.constantFold(errors, compilerOptions.compTarget)
program.constantFold(errors, compilerOptions)
errors.report()
program.desugaring(errors)
errors.report()
@ -385,7 +385,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
val optsDone1 = program.simplifyExpressions(errors, compTarget)
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
val optsDone3 = program.inlineSubroutines(compilerOptions)
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
program.constantFold(errors, compilerOptions) // because simplified statements and expressions can result in more constants that can be folded away
if(!errors.noErrors()) {
errors.report()
break
@ -397,7 +397,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
remover2.visit(program)
remover2.applyModifications()
if(errors.noErrors())
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
program.constantFold(errors, compilerOptions) // because simplified statements and expressions can result in more constants that can be folded away
errors.report()
}

View File

@ -120,7 +120,9 @@ class ModuleImporter(private val program: Program,
importedModule.statements.remove(block)
if(blockHasMergeOption && !existingBlockHasMergeOption) {
existingBlock.statements.add(0, Directive("%option", listOf(DirectiveArg(null, "merge", null, block.position)), block.position))
val directive = Directive("%option", listOf(DirectiveArg(null, "merge", null, block.position)), block.position)
existingBlock.statements.add(0, directive)
directive.linkParents(existingBlock)
}
}
}

View File

@ -465,8 +465,8 @@ class TestOptimization: FunSpec({
%option force_output
sub start() {
uword aa
ubyte zz
uword @shared aa
ubyte @shared zz
@(aa) = zz + 32
}
}

View File

@ -386,182 +386,182 @@ main {
}
sub shiftruw0() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 0
}
sub shiftruw1() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 1
}
sub shiftruw2() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 2
}
sub shiftruw3() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 3
}
sub shiftruw4() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 4
}
sub shiftruw5() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 5
}
sub shiftruw6() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 6
}
sub shiftruw7() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q >> 7
}
sub shiftruw8() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 8)
}
sub shiftruw9() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 9)
}
sub shiftruw10() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 10)
}
sub shiftruw11() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 11)
}
sub shiftruw12() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 12)
}
sub shiftruw13() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 13)
}
sub shiftruw14() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 14)
}
sub shiftruw15() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 15)
}
sub shiftruw16() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 16)
}
sub shiftruw17() -> uword {
uword q = $a49f
uword @shared q = $a49f
return (q >> 17)
}
sub shiftrsw0() -> word {
word q = -12345
word @shared q = -12345
return q >> 0
}
sub shiftrsw1() -> word {
word q = -12345
word @shared q = -12345
return q >> 1
}
sub shiftrsw2() -> word {
word q = -12345
word @shared q = -12345
return q >> 2
}
sub shiftrsw3() -> word {
word q = -12345
word @shared q = -12345
return q >> 3
}
sub shiftrsw4() -> word {
word q = -12345
word @shared q = -12345
return q >> 4
}
sub shiftrsw5() -> word {
word q = -12345
word @shared q = -12345
return q >> 5
}
sub shiftrsw6() -> word {
word q = -12345
word @shared q = -12345
return q >> 6
}
sub shiftrsw7() -> word {
word q = -12345
word @shared q = -12345
return q >> 7
}
sub shiftrsw8() -> word {
word q = -12345
word @shared q = -12345
return (q >> 8)
}
sub shiftrsw9() -> word {
word q = -12345
word @shared q = -12345
return (q >> 9)
}
sub shiftrsw10() -> word {
word q = -12345
word @shared q = -12345
return (q >> 10)
}
sub shiftrsw11() -> word {
word q = -12345
word @shared q = -12345
return (q >> 11)
}
sub shiftrsw12() -> word {
word q = -12345
word @shared q = -12345
return (q >> 12)
}
sub shiftrsw13() -> word {
word q = -12345
word @shared q = -12345
return (q >> 13)
}
sub shiftrsw14() -> word {
word q = -12345
word @shared q = -12345
return (q >> 14)
}
sub shiftrsw15() -> word {
word q = -12345
word @shared q = -12345
return (q >> 15)
}
sub shiftrsw16() -> word {
word q = -12345
word @shared q = -12345
return (q >> 16)
}
sub shiftrsw17() -> word {
word q = -12345
word @shared q = -12345
return (q >> 17)
}
@ -569,352 +569,352 @@ main {
sub shiftluw0() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 0
}
sub shiftluw1() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 1
}
sub shiftluw2() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 2
}
sub shiftluw3() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 3
}
sub shiftluw4() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 4
}
sub shiftluw5() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 5
}
sub shiftluw6() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 6
}
sub shiftluw7() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 7
}
sub shiftluw8() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 8
}
sub shiftluw9() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 9
}
sub shiftluw10() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 10
}
sub shiftluw11() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 11
}
sub shiftluw12() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 12
}
sub shiftluw13() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 13
}
sub shiftluw14() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 14
}
sub shiftluw15() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 15
}
sub shiftluw16() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 16
}
sub shiftluw17() -> uword {
uword q = $a49f
uword @shared q = $a49f
return q << 17
}
sub shiftlsw0() -> word {
word q = -12345
word @shared q = -12345
return q << 0
}
sub shiftlsw1() -> word {
word q = -12345
word @shared q = -12345
return q << 1
}
sub shiftlsw2() -> word {
word q = -12345
word @shared q = -12345
return q << 2
}
sub shiftlsw3() -> word {
word q = -12345
word @shared q = -12345
return q << 3
}
sub shiftlsw4() -> word {
word q = -12345
word @shared q = -12345
return q << 4
}
sub shiftlsw5() -> word {
word q = -12345
word @shared q = -12345
return q << 5
}
sub shiftlsw6() -> word {
word q = -12345
word @shared q = -12345
return q << 6
}
sub shiftlsw7() -> word {
word q = -12345
word @shared q = -12345
return q << 7
}
sub shiftlsw8() -> word {
word q = -12345
word @shared q = -12345
return q << 8
}
sub shiftlsw9() -> word {
word q = -12345
word @shared q = -12345
return q << 9
}
sub shiftlsw10() -> word {
word q = -12345
word @shared q = -12345
return q << 10
}
sub shiftlsw11() -> word {
word q = -12345
word @shared q = -12345
return q << 11
}
sub shiftlsw12() -> word {
word q = -12345
word @shared q = -12345
return q << 12
}
sub shiftlsw13() -> word {
word q = -12345
word @shared q = -12345
return q << 13
}
sub shiftlsw14() -> word {
word q = -12345
word @shared q = -12345
return q << 14
}
sub shiftlsw15() -> word {
word q = -12345
word @shared q = -12345
return q << 15
}
sub shiftlsw16() -> word {
word q = -12345
word @shared q = -12345
return q << 16
}
sub shiftlsw17() -> word {
word q = -12345
word @shared q = -12345
return q << 17
}
sub shiftlb0() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 0
}
sub shiftlb1() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 1
}
sub shiftlb2() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 2
}
sub shiftlb3() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 3
}
sub shiftlb4() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 4
}
sub shiftlb5() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 5
}
sub shiftlb6() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 6
}
sub shiftlb7() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 7
}
sub shiftlb8() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 8
}
sub shiftlb9() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy << 9
}
sub shiftrb0() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 0
}
sub shiftrb1() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 1
}
sub shiftrb2() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 2
}
sub shiftrb3() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 3
}
sub shiftrb4() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 4
}
sub shiftrb5() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 5
}
sub shiftrb6() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 6
}
sub shiftrb7() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 7
}
sub shiftrb8() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 8
}
sub shiftrb9() -> ubyte {
ubyte yy=$ed
ubyte @shared yy=$ed
return yy >> 9
}
sub shiftlsb0() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 0
}
sub shiftlsb1() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 1
}
sub shiftlsb2() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 2
}
sub shiftlsb3() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 3
}
sub shiftlsb4() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 4
}
sub shiftlsb5() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 5
}
sub shiftlsb6() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 6
}
sub shiftlsb7() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 7
}
sub shiftlsb8() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 8
}
sub shiftlsb9() -> byte {
byte yy=-123
byte @shared yy=-123
return yy << 9
}
sub shiftrsb0() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 0
}
sub shiftrsb1() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 1
}
sub shiftrsb2() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 2
}
sub shiftrsb3() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 3
}
sub shiftrsb4() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 4
}
sub shiftrsb5() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 5
}
sub shiftrsb6() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 6
}
sub shiftrsb7() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 7
}
sub shiftrsb8() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 8
}
sub shiftrsb9() -> byte {
byte yy=-123
byte @shared yy=-123
return yy >> 9
}
}

View File

@ -26,6 +26,7 @@ class Program(val name: String,
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
val directive = Directive("%option", listOf(DirectiveArg(null,"no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY)
block.statements.add(directive)
directive.linkParents(block)
internedStringsModule.statements.add(block)
_modules.add(0, internedStringsModule)

View File

@ -2,8 +2,6 @@
TODO
====
- fix bitshift.p8
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
...